diff --git a/DEPS b/DEPS index 82da2461..a5309936 100644 --- a/DEPS +++ b/DEPS
@@ -201,7 +201,7 @@ # By default, do not check out versions of toolschains and sdks that are # specifically only needed by Lacros. 'checkout_lacros_sdk': False, - 'lacros_sdk_version': '14523.0.0', + 'lacros_sdk_version': '14556.0.0', # Generate location tag metadata to include in tests result data uploaded # to ResultDB. This isn't needed on some configs and the tool that generates @@ -253,7 +253,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'c444394841139218d26be2b12e5953246a26fda7', + 'skia_revision': '9de6144f7d90371537a7abda2062c6c960072fb6', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -261,7 +261,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': 'b465a7366cb82a1be0bca5c4508eef7157363843', + 'angle_revision': '3a529ce245d0ca45fa8e85491a26ec7825aa7c92', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -269,7 +269,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': 'c2710889e7eda2f88f6e8b1323bd3b971bbb885a', + 'pdfium_revision': '9718f72db3f35da3f073111829485236c6ff7267', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. @@ -300,7 +300,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling freetype # and whatever else without interference from each other. - 'freetype_revision': '1e2eb65048f75c64b68708efed6ce904c31f3b2f', + 'freetype_revision': '53dfdcd8198d2b3201a23c4bad9190519ba918db', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling freetype # and whatever else without interference from each other. @@ -320,7 +320,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '4cc26bd5a059cf275feff2c934f744617dd26e8c', + 'catapult_revision': '389f33bb40a3345b73a68613178c789476ceaecf', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -328,7 +328,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': 'b2a3923617328a970bf07eca721bb8f6ad9c4574', + 'devtools_frontend_revision': 'dee4cc38be8b1ab523ac594c7abf0a08016f9f3f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -368,7 +368,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': '80b94d13a707ccf941b4e36bb18b120285db33c8', + 'dawn_revision': 'b657e0df98b215aac28b3457210bdc5149663bba', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -396,7 +396,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling nearby # and whatever else without interference from each other. - 'nearby_revision': '31b2aae6c5442c55d819e7a966b8dd996a0edcfa', + 'nearby_revision': '517d77f5aa4fea8f4437125830cfc55f84705e3d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling securemessage # and whatever else without interference from each other. @@ -715,7 +715,7 @@ }, 'src/ios/third_party/earl_grey2/src': { - 'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '7d387221a74e94a7f26224c3e52dc5a1e1401e4c', + 'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '16b7d15f81c0a724b2b2054451cbeae93fbccedb', 'condition': 'checkout_ios', }, @@ -816,7 +816,7 @@ 'packages': [ { 'package': 'chromium/rts/model/linux-amd64', - 'version': 'RLW71Y2eRTjMSjaHAyDffpdD7OwJhJLbjOf0-BLLmwsC', + 'version': 'l1xDoCBm1rDEFIlePkzB2hTG4r1YvYoxdNBU3SGjTDoC', }, ], 'dep_type': 'cipd', @@ -827,7 +827,7 @@ 'packages': [ { 'package': 'chromium/rts/model/mac-amd64', - 'version': 'kXqkgNAZI3mjN_7BL0owH1iDd9WbjGNLQQmF5UiaINMC', + 'version': 'ScMUxoCQFi3vFXDAlBj3VezWCnqk9hxpFW8GznMw454C', }, ], 'dep_type': 'cipd', @@ -838,7 +838,7 @@ 'packages': [ { 'package': 'chromium/rts/model/windows-amd64', - 'version': '2cIzm3j9sDgwkg5HQzYtZ923qplJv2tQ1yQf7-edmG4C', + 'version': 'iZunll1kgfbUFl7u6t5VnY4-MHcjb72ZS9UVDhTAr8cC', }, ], 'dep_type': 'cipd', @@ -932,7 +932,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_build_tools/aapt2', - 'version': 'yQIf5Ev_-q9u6Pr_a0APd1dHcImJSmotVADw4Pj151QC', + 'version': 'wicn5Ce1ay6ivbZ1GNFF0gRSS3NYv_7hJTPtVga3O-QC', }, ], 'condition': 'checkout_android', @@ -998,7 +998,7 @@ Var('chromium_git') + '/angle/angle.git' + '@' + Var('angle_revision'), 'src/third_party/dav1d/libdav1d': - Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + 'b1a5189c9d37c837099ce50852b6ce9597b89b0c', + Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + '56e7ffc0dbe44970a4d55ec2241824c67add9dd5', 'src/third_party/dawn': Var('dawn_git') + '/dawn.git' + '@' + Var('dawn_revision'), @@ -1107,7 +1107,7 @@ # Tools used when building Chrome for Chrome OS. This affects both the Simple # Chrome workflow, as well as the chromeos-chrome ebuild. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '725b443750be9664a1c5a12ce19d5fc42dda4cd2', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3d2387a672b437b2d32d83120080a5e593b0e5af', 'condition': 'checkout_chromeos', }, @@ -1127,7 +1127,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '950a6b4225ed3280aa0fbca2de51bfbaecd7695a', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '76979dae56c08d24b092347a1bdd2abe474555a3', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -1267,7 +1267,7 @@ Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + '41cdffd71c9948f63c7ad36e1fb0ff519aa7a37e', 'src/third_party/icu': - Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '8a5b728e4f43b0eabdb9ea450f956d67cfb22719', + Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'ea8c08d8783fceda86c19618694881149e23f305', 'src/third_party/icu4j': { 'packages': [ @@ -1510,7 +1510,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'a2dabf80a38b16846d9a3209d61f452a1bfc8f15', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '9b8eaa21f16d10cffefdf758a8b900e014e4788d', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1588,7 +1588,7 @@ 'packages': [ { 'package': 'fuchsia/third_party/android/aemu/release/linux-amd64', - 'version': 'jbMpjSzTRngktfwC6FszY0ASXYXkR77i2_dDrvQsa9oC' + 'version': 'npFnPpRDsfR65dWNWK7Bd-sNnXyASiLIUQB_fWalsiYC' }, ], 'condition': 'host_os == "linux" and checkout_fuchsia', @@ -1692,7 +1692,7 @@ 'src/third_party/usrsctp/usrsctplib': Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '62d7d0c928c9a040dce96aa2f16c00e7e67d59cb', - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@923b4de70fb5b6e9268b57837b2d517b2ba556c3', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@5fe1b21d6ba47a8dee59068d6927a4c157ec13a3', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907', @@ -1731,7 +1731,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'a630866d89f74aa95cf3aecd78987637ee195b68', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '026cfa3c8bde814ebbe5e94d0803dfd173c9bb02', + Var('webrtc_git') + '/src.git' + '@' + '66ddd5ab6d853c0e8ece811bb2d8b642b8602fc7', 'src/third_party/libgifcodec': Var('skia_git') + '/libgifcodec' + '@'+ Var('libgifcodec_revision'), @@ -1758,7 +1758,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/linux-amd64', - 'version': 'wjKDZ5vJELJ_j3O037nIWhBEMF0cY4Y1g4tLc47hPJoC', + 'version': 'iqtz2prn9CUv6A8KCcxJzadmPEDLY1FPP-b2YqIFQ1cC', }, ], 'dep_type': 'cipd', @@ -1768,7 +1768,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/windows-amd64', - 'version': 'Vg04A_bOadtB2ljbA9DGKe69_Uc6pmX5mk_ABoO2R3EC', + 'version': 'EbVQXa1u0hbZ8pxb0Il6Rbc1ErHpIN_-kMVOzBXMQyoC', }, ], 'dep_type': 'cipd', @@ -1779,7 +1779,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/mac-amd64', - 'version': 'jw5QK1qcTGSBr-yjH0d-9F_MNeq6e5_5aWLq_oGWy0QC', + 'version': 'Y4l14LBqCsT9EhffPIOtso9VSpwqQE9WccjZdDBZmLoC', }, ], 'dep_type': 'cipd', @@ -1790,7 +1790,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/mac-arm64', - 'version': 'o4BSMT1hKtY4T4VBfANeSm-NuhxoxPYUp3lF0EpoUvMC', + 'version': 'INEqc8JI_mtww_X0ShOlDkF3S8OG4tjF4Nkei0K7ci8C', }, ], 'dep_type': 'cipd', @@ -1801,7 +1801,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@84b3be6df03d468145962a5239d27da83ccd3e14', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@72adf65a661b1c04449d6ca665c9ced97267404e', 'condition': 'checkout_src_internal', }, @@ -1853,7 +1853,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/projector_app/app', - 'version': 'cvaInkU-R_Gp-n2T4FBD02VwkF3VjZEuBh5sAFLoCf8C', + 'version': 'C8gZ9FIW7W4ZX_T-0MFkA9OSY7DcZzSWVk4IS2fppZwC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -3880,15 +3880,29 @@ ], }, { - 'name': 'clang_format_mac', + 'name': 'clang_format_mac_x64', 'pattern': '.', - 'condition': 'host_os == "mac"', + 'condition': 'host_os == "mac" and host_cpu == "x64"', 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', '--no_auth', '--bucket', 'chromium-clang-format', - '-s', 'src/buildtools/mac/clang-format.sha1', + '-s', 'src/buildtools/mac/clang-format.x64.sha1', + '-o', 'src/buildtools/mac/clang-format', + ], + }, + { + 'name': 'clang_format_mac_arm64', + 'pattern': '.', + 'condition': 'host_os == "mac" and host_cpu == "arm64"', + 'action': [ 'python3', + 'src/third_party/depot_tools/download_from_google_storage.py', + '--no_resume', + '--no_auth', + '--bucket', 'chromium-clang-format', + '-s', 'src/buildtools/mac/clang-format.arm64.sha1', + '-o', 'src/buildtools/mac/clang-format', ], }, {
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 99723fa..9140e2b 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -1015,19 +1015,6 @@ ), ) -# Format: Sequence of tuples containing: -# * String pattern or, if starting with a slash, a regular expression. -# * Sequence of strings to show when the pattern matches. -_DEPRECATED_MOJO_TYPES : Sequence[BanRule] = ( - BanRule( - r'/\bmojo::AssociatedInterfacePtrInfo\b', - ( - 'mojo::AssociatedInterfacePtrInfo<Interface> is deprecated.', - 'Use mojo::PendingAssociatedRemote<Interface> instead.', - ), - ), -) - _IPC_ENUM_TRAITS_DEPRECATED = ( 'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n' 'See http://www.chromium.org/Home/chromium-security/education/' @@ -1733,48 +1720,6 @@ return result -def CheckNoDeprecatedMojoTypes(input_api, output_api): - """Make sure that old Mojo types are not used.""" - warnings = [] - errors = [] - - # For any path that is not an "ok" or an "error" path, a warning will be - # raised if deprecated mojo types are found. - ok_paths = ['components/arc'] - error_paths = ['third_party/blink', 'content'] - - file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h')) - for f in input_api.AffectedFiles(file_filter=file_filter): - # Don't check //components/arc, not yet migrated (see crrev.com/c/1868870). - if any(map(lambda path: f.LocalPath().startswith(path), ok_paths)): - continue - - for line_num, line in f.ChangedContents(): - for ban_rule in _DEPRECATED_MOJO_TYPES: - problems = _GetMessageForMatchingType(input_api, f, line_num, - line, ban_rule) - - if problems: - # Raise errors inside |error_paths| and warnings everywhere else. - if any( - map(lambda path: f.LocalPath().startswith(path), - error_paths)): - errors.extend(problems) - else: - warnings.extend(problems) - - result = [] - if (warnings): - result.append( - output_api.PresubmitPromptWarning( - 'Banned Mojo types were used.\n' + '\n'.join(warnings))) - if (errors): - result.append( - output_api.PresubmitError('Banned Mojo types were used.\n' + - '\n'.join(errors))) - return result - - def CheckNoPragmaOnce(input_api, output_api): """Make sure that banned functions are not used.""" files = []
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py index 76d425a..beb5f0a4 100755 --- a/PRESUBMIT_test.py +++ b/PRESUBMIT_test.py
@@ -2613,74 +2613,6 @@ self.assertTrue('bad.mojom' in results[0].message) self.assertTrue('good.mojom' not in results[0].message) - def testDeprecatedMojoTypes(self): - ok_paths = ['components/arc'] - warning_paths = ['some/cpp'] - error_paths = ['third_party/blink', 'content'] - test_cases = [ - { - 'type': 'mojo::AssociatedInterfacePtrInfo<>', - 'file': 'file4.cc' - }, - { - 'type': 'mojo::AssociatedInterfaceRequest<>', - 'file': 'file5.cc' - }, - { - 'type': 'mojo::InterfacePtr<>', - 'file': 'file8.cc' - }, - { - 'type': 'mojo::InterfacePtrInfo<>', - 'file': 'file9.cc' - }, - { - 'type': 'mojo::InterfaceRequest<>', - 'file': 'file10.cc' - }, - { - 'type': 'mojo::MakeRequest()', - 'file': 'file11.cc' - }, - ] - - # Build the list of MockFiles considering paths that should trigger warnings - # as well as paths that should trigger errors. - input_api = MockInputApi() - input_api.files = [] - for test_case in test_cases: - for path in ok_paths: - input_api.files.append(MockFile(os.path.join(path, test_case['file']), - [test_case['type']])) - for path in warning_paths: - input_api.files.append(MockFile(os.path.join(path, test_case['file']), - [test_case['type']])) - for path in error_paths: - input_api.files.append(MockFile(os.path.join(path, test_case['file']), - [test_case['type']])) - - results = PRESUBMIT.CheckNoDeprecatedMojoTypes(input_api, MockOutputApi()) - - # warnings are results[0], errors are results[1] - self.assertEqual(2, len(results)) - - for test_case in test_cases: - # Check that no warnings nor errors have been triggered for these paths. - for path in ok_paths: - self.assertFalse(path in results[0].message) - self.assertFalse(path in results[1].message) - - # Check warnings have been triggered for these paths. - for path in warning_paths: - self.assertTrue(path in results[0].message) - self.assertFalse(path in results[1].message) - - # Check errors have been triggered for these paths. - for path in error_paths: - self.assertFalse(path in results[0].message) - self.assertTrue(path in results[1].message) - - class NoProductionCodeUsingTestOnlyFunctionsTest(unittest.TestCase): def testTruePositives(self): mock_input_api = MockInputApi()
diff --git a/WATCHLISTS b/WATCHLISTS index b2b5c0e..55f2499 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -1360,6 +1360,7 @@ }, 'multidevice': { 'filepath': 'ash/components/proximity_auth/'\ + '|ash/components/multidevice/'\ '|ash/multi_device_setup/'\ '|ash/resources/multidevice_resources.grdp'\ '|ash/services/device_sync/'\ @@ -1374,7 +1375,6 @@ '|chrome/browser/ui/webui/chromeos/multidevice_setup/'\ '|chrome/browser/ui/webui/settings/chromeos/multidevice'\ '|chrome/test/data/webui/multidevice_setup/'\ - '|chromeos/components/multidevice/'\ '|ui/webui/resources/cr_components/chromeos/multidevice_setup/' }, 'multipaste': { @@ -1506,6 +1506,9 @@ '|OriginTrial'\ '|ConditionalFeature', }, + 'os_crypt': { + 'filepath': 'components/os_crypt/', + }, 'ozone': { 'filepath': 'ui/ozone/|'\ 'ui/events/ozone/|'\ @@ -2689,6 +2692,7 @@ 'omnibox': ['jdonnelly+watch@chromium.org'], 'origin_trials': ['chasej+watch@chromium.org', 'iclelland+watch@chromium.org'], + 'os_crypt': ['wfh+watch@chromium.org'], 'ozone': ['ozone-reviews@chromium.org'], 'ozone_fuchsia': ['dworsham@google.com', 'emircan@google.com',
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java index 4b124688..95f8910 100644 --- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java +++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -251,9 +251,6 @@ "Executes tasks with the kBootstrap task type on the default task queues " + "(based on priority of the task) rather than a dedicated " + "high-priority task queue."), - Flag.baseFeature(BlinkFeatures.RTC_DISALLOW_PLAN_B_OUTSIDE_DEPRECATION_TRIAL, - "Makes constructing an RTCPeerConnection with {sdpSemantics:'plan-b'} throw " - + "an exception."), Flag.baseFeature(BlinkFeatures.PREFETCH_ANDROID_FONTS, "Enables prefetching Android fonts on renderer startup."), Flag.baseFeature(AwFeatures.WEBVIEW_LEGACY_TLS_SUPPORT, @@ -300,5 +297,8 @@ "Free Canvas2D resources when the webview is in the background."), Flag.baseFeature(VizFeatures.SURFACE_SYNC_THROTTLING, "Enables throttling of Surface Sync to improve rotations"), + Flag.baseFeature(BlinkFeatures.AUTOFILL_SHADOW_DOM, + "Enables Autofill associate form elements with form " + + "control elements across shadow boundaries."), }; }
diff --git a/android_webview/tools/generate_flag_labels.py b/android_webview/tools/generate_flag_labels.py index 84072d39..b301eea 100755 --- a/android_webview/tools/generate_flag_labels.py +++ b/android_webview/tools/generate_flag_labels.py
@@ -51,9 +51,9 @@ 'GmsCoreEmoji': 'GMSCoreEmoji', 'KeyboardAccessoryPaymentVirtualCardFeature': 'IPH_KeyboardAccessoryPaymentVirtualCard', # pylint: disable=line-too-long 'CompositeBgColorAnimation': 'CompositeBGColorAnimation', - 'RtcDisallowPlanBOutsideDeprecationTrial': 'RTCDisallowPlanBOutsideDeprecationTrial', # pylint: disable=line-too-long 'enable-http2-grease-settings': 'http2-grease-settings', - 'AvoidUnnecessaryBeforeUnloadCheckPostTask': 'AvoidUnnecessaryBeforeUnloadCheck', # pylint: disable=line-too-long + 'AvoidUnnecessaryBeforeUnloadCheckPostTask': 'AvoidUnnecessaryBeforeUnloadCheck', # pylint: disable=line-too-long, + 'AutofillShadowDom': 'AutofillShadowDOM' }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 0cbf25f4..76c2f85 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -1388,6 +1388,8 @@ "system/phonehub/tether_connection_pending_view.cc", "system/phonehub/tether_connection_pending_view.h", "system/phonehub/ui_constants.h", + "system/power/adaptive_charging_controller.cc", + "system/power/adaptive_charging_controller.h", "system/power/backlights_forced_off_setter.cc", "system/power/backlights_forced_off_setter.h", "system/power/battery_image_source.cc", @@ -1769,6 +1771,8 @@ "wm/desks/templates/desks_templates_presenter.h", "wm/desks/templates/desks_templates_util.cc", "wm/desks/templates/desks_templates_util.h", + "wm/desks/templates/restore_data_collector.cc", + "wm/desks/templates/restore_data_collector.h", "wm/desks/templates/save_desk_template_button.cc", "wm/desks/templates/save_desk_template_button.h", "wm/desks/zero_state_button.cc", @@ -2070,6 +2074,7 @@ "//ash/components/audio", "//ash/components/fwupd", "//ash/components/geolocation", + "//ash/components/multidevice/logging", "//ash/components/peripheral_notification", "//ash/components/phonehub", "//ash/components/settings", @@ -2104,7 +2109,6 @@ "//cc/paint:paint", "//chromeos/assistant:buildflags", "//chromeos/components/feature_usage", - "//chromeos/components/multidevice/logging", "//chromeos/components/quick_answers/public/cpp:prefs", "//chromeos/components/sensors:buildflags", "//chromeos/components/sensors:sensors", @@ -3094,6 +3098,8 @@ "projector/test/mock_projector_metadata_controller.h", "projector/test/mock_projector_ui_controller.cc", "projector/test/mock_projector_ui_controller.h", + "system/geolocation/test_geolocation_url_loader_factory.cc", + "system/geolocation/test_geolocation_url_loader_factory.h", "test/layer_animation_stopped_waiter.cc", "test/layer_animation_stopped_waiter.h", @@ -3225,6 +3231,7 @@ "//ash/components/attestation:test_support", "//ash/components/audio", "//ash/components/disks:test_support", + "//ash/components/geolocation", "//ash/constants", "//ash/keyboard/ui", "//ash/keyboard/ui:test_support",
diff --git a/ash/DEPS b/ash/DEPS index 88f5332c..66f42b1 100644 --- a/ash/DEPS +++ b/ash/DEPS
@@ -58,7 +58,6 @@ "-chromeos", # //ash can use chromeos components that sit below it in the dependency tree. "+chromeos/components/feature_usage", - "+chromeos/components/multidevice", "+chromeos/components/quick_answers", "+chromeos/components/sensors", "+chromeos/constants",
diff --git a/ash/app_list/views/search_result_tile_item_view.cc b/ash/app_list/views/search_result_tile_item_view.cc index c4fdff020..5fbeaa39 100644 --- a/ash/app_list/views/search_result_tile_item_view.cc +++ b/ash/app_list/views/search_result_tile_item_view.cc
@@ -29,12 +29,12 @@ #include "ui/accessibility/ax_node_data.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/image_model.h" +#include "ui/base/themed_vector_icon.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/paint_vector_icon.h" -#include "ui/native_theme/themed_vector_icon.h" #include "ui/views/accessibility/accessibility_paint_checks.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/background.h" @@ -42,7 +42,6 @@ #include "ui/views/controls/label.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/focus/focus_manager.h" -#include "ui/views/image_model_utils.h" namespace ash { @@ -395,8 +394,7 @@ return; } - gfx::ImageSkia badge_icon_skia = - views::GetImageSkiaFromImageModel(badge_icon, GetColorProvider()); + gfx::ImageSkia badge_icon_skia = badge_icon.Rasterize(GetColorProvider()); if (use_badge_icon_background) { badge_icon_skia =
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc index 6fb4c4e..d508bfb 100644 --- a/ash/app_list/views/search_result_view.cc +++ b/ash/app_list/views/search_result_view.cc
@@ -46,7 +46,6 @@ #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" -#include "ui/views/image_model_utils.h" #include "ui/views/layout/flex_layout.h" #include "ui/views/layout/flex_layout_view.h" #include "ui/views/style/typography.h" @@ -446,8 +445,8 @@ return; } - gfx::ImageSkia badge_icon_skia = views::GetImageSkiaFromImageModel( - result()->badge_icon(), GetColorProvider()); + gfx::ImageSkia badge_icon_skia = + result()->badge_icon().Rasterize(GetColorProvider()); if (result()->use_badge_icon_background()) { badge_icon_skia =
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 9ed542c..df8184d6 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -1871,6 +1871,9 @@ <message name="IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_TITLE" desc="The text of the title of the desks templates delete dialog, which shows up when trying to delete a template."> Delete template? </message> + <message name="IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION" desc="The text of the description of the desks templates unsupported apps dialog, which shows up when trying to save a desk that contains an unsupported app (i.e. Crostini app)."> + Lacros windows aren't currently supported. Other apps will be saved. + </message> <message name="IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LINUX_APPS_DIALOG_DESCRIPTION" desc="The text of the description of the desks templates unsupported apps dialog, which shows up when trying to save a desk that contains an unsupported app (i.e. Crostini app)."> Linux apps aren’t currently supported. Other apps will be saved. </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..0bf8860 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +ac5ac60f1df14eab58e8a94244722f33be076cb9 \ No newline at end of file
diff --git a/ash/components/BUILD.gn b/ash/components/BUILD.gn index 248bb6e..f008060 100644 --- a/ash/components/BUILD.gn +++ b/ash/components/BUILD.gn
@@ -33,6 +33,7 @@ "//ash/components/geolocation:unit_tests", "//ash/components/login/auth:unit_tests", "//ash/components/login/session:unit_tests", + "//ash/components/multidevice:unit_tests", "//ash/components/peripheral_notification:unit_tests", "//ash/components/phonehub:unit_tests", "//ash/components/policy:unit_tests",
diff --git a/ash/components/arc/arc_features.cc b/ash/components/arc/arc_features.cc index a17e6b9..4f9f766d 100644 --- a/ash/components/arc/arc_features.cc +++ b/ash/components/arc/arc_features.cc
@@ -111,7 +111,7 @@ // When enabled, utility processes are spawned to perform hardware decode // acceleration on behalf of ARC++/ARCVM instead of using the GPU process. const base::Feature kOutOfProcessVideoDecoding{ - "OutOfProcessVideoDecoding", base::FEATURE_ENABLED_BY_DEFAULT}; + "OutOfProcessVideoDecoding", base::FEATURE_DISABLED_BY_DEFAULT}; // Controls ARC picture-in-picture feature. If this is enabled, then Android // will control which apps can enter PIP. If this is disabled, then ARC PIP
diff --git a/ash/components/device_activity/device_activity_controller.cc b/ash/components/device_activity/device_activity_controller.cc index bae2ca31..5badb91b 100644 --- a/ash/components/device_activity/device_activity_controller.cc +++ b/ash/components/device_activity/device_activity_controller.cc
@@ -11,6 +11,7 @@ #include "ash/components/device_activity/monthly_use_case_impl.h" #include "base/check_op.h" #include "base/metrics/histogram_functions.h" +#include "base/rand_util.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "chromeos/dbus/session_manager/session_manager_client.h" @@ -76,11 +77,37 @@ unix_epoch); } +// static +base::TimeDelta DeviceActivityController::DetermineStartUpDelay( + base::Time chrome_first_run_ts) { + // |random_delay| picks a random minute between [0, 29] inclusive (30 buckets) + // to delay start. This will distribute the high qps during certain times, + // across 30 equally probable buckets. + base::TimeDelta random_delay = base::Minutes(base::RandInt(0, 29)); + + // Wait at least 10 minutes from the first chrome run sentinel file creation + // time. This creation time is used as an indicator of when the device last + // reset (powerwashed/recovery/RMA). PSM servers take 10 minutes from CheckIn + // to return the correct response for CheckMembership requests, since the PSM + // servers need to update their cache. + // + // This delay avoids the scenario where a device checks in, powerwashes, and + // on device start up, gets the wrong check membership response. + base::TimeDelta delay_on_first_chrome_run; + base::Time current_ts = base::Time::Now(); + if (current_ts < (chrome_first_run_ts + base::Minutes(10))) { + delay_on_first_chrome_run = + chrome_first_run_ts + base::Minutes(10) - current_ts; + } + + return delay_on_first_chrome_run + random_delay; +} + DeviceActivityController::DeviceActivityController( version_info::Channel chromeos_channel, PrefService* local_state, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - base::TimeDelta start_delay) + base::TimeDelta start_up_delay) : statistics_provider_( chromeos::system::StatisticsProvider::GetInstance()) { DeviceActivityClient::RecordDeviceActivityMethodCalled( @@ -90,15 +117,12 @@ DCHECK(!g_ash_device_activity_controller); g_ash_device_activity_controller = this; - // Uses a random minute between [0, 29] inclusive (30 buckets) to delay start. - // This will distribute the high qps during certain times, across 30 equally - // probable buckets. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::BindOnce(&device_activity::DeviceActivityController::Start, weak_factory_.GetWeakPtr(), chromeos_channel, local_state, url_loader_factory), - start_delay); + start_up_delay); } DeviceActivityController::~DeviceActivityController() {
diff --git a/ash/components/device_activity/device_activity_controller.h b/ash/components/device_activity/device_activity_controller.h index 097ef300..08f5c54f 100644 --- a/ash/components/device_activity/device_activity_controller.h +++ b/ash/components/device_activity/device_activity_controller.h
@@ -8,6 +8,7 @@ #include <memory> #include "base/component_export.h" +#include "base/time/time.h" #include "chromeos/system/statistics_provider.h" #include "services/network/public/cpp/shared_url_loader_factory.h" @@ -32,11 +33,15 @@ // Registers local state preferences. static void RegisterPrefs(PrefRegistrySimple* registry); + // Determines the total start up delay before starting device activity + // reporting. + static base::TimeDelta DetermineStartUpDelay(base::Time chrome_first_run_ts); + DeviceActivityController( version_info::Channel chromeos_channel, PrefService* local_state, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - base::TimeDelta start_delay); + base::TimeDelta start_up_delay); DeviceActivityController(const DeviceActivityController&) = delete; DeviceActivityController& operator=(const DeviceActivityController&) = delete; ~DeviceActivityController();
diff --git a/ash/components/device_activity/device_activity_controller_unittest.cc b/ash/components/device_activity/device_activity_controller_unittest.cc index 0c2fe703..6cfcc5f1 100644 --- a/ash/components/device_activity/device_activity_controller_unittest.cc +++ b/ash/components/device_activity/device_activity_controller_unittest.cc
@@ -53,7 +53,7 @@ device_activity_controller_ = std::make_unique<DeviceActivityController>( kFakeChromeOSChannel, local_state(), test_shared_loader_factory_, - /* start_delay */ base::Minutes(0)); + /* start_up_delay */ base::Minutes(0)); } void TearDown() override { device_activity_controller_.reset(); }
diff --git a/chromeos/components/multidevice/BUILD.gn b/ash/components/multidevice/BUILD.gn similarity index 89% rename from chromeos/components/multidevice/BUILD.gn rename to ash/components/multidevice/BUILD.gn index 98781b8..7cee0074 100644 --- a/chromeos/components/multidevice/BUILD.gn +++ b/ash/components/multidevice/BUILD.gn
@@ -2,7 +2,9 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -assert(is_chromeos, "MultiDevice is Chrome OS only") +import("//build/config/chromeos/ui_mode.gni") + +assert(is_chromeos_ash) static_library("multidevice") { sources = [ @@ -27,10 +29,10 @@ ] deps = [ + "//ash/components/multidevice/logging", "//ash/services/device_sync/proto:util", "//base", "//base:i18n", - "//chromeos/components/multidevice/logging", "//chromeos/dbus", "//chromeos/dbus/easy_unlock", "//components/prefs", @@ -88,10 +90,10 @@ deps = [ ":multidevice", ":test_support", + "//ash/components/multidevice/logging:unit_tests", + "//ash/components/multidevice/mojom:unit_tests", "//ash/services/device_sync/proto", "//base/test:test_support", - "//chromeos/components/multidevice/logging:unit_tests", - "//chromeos/components/multidevice/mojom:unit_tests", "//testing/gtest", ] }
diff --git a/ash/components/multidevice/DEPS b/ash/components/multidevice/DEPS new file mode 100644 index 0000000..c69a828b --- /dev/null +++ b/ash/components/multidevice/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+third_party/securemessage/proto", +]
diff --git a/chromeos/components/multidevice/DIR_METADATA b/ash/components/multidevice/DIR_METADATA similarity index 100% rename from chromeos/components/multidevice/DIR_METADATA rename to ash/components/multidevice/DIR_METADATA
diff --git a/chromeos/components/multidevice/OWNERS b/ash/components/multidevice/OWNERS similarity index 100% rename from chromeos/components/multidevice/OWNERS rename to ash/components/multidevice/OWNERS
diff --git a/chromeos/components/multidevice/beacon_seed.cc b/ash/components/multidevice/beacon_seed.cc similarity index 98% rename from chromeos/components/multidevice/beacon_seed.cc rename to ash/components/multidevice/beacon_seed.cc index f91fb05..587f511 100644 --- a/chromeos/components/multidevice/beacon_seed.cc +++ b/ash/components/multidevice/beacon_seed.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/beacon_seed.h" +#include "ash/components/multidevice/beacon_seed.h" #include <algorithm>
diff --git a/chromeos/components/multidevice/beacon_seed.h b/ash/components/multidevice/beacon_seed.h similarity index 93% rename from chromeos/components/multidevice/beacon_seed.h rename to ash/components/multidevice/beacon_seed.h index 8cd26a49..46ae45f 100644 --- a/chromeos/components/multidevice/beacon_seed.h +++ b/ash/components/multidevice/beacon_seed.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_ +#define ASH_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_ #include <google/protobuf/repeated_field.h> #include <ostream> @@ -76,4 +76,4 @@ } // namespace multidevice } // namespace ash -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_
diff --git a/chromeos/components/multidevice/expiring_remote_device_cache.cc b/ash/components/multidevice/expiring_remote_device_cache.cc similarity index 93% rename from chromeos/components/multidevice/expiring_remote_device_cache.cc rename to ash/components/multidevice/expiring_remote_device_cache.cc index d8fe0fb..30ccc26 100644 --- a/chromeos/components/multidevice/expiring_remote_device_cache.cc +++ b/ash/components/multidevice/expiring_remote_device_cache.cc
@@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/expiring_remote_device_cache.h" +#include "ash/components/multidevice/expiring_remote_device_cache.h" +#include "ash/components/multidevice/remote_device_cache.h" #include "base/containers/contains.h" -#include "chromeos/components/multidevice/remote_device_cache.h" namespace chromeos {
diff --git a/chromeos/components/multidevice/expiring_remote_device_cache.h b/ash/components/multidevice/expiring_remote_device_cache.h similarity index 86% rename from chromeos/components/multidevice/expiring_remote_device_cache.h rename to ash/components/multidevice/expiring_remote_device_cache.h index 5fc71aa..0b4d1a38 100644 --- a/chromeos/components/multidevice/expiring_remote_device_cache.h +++ b/ash/components/multidevice/expiring_remote_device_cache.h
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_ +#define ASH_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_ #include <memory> #include <string> +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "base/containers/flat_set.h" -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/remote_device_cache.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos { @@ -74,4 +74,4 @@ } // namespace chromeos -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_
diff --git a/chromeos/components/multidevice/expiring_remote_device_cache_unittest.cc b/ash/components/multidevice/expiring_remote_device_cache_unittest.cc similarity index 95% rename from chromeos/components/multidevice/expiring_remote_device_cache_unittest.cc rename to ash/components/multidevice/expiring_remote_device_cache_unittest.cc index 175e0157..4f047c12 100644 --- a/chromeos/components/multidevice/expiring_remote_device_cache_unittest.cc +++ b/ash/components/multidevice/expiring_remote_device_cache_unittest.cc
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/expiring_remote_device_cache.h" +#include "ash/components/multidevice/expiring_remote_device_cache.h" #include <algorithm> -#include "chromeos/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos {
diff --git a/chromeos/components/multidevice/fake_secure_message_delegate.cc b/ash/components/multidevice/fake_secure_message_delegate.cc similarity index 98% rename from chromeos/components/multidevice/fake_secure_message_delegate.cc rename to ash/components/multidevice/fake_secure_message_delegate.cc index d8fabc5b..8ed3197 100644 --- a/chromeos/components/multidevice/fake_secure_message_delegate.cc +++ b/ash/components/multidevice/fake_secure_message_delegate.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" +#include "ash/components/multidevice/fake_secure_message_delegate.h" #include <stddef.h>
diff --git a/chromeos/components/multidevice/fake_secure_message_delegate.h b/ash/components/multidevice/fake_secure_message_delegate.h similarity index 88% rename from chromeos/components/multidevice/fake_secure_message_delegate.h rename to ash/components/multidevice/fake_secure_message_delegate.h index c8ec59b..8e63fddb 100644 --- a/chromeos/components/multidevice/fake_secure_message_delegate.h +++ b/ash/components/multidevice/fake_secure_message_delegate.h
@@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_ +#define ASH_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_ #include <memory> -#include "chromeos/components/multidevice/secure_message_delegate.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" +#include "ash/components/multidevice/secure_message_delegate.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" namespace chromeos { @@ -85,4 +85,4 @@ using ::chromeos::multidevice::FakeSecureMessageDelegateFactory; } // namespace ash::multidevice -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_
diff --git a/chromeos/components/multidevice/fake_secure_message_delegate_unittest.cc b/ash/components/multidevice/fake_secure_message_delegate_unittest.cc similarity index 98% rename from chromeos/components/multidevice/fake_secure_message_delegate_unittest.cc rename to ash/components/multidevice/fake_secure_message_delegate_unittest.cc index 1d0ab90b..7ead603 100644 --- a/chromeos/components/multidevice/fake_secure_message_delegate_unittest.cc +++ b/ash/components/multidevice/fake_secure_message_delegate_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" +#include "ash/components/multidevice/fake_secure_message_delegate.h" #include "base/bind.h" #include "base/callback.h"
diff --git a/chromeos/components/multidevice/logging/BUILD.gn b/ash/components/multidevice/logging/BUILD.gn similarity index 86% rename from chromeos/components/multidevice/logging/BUILD.gn rename to ash/components/multidevice/logging/BUILD.gn index 2db4ca0..218e7572 100644 --- a/chromeos/components/multidevice/logging/BUILD.gn +++ b/ash/components/multidevice/logging/BUILD.gn
@@ -2,6 +2,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chromeos/ui_mode.gni") + +assert(is_chromeos_ash) + static_library("logging") { sources = [ "log_buffer.cc",
diff --git a/chromeos/components/multidevice/logging/log_buffer.cc b/ash/components/multidevice/logging/log_buffer.cc similarity index 96% rename from chromeos/components/multidevice/logging/log_buffer.cc rename to ash/components/multidevice/logging/log_buffer.cc index 5c6709e9..fb3e023e 100644 --- a/chromeos/components/multidevice/logging/log_buffer.cc +++ b/ash/components/multidevice/logging/log_buffer.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/logging/log_buffer.h" +#include "ash/components/multidevice/logging/log_buffer.h" #include "base/no_destructor.h" #include "base/synchronization/lock.h"
diff --git a/chromeos/components/multidevice/logging/log_buffer.h b/ash/components/multidevice/logging/log_buffer.h similarity index 92% rename from chromeos/components/multidevice/logging/log_buffer.h rename to ash/components/multidevice/logging/log_buffer.h index b3b366df..4217ce2 100644 --- a/chromeos/components/multidevice/logging/log_buffer.h +++ b/ash/components/multidevice/logging/log_buffer.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_ +#define ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_ #include <stddef.h> @@ -92,4 +92,4 @@ } // namespace multidevice } // namespace ash -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_
diff --git a/chromeos/components/multidevice/logging/logging.cc b/ash/components/multidevice/logging/logging.cc similarity index 92% rename from chromeos/components/multidevice/logging/logging.cc rename to ash/components/multidevice/logging/logging.cc index 4f3fa36..bd4c9e5 100644 --- a/chromeos/components/multidevice/logging/logging.cc +++ b/ash/components/multidevice/logging/logging.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/logging/log_buffer.h" +#include "ash/components/multidevice/logging/log_buffer.h" namespace chromeos {
diff --git a/chromeos/components/multidevice/logging/logging.h b/ash/components/multidevice/logging/logging.h similarity index 92% rename from chromeos/components/multidevice/logging/logging.h rename to ash/components/multidevice/logging/logging.h index 9b504b593..112b519a 100644 --- a/chromeos/components/multidevice/logging/logging.h +++ b/ash/components/multidevice/logging/logging.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_ +#define ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_ #include <sstream> @@ -70,4 +70,4 @@ } // namespace multidevice } // namespace ash -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_
diff --git a/chromeos/components/multidevice/logging/logging_unittest.cc b/ash/components/multidevice/logging/logging_unittest.cc similarity index 96% rename from chromeos/components/multidevice/logging/logging_unittest.cc rename to ash/components/multidevice/logging/logging_unittest.cc index 8f96b35e..1e4f4289 100644 --- a/chromeos/components/multidevice/logging/logging_unittest.cc +++ b/ash/components/multidevice/logging/logging_unittest.cc
@@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" #include <stddef.h> +#include "ash/components/multidevice/logging/log_buffer.h" #include "base/lazy_instance.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" -#include "chromeos/components/multidevice/logging/log_buffer.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos {
diff --git a/chromeos/components/multidevice/mojom/BUILD.gn b/ash/components/multidevice/mojom/BUILD.gn similarity index 87% rename from chromeos/components/multidevice/mojom/BUILD.gn rename to ash/components/multidevice/mojom/BUILD.gn index 02ace131..c0399992 100644 --- a/chromeos/components/multidevice/mojom/BUILD.gn +++ b/ash/components/multidevice/mojom/BUILD.gn
@@ -2,8 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chromeos/ui_mode.gni") import("//mojo/public/tools/bindings/mojom.gni") +assert(is_chromeos_ash) + mojom("mojom") { sources = [ "multidevice_types.mojom" ] public_deps = [ "//mojo/public/mojom/base" ] @@ -26,12 +29,12 @@ traits_headers = [ "multidevice_mojom_traits.h" ] traits_sources = [ "multidevice_mojom_traits.cc" ] traits_public_deps = [ + "//ash/components/multidevice", "//base", - "//chromeos/components/multidevice", ] traits_deps = [ + "//ash/components/multidevice/logging", "//ash/services/device_sync/proto", - "//chromeos/components/multidevice/logging", "//device/bluetooth/public/cpp", ] }, @@ -45,9 +48,9 @@ deps = [ ":mojom", + "//ash/components/multidevice", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", "//mojo/public/cpp/test_support:test_utils", "//testing/gtest", ]
diff --git a/chromeos/components/multidevice/mojom/OWNERS b/ash/components/multidevice/mojom/OWNERS similarity index 100% rename from chromeos/components/multidevice/mojom/OWNERS rename to ash/components/multidevice/mojom/OWNERS
diff --git a/chromeos/components/multidevice/mojom/multidevice_mojom_traits.cc b/ash/components/multidevice/mojom/multidevice_mojom_traits.cc similarity index 98% rename from chromeos/components/multidevice/mojom/multidevice_mojom_traits.cc rename to ash/components/multidevice/mojom/multidevice_mojom_traits.cc index e4be468..825595a 100644 --- a/chromeos/components/multidevice/mojom/multidevice_mojom_traits.cc +++ b/ash/components/multidevice/mojom/multidevice_mojom_traits.cc
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/mojom/multidevice_mojom_traits.h" +#include "ash/components/multidevice/mojom/multidevice_mojom_traits.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "base/notreached.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "device/bluetooth/public/cpp/bluetooth_address.h" #include "mojo/public/cpp/base/time_mojom_traits.h"
diff --git a/chromeos/components/multidevice/mojom/multidevice_mojom_traits.h b/ash/components/multidevice/mojom/multidevice_mojom_traits.h similarity index 85% rename from chromeos/components/multidevice/mojom/multidevice_mojom_traits.h rename to ash/components/multidevice/mojom/multidevice_mojom_traits.h index 31e5a1d..fad5ef0 100644 --- a/chromeos/components/multidevice/mojom/multidevice_mojom_traits.h +++ b/ash/components/multidevice/mojom/multidevice_mojom_traits.h
@@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_ +#define ASH_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_ #include <string> #include <vector> +#include "ash/components/multidevice/beacon_seed.h" +#include "ash/components/multidevice/mojom/multidevice_types.mojom-shared.h" +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/beacon_seed.h" -#include "chromeos/components/multidevice/mojom/multidevice_types.mojom-shared.h" -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "mojo/public/cpp/bindings/enum_traits.h" #include "mojo/public/cpp/bindings/struct_traits.h" @@ -87,4 +87,4 @@ } // namespace mojo -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_
diff --git a/chromeos/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc b/ash/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc similarity index 94% rename from chromeos/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc rename to ash/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc index b835c1d0..2a516e0 100644 --- a/chromeos/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc +++ b/ash/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/mojom/multidevice_mojom_traits.h" +#include "ash/components/multidevice/mojom/multidevice_mojom_traits.h" +#include "ash/components/multidevice/beacon_seed.h" +#include "ash/components/multidevice/mojom/multidevice_types.mojom.h" +#include "ash/components/multidevice/remote_device.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/beacon_seed.h" -#include "chromeos/components/multidevice/mojom/multidevice_types.mojom.h" -#include "chromeos/components/multidevice/remote_device.h" #include "mojo/public/cpp/base/time_mojom_traits.h" #include "mojo/public/cpp/test_support/test_utils.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromeos/components/multidevice/mojom/multidevice_types.mojom b/ash/components/multidevice/mojom/multidevice_types.mojom similarity index 100% rename from chromeos/components/multidevice/mojom/multidevice_types.mojom rename to ash/components/multidevice/mojom/multidevice_types.mojom
diff --git a/chromeos/components/multidevice/remote_device.cc b/ash/components/multidevice/remote_device.cc similarity index 97% rename from chromeos/components/multidevice/remote_device.cc rename to ash/components/multidevice/remote_device.cc index 51ef814..4a9ee52 100644 --- a/chromeos/components/multidevice/remote_device.cc +++ b/ash/components/multidevice/remote_device.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device.h" #include "base/base64.h"
diff --git a/chromeos/components/multidevice/remote_device.h b/ash/components/multidevice/remote_device.h similarity index 89% rename from chromeos/components/multidevice/remote_device.h rename to ash/components/multidevice/remote_device.h index 90f5617..b1d6a88d 100644 --- a/chromeos/components/multidevice/remote_device.h +++ b/ash/components/multidevice/remote_device.h
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_ +#define ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_ #include <map> #include <string> #include <vector> -#include "chromeos/components/multidevice/beacon_seed.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" +#include "ash/components/multidevice/beacon_seed.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" namespace chromeos { @@ -94,4 +94,4 @@ } // namespace multidevice } // namespace ash -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_
diff --git a/chromeos/components/multidevice/remote_device_cache.cc b/ash/components/multidevice/remote_device_cache.cc similarity index 96% rename from chromeos/components/multidevice/remote_device_cache.cc rename to ash/components/multidevice/remote_device_cache.cc index 43f068b..f5b3e8c6 100644 --- a/chromeos/components/multidevice/remote_device_cache.cc +++ b/ash/components/multidevice/remote_device_cache.cc
@@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/chromeos/components/multidevice/remote_device_cache.h b/ash/components/multidevice/remote_device_cache.h similarity index 89% rename from chromeos/components/multidevice/remote_device_cache.h rename to ash/components/multidevice/remote_device_cache.h index e0ff593..95d5cad 100644 --- a/chromeos/components/multidevice/remote_device_cache.h +++ b/ash/components/multidevice/remote_device_cache.h
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_ +#define ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_ #include <memory> #include <string> #include <vector> -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos { @@ -87,4 +87,4 @@ } } // namespace ash -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_
diff --git a/chromeos/components/multidevice/remote_device_cache_unittest.cc b/ash/components/multidevice/remote_device_cache_unittest.cc similarity index 97% rename from chromeos/components/multidevice/remote_device_cache_unittest.cc rename to ash/components/multidevice/remote_device_cache_unittest.cc index 7638b9d2..389beb2 100644 --- a/chromeos/components/multidevice/remote_device_cache_unittest.cc +++ b/ash/components/multidevice/remote_device_cache_unittest.cc
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/remote_device_cache.h" #include <algorithm> -#include "chromeos/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos {
diff --git a/chromeos/components/multidevice/remote_device_ref.cc b/ash/components/multidevice/remote_device_ref.cc similarity index 96% rename from chromeos/components/multidevice/remote_device_ref.cc rename to ash/components/multidevice/remote_device_ref.cc index 82598714..67d32e2 100644 --- a/chromeos/components/multidevice/remote_device_ref.cc +++ b/ash/components/multidevice/remote_device_ref.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" #include <sstream>
diff --git a/chromeos/components/multidevice/remote_device_ref.h b/ash/components/multidevice/remote_device_ref.h similarity index 93% rename from chromeos/components/multidevice/remote_device_ref.h rename to ash/components/multidevice/remote_device_ref.h index 63e441cd..f26d8e0 100644 --- a/chromeos/components/multidevice/remote_device_ref.h +++ b/ash/components/multidevice/remote_device_ref.h
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_ +#define ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_ #include <memory> #include <string> #include <vector> +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/software_feature_state.h" #include "base/gtest_prod_util.h" -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/software_feature_state.h" namespace ash { class EasyUnlockServiceRegular; @@ -134,4 +134,4 @@ } // namespace multidevice } // namespace ash -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_
diff --git a/chromeos/components/multidevice/remote_device_ref_unittest.cc b/ash/components/multidevice/remote_device_ref_unittest.cc similarity index 96% rename from chromeos/components/multidevice/remote_device_ref_unittest.cc rename to ash/components/multidevice/remote_device_ref_unittest.cc index 457acac..b22dfa07 100644 --- a/chromeos/components/multidevice/remote_device_ref_unittest.cc +++ b/ash/components/multidevice/remote_device_ref_unittest.cc
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" #include <memory> -#include "chromeos/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos {
diff --git a/chromeos/components/multidevice/remote_device_test_util.cc b/ash/components/multidevice/remote_device_test_util.cc similarity index 98% rename from chromeos/components/multidevice/remote_device_test_util.cc rename to ash/components/multidevice/remote_device_test_util.cc index 2337992..cc05136 100644 --- a/chromeos/components/multidevice/remote_device_test_util.cc +++ b/ash/components/multidevice/remote_device_test_util.cc
@@ -5,7 +5,7 @@ #include <map> #include <string> -#include "chromeos/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "base/base64.h" #include "base/base64url.h"
diff --git a/chromeos/components/multidevice/remote_device_test_util.h b/ash/components/multidevice/remote_device_test_util.h similarity index 90% rename from chromeos/components/multidevice/remote_device_test_util.h rename to ash/components/multidevice/remote_device_test_util.h index a0065d93..20547e0 100644 --- a/chromeos/components/multidevice/remote_device_test_util.h +++ b/ash/components/multidevice/remote_device_test_util.h
@@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_ +#define ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_ #include <memory> #include <string> #include <vector> -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" namespace chromeos { @@ -78,4 +78,4 @@ } // namespace multidevice } // namespace ash -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_
diff --git a/chromeos/components/multidevice/secure_message_delegate.cc b/ash/components/multidevice/secure_message_delegate.cc similarity index 91% rename from chromeos/components/multidevice/secure_message_delegate.cc rename to ash/components/multidevice/secure_message_delegate.cc index 8e02f29..5a2ea43 100644 --- a/chromeos/components/multidevice/secure_message_delegate.cc +++ b/ash/components/multidevice/secure_message_delegate.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/secure_message_delegate.h" +#include "ash/components/multidevice/secure_message_delegate.h" namespace chromeos {
diff --git a/chromeos/components/multidevice/secure_message_delegate.h b/ash/components/multidevice/secure_message_delegate.h similarity index 95% rename from chromeos/components/multidevice/secure_message_delegate.h rename to ash/components/multidevice/secure_message_delegate.h index a354419..79b7f84 100644 --- a/chromeos/components/multidevice/secure_message_delegate.h +++ b/ash/components/multidevice/secure_message_delegate.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_ +#define ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_ #include <memory> #include <string> @@ -107,4 +107,4 @@ using ::chromeos::multidevice::SecureMessageDelegate; } -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_
diff --git a/chromeos/components/multidevice/secure_message_delegate_impl.cc b/ash/components/multidevice/secure_message_delegate_impl.cc similarity index 97% rename from chromeos/components/multidevice/secure_message_delegate_impl.cc rename to ash/components/multidevice/secure_message_delegate_impl.cc index dcabd44..1f9b24a 100644 --- a/chromeos/components/multidevice/secure_message_delegate_impl.cc +++ b/ash/components/multidevice/secure_message_delegate_impl.cc
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/easy_unlock/easy_unlock_client.h" #include "third_party/cros_system_api/dbus/service_constants.h"
diff --git a/chromeos/components/multidevice/secure_message_delegate_impl.h b/ash/components/multidevice/secure_message_delegate_impl.h similarity index 91% rename from chromeos/components/multidevice/secure_message_delegate_impl.h rename to ash/components/multidevice/secure_message_delegate_impl.h index 08baa08b..fa336b77 100644 --- a/chromeos/components/multidevice/secure_message_delegate_impl.h +++ b/ash/components/multidevice/secure_message_delegate_impl.h
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_ +#define ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_ +#include "ash/components/multidevice/secure_message_delegate.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/secure_message_delegate.h" namespace chromeos { @@ -85,4 +85,4 @@ using ::chromeos::multidevice::SecureMessageDelegateImpl; } -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_
diff --git a/chromeos/components/multidevice/software_feature.cc b/ash/components/multidevice/software_feature.cc similarity index 98% rename from chromeos/components/multidevice/software_feature.cc rename to ash/components/multidevice/software_feature.cc index 6c76e81..4c87b967 100644 --- a/chromeos/components/multidevice/software_feature.cc +++ b/ash/components/multidevice/software_feature.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature.h" #include "base/notreached.h"
diff --git a/chromeos/components/multidevice/software_feature.h b/ash/components/multidevice/software_feature.h similarity index 93% rename from chromeos/components/multidevice/software_feature.h rename to ash/components/multidevice/software_feature.h index 5c33e1a..458ca62 100644 --- a/chromeos/components/multidevice/software_feature.h +++ b/ash/components/multidevice/software_feature.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_ +#define ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_ #include <ostream> @@ -83,4 +83,4 @@ } } // namespace ash -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_
diff --git a/chromeos/components/multidevice/software_feature_state.cc b/ash/components/multidevice/software_feature_state.cc similarity index 90% rename from chromeos/components/multidevice/software_feature_state.cc rename to ash/components/multidevice/software_feature_state.cc index 25d0f95c..b1f692e 100644 --- a/chromeos/components/multidevice/software_feature_state.cc +++ b/ash/components/multidevice/software_feature_state.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/software_feature_state.h" +#include "ash/components/multidevice/software_feature_state.h" namespace chromeos {
diff --git a/chromeos/components/multidevice/software_feature_state.h b/ash/components/multidevice/software_feature_state.h similarity index 84% rename from chromeos/components/multidevice/software_feature_state.h rename to ash/components/multidevice/software_feature_state.h index b9fa4d0..edaa72fa 100644 --- a/chromeos/components/multidevice/software_feature_state.h +++ b/ash/components/multidevice/software_feature_state.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_ +#define ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_ #include <ostream> @@ -41,4 +41,4 @@ } } // namespace ash -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_
diff --git a/chromeos/components/multidevice/stub_multidevice_util.cc b/ash/components/multidevice/stub_multidevice_util.cc similarity index 97% rename from chromeos/components/multidevice/stub_multidevice_util.cc rename to ash/components/multidevice/stub_multidevice_util.cc index 0f876fd0..c8b2b20 100644 --- a/chromeos/components/multidevice/stub_multidevice_util.cc +++ b/ash/components/multidevice/stub_multidevice_util.cc
@@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/components/multidevice/stub_multidevice_util.h" +#include "ash/components/multidevice/stub_multidevice_util.h" #include <map> #include <vector> +#include "ash/components/multidevice/beacon_seed.h" #include "ash/constants/ash_features.h" #include "base/base64.h" #include "base/base64url.h" #include "base/no_destructor.h" #include "base/system/sys_info.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/beacon_seed.h" namespace chromeos {
diff --git a/chromeos/components/multidevice/stub_multidevice_util.h b/ash/components/multidevice/stub_multidevice_util.h similarity index 80% rename from chromeos/components/multidevice/stub_multidevice_util.h rename to ash/components/multidevice/stub_multidevice_util.h index 2366ce9..6762822 100644 --- a/chromeos/components/multidevice/stub_multidevice_util.h +++ b/ash/components/multidevice/stub_multidevice_util.h
@@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_ -#define CHROMEOS_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_ +#ifndef ASH_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_ +#define ASH_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_ -#include "chromeos/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device.h" namespace chromeos { @@ -35,4 +35,4 @@ } } // namespace ash -#endif // CHROMEOS_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_ +#endif // ASH_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_
diff --git a/ash/components/phonehub/BUILD.gn b/ash/components/phonehub/BUILD.gn index badbe06..601dc8cf 100644 --- a/ash/components/phonehub/BUILD.gn +++ b/ash/components/phonehub/BUILD.gn
@@ -115,6 +115,8 @@ ] deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/components/phonehub/proto", "//ash/constants", "//ash/services/device_sync/public/cpp", @@ -124,8 +126,6 @@ "//ash/services/secure_channel/public/mojom", "//ash/webui/eche_app_ui:eche_app_ui_pref", "//base", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", "//chromeos/dbus/power", "//chromeos/services/network_config", "//chromeos/services/network_config:in_process_instance", @@ -179,11 +179,11 @@ public_deps = [ ":phonehub" ] deps = [ + "//ash/components/multidevice/logging", "//ash/components/phonehub/proto", "//ash/constants", "//ash/services/multidevice_setup/public/mojom", "//base", - "//chromeos/components/multidevice/logging", ] } @@ -253,6 +253,8 @@ ":debug", ":phonehub", ":test_support", + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", "//ash/components/phonehub/proto", "//ash/constants", "//ash/services/device_sync/public/cpp", @@ -265,8 +267,6 @@ "//ash/webui/eche_app_ui:eche_app_ui_pref", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", "//chromeos/dbus/power", "//chromeos/network:network", "//chromeos/network:test_support",
diff --git a/ash/components/phonehub/browser_tabs_model.cc b/ash/components/phonehub/browser_tabs_model.cc index bdfd88e3..2dc923b 100644 --- a/ash/components/phonehub/browser_tabs_model.cc +++ b/ash/components/phonehub/browser_tabs_model.cc
@@ -4,7 +4,7 @@ #include "ash/components/phonehub/browser_tabs_model.h" -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" namespace ash { namespace phonehub {
diff --git a/ash/components/phonehub/camera_roll_thumbnail_decoder_impl.cc b/ash/components/phonehub/camera_roll_thumbnail_decoder_impl.cc index 515cf12..610ce1e 100644 --- a/ash/components/phonehub/camera_roll_thumbnail_decoder_impl.cc +++ b/ash/components/phonehub/camera_roll_thumbnail_decoder_impl.cc
@@ -4,13 +4,13 @@ #include "ash/components/phonehub/camera_roll_thumbnail_decoder_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/camera_roll_item.h" #include "ash/components/phonehub/proto/phonehub_api.pb.h" #include "base/bind.h" #include "base/callback.h" #include "base/containers/flat_map.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "services/data_decoder/public/cpp/decode_image.h" #include "services/data_decoder/public/mojom/image_decoder.mojom.h" #include "ui/gfx/image/image.h"
diff --git a/ash/components/phonehub/connection_scheduler_impl.cc b/ash/components/phonehub/connection_scheduler_impl.cc index 3eb85659..0eaa6acf 100644 --- a/ash/components/phonehub/connection_scheduler_impl.cc +++ b/ash/components/phonehub/connection_scheduler_impl.cc
@@ -4,11 +4,11 @@ #include "ash/components/phonehub/connection_scheduler_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/feature_status.h" #include "ash/services/secure_channel/public/cpp/client/connection_manager.h" #include "base/bind.h" #include "base/threading/sequenced_task_runner_handle.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace phonehub {
diff --git a/ash/components/phonehub/cros_state_sender.cc b/ash/components/phonehub/cros_state_sender.cc index f71cf278..4e01c5e 100644 --- a/ash/components/phonehub/cros_state_sender.cc +++ b/ash/components/phonehub/cros_state_sender.cc
@@ -4,10 +4,10 @@ #include "ash/components/phonehub/cros_state_sender.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/message_sender.h" #include "ash/components/phonehub/phone_model.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace phonehub {
diff --git a/ash/components/phonehub/do_not_disturb_controller_impl.cc b/ash/components/phonehub/do_not_disturb_controller_impl.cc index db99419..8a4dcab 100644 --- a/ash/components/phonehub/do_not_disturb_controller_impl.cc +++ b/ash/components/phonehub/do_not_disturb_controller_impl.cc
@@ -4,9 +4,9 @@ #include "ash/components/phonehub/do_not_disturb_controller_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/message_sender.h" #include "ash/components/phonehub/user_action_recorder.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace phonehub {
diff --git a/ash/components/phonehub/fake_camera_roll_manager.cc b/ash/components/phonehub/fake_camera_roll_manager.cc index e10972f..9cf6262f 100644 --- a/ash/components/phonehub/fake_camera_roll_manager.cc +++ b/ash/components/phonehub/fake_camera_roll_manager.cc
@@ -4,8 +4,8 @@ #include "ash/components/phonehub/fake_camera_roll_manager.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/proto/phonehub_api.pb.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace phonehub {
diff --git a/ash/components/phonehub/fake_multidevice_feature_access_manager.cc b/ash/components/phonehub/fake_multidevice_feature_access_manager.cc index a97c99d..cc98fdd9 100644 --- a/ash/components/phonehub/fake_multidevice_feature_access_manager.cc +++ b/ash/components/phonehub/fake_multidevice_feature_access_manager.cc
@@ -52,6 +52,7 @@ return; apps_access_status_ = apps_access_status; + NotifyAppsAccessChanged(); } MultideviceFeatureAccessManager::AccessStatus
diff --git a/ash/components/phonehub/feature_status_provider_impl.cc b/ash/components/phonehub/feature_status_provider_impl.cc index a102ad70..bc256a9e 100644 --- a/ash/components/phonehub/feature_status_provider_impl.cc +++ b/ash/components/phonehub/feature_status_provider_impl.cc
@@ -6,14 +6,14 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "base/bind.h" #include "base/metrics/histogram_macros.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "device/bluetooth/bluetooth_adapter_factory.h" namespace ash {
diff --git a/ash/components/phonehub/feature_status_provider_impl_unittest.cc b/ash/components/phonehub/feature_status_provider_impl_unittest.cc index 238df4ba..80f04450 100644 --- a/ash/components/phonehub/feature_status_provider_impl_unittest.cc +++ b/ash/components/phonehub/feature_status_provider_impl_unittest.cc
@@ -7,11 +7,11 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" #include "ash/services/secure_channel/public/cpp/client/fake_connection_manager.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power/power_manager_client.h" #include "components/session_manager/core/session_manager.h"
diff --git a/ash/components/phonehub/find_my_device_controller_impl.cc b/ash/components/phonehub/find_my_device_controller_impl.cc index 5dd2777..5a900dd 100644 --- a/ash/components/phonehub/find_my_device_controller_impl.cc +++ b/ash/components/phonehub/find_my_device_controller_impl.cc
@@ -4,9 +4,9 @@ #include "ash/components/phonehub/find_my_device_controller_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/message_sender.h" #include "ash/components/phonehub/user_action_recorder.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace phonehub {
diff --git a/ash/components/phonehub/invalid_connection_disconnector.cc b/ash/components/phonehub/invalid_connection_disconnector.cc index b4f50571..1577953 100644 --- a/ash/components/phonehub/invalid_connection_disconnector.cc +++ b/ash/components/phonehub/invalid_connection_disconnector.cc
@@ -4,10 +4,10 @@ #include "ash/components/phonehub/invalid_connection_disconnector.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/phone_model.h" #include "base/time/time.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace phonehub {
diff --git a/ash/components/phonehub/message_receiver.h b/ash/components/phonehub/message_receiver.h index 16ffa5c8..593a65c2 100644 --- a/ash/components/phonehub/message_receiver.h +++ b/ash/components/phonehub/message_receiver.h
@@ -5,10 +5,10 @@ #ifndef ASH_COMPONENTS_PHONEHUB_MESSAGE_RECEIVER_H_ #define ASH_COMPONENTS_PHONEHUB_MESSAGE_RECEIVER_H_ +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/proto/phonehub_api.pb.h" #include "base/observer_list.h" #include "base/observer_list_types.h" -#include "chromeos/components/multidevice/logging/logging.h" // Responsible for receiving message updates from the remote phone device. namespace ash {
diff --git a/ash/components/phonehub/multidevice_feature_access_manager.cc b/ash/components/phonehub/multidevice_feature_access_manager.cc index 706d7af..5ef4348 100644 --- a/ash/components/phonehub/multidevice_feature_access_manager.cc +++ b/ash/components/phonehub/multidevice_feature_access_manager.cc
@@ -4,9 +4,9 @@ #include "ash/components/phonehub/multidevice_feature_access_manager.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace phonehub { @@ -55,6 +55,11 @@ observer.OnCameraRollAccessChanged(); } +void MultideviceFeatureAccessManager::NotifyAppsAccessChanged() { + for (auto& observer : observer_list_) + observer.OnAppsAccessChanged(); +} + void MultideviceFeatureAccessManager::SetNotificationSetupOperationStatus( NotificationAccessSetupOperation::Status new_status) { DCHECK(IsSetupOperationInProgress()); @@ -92,6 +97,10 @@ // Optional method, inherit class doesn't have to implement this } +void MultideviceFeatureAccessManager::Observer::OnAppsAccessChanged() { + // Optional method, inherit class doesn't have to implement this +} + std::ostream& operator<<(std::ostream& stream, MultideviceFeatureAccessManager::AccessStatus status) { switch (status) {
diff --git a/ash/components/phonehub/multidevice_feature_access_manager.h b/ash/components/phonehub/multidevice_feature_access_manager.h index dfaf4b37..2b8b8710 100644 --- a/ash/components/phonehub/multidevice_feature_access_manager.h +++ b/ash/components/phonehub/multidevice_feature_access_manager.h
@@ -72,6 +72,10 @@ // Called when camera roll access has changed; use // GetCameraRollAccessStatus() for the new status. virtual void OnCameraRollAccessChanged(); + + // Called when apps access has changed; use + // GetAppsAccessStatus() for the new status. + virtual void OnAppsAccessChanged(); }; MultideviceFeatureAccessManager(MultideviceFeatureAccessManager&) = delete; @@ -118,6 +122,7 @@ void NotifyNotificationAccessChanged(); void NotifyCameraRollAccessChanged(); + void NotifyAppsAccessChanged(); void SetNotificationSetupOperationStatus( NotificationAccessSetupOperation::Status new_status);
diff --git a/ash/components/phonehub/multidevice_feature_access_manager_impl.cc b/ash/components/phonehub/multidevice_feature_access_manager_impl.cc index 22ea4db4..c7d86df 100644 --- a/ash/components/phonehub/multidevice_feature_access_manager_impl.cc +++ b/ash/components/phonehub/multidevice_feature_access_manager_impl.cc
@@ -4,12 +4,14 @@ #include "ash/components/phonehub/multidevice_feature_access_manager_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/connection_scheduler.h" #include "ash/components/phonehub/message_sender.h" #include "ash/components/phonehub/pref_names.h" #include "ash/components/phonehub/util/histogram_util.h" +#include "ash/constants/ash_features.h" #include "ash/webui/eche_app_ui/pref_names.h" -#include "chromeos/components/multidevice/logging/logging.h" +#include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "pref_names.h" @@ -48,10 +50,18 @@ current_feature_status_ = feature_status_provider_->GetStatus(); feature_status_provider_->AddObserver(this); + + pref_change_registrar_.Init(pref_service_); + pref_change_registrar_.Add( + eche_app::prefs::kAppsAccessStatus, + base::BindRepeating( + &MultideviceFeatureAccessManagerImpl::NotifyAppsAccessChanged, + base::Unretained(this))); } MultideviceFeatureAccessManagerImpl::~MultideviceFeatureAccessManagerImpl() { feature_status_provider_->RemoveObserver(this); + pref_change_registrar_.RemoveAll(); } bool MultideviceFeatureAccessManagerImpl::
diff --git a/ash/components/phonehub/multidevice_feature_access_manager_impl.h b/ash/components/phonehub/multidevice_feature_access_manager_impl.h index 0670373..d52e8f1 100644 --- a/ash/components/phonehub/multidevice_feature_access_manager_impl.h +++ b/ash/components/phonehub/multidevice_feature_access_manager_impl.h
@@ -9,6 +9,7 @@ #include "ash/components/phonehub/feature_status_provider.h" #include "ash/components/phonehub/message_receiver.h" +#include "components/prefs/pref_change_registrar.h" class PrefRegistrySimple; class PrefService; @@ -65,6 +66,9 @@ FeatureStatusProvider* feature_status_provider_; MessageSender* message_sender_; ConnectionScheduler* connection_scheduler_; + + // Registers preference value change listeners. + PrefChangeRegistrar pref_change_registrar_; }; } // namespace phonehub
diff --git a/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc b/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc index 5dc97a50..3951777 100644 --- a/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc +++ b/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc
@@ -11,6 +11,8 @@ #include "ash/components/phonehub/fake_message_sender.h" #include "ash/components/phonehub/notification_access_setup_operation.h" #include "ash/components/phonehub/pref_names.h" +#include "ash/webui/eche_app_ui/pref_names.h" +#include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" #include "testing/gtest/include/gtest/gtest.h" @@ -35,6 +37,9 @@ // MultideviceFeatureAccessManager::Observer: void OnCameraRollAccessChanged() override { ++num_calls_; } + // MultideviceFeatureAccessManager::Observer: + void OnAppsAccessChanged() override { ++num_calls_; } + private: size_t num_calls_ = 0; }; @@ -77,6 +82,9 @@ std::make_unique<FakeFeatureStatusProvider>(); fake_message_sender_ = std::make_unique<FakeMessageSender>(); fake_connection_scheduler_ = std::make_unique<FakeConnectionScheduler>(); + pref_service_.registry()->RegisterIntegerPref( + eche_app::prefs::kAppsAccessStatus, + /*default_value=*/0); } void TearDown() override { manager_->RemoveObserver(&fake_observer_); } @@ -98,6 +106,12 @@ manager_->AddObserver(&fake_observer_); } + void InitializeAppsAccessStatus(MultideviceFeatureAccessManager::AccessStatus + apps_access_expected_status) { + pref_service_.SetInteger(eche_app::prefs::kAppsAccessStatus, + static_cast<int>(apps_access_expected_status)); + } + NotificationAccessSetupOperation::Status GetNotificationAccessSetupOperationStatus() { return fake_delegate_.status(); @@ -127,6 +141,13 @@ EXPECT_EQ(expected_status, manager_->GetCameraRollAccessStatus()); } + void VerifyAppsAccessGrantedState( + MultideviceFeatureAccessManager::AccessStatus expected_status) { + EXPECT_EQ(static_cast<int>(expected_status), + pref_service_.GetInteger(eche_app::prefs::kAppsAccessStatus)); + EXPECT_EQ(expected_status, manager_->GetAppsAccessStatus()); + } + bool HasMultideviceFeatureSetupUiBeenDismissed() { return manager_->HasMultideviceFeatureSetupUiBeenDismissed(); } @@ -597,5 +618,28 @@ EXPECT_EQ(1u, GetNumObserverCalls()); } +TEST_F(MultideviceFeatureAccessManagerImplTest, AppsAccessChanged) { + InitializeAccessStatus( + MultideviceFeatureAccessManager::AccessStatus::kAccessGranted, + MultideviceFeatureAccessManager::AccessStatus::kAccessGranted); + InitializeAppsAccessStatus( + MultideviceFeatureAccessManager::AccessStatus::kAccessGranted); + VerifyAppsAccessGrantedState( + MultideviceFeatureAccessManager::AccessStatus::kAccessGranted); + EXPECT_EQ(1u, GetNumObserverCalls()); + + InitializeAppsAccessStatus( + MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted); + VerifyAppsAccessGrantedState( + MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted); + EXPECT_EQ(2u, GetNumObserverCalls()); + + InitializeAppsAccessStatus( + MultideviceFeatureAccessManager::AccessStatus::kProhibited); + VerifyAppsAccessGrantedState( + MultideviceFeatureAccessManager::AccessStatus::kProhibited); + EXPECT_EQ(3u, GetNumObserverCalls()); +} + } // namespace phonehub } // namespace ash
diff --git a/ash/components/phonehub/multidevice_setup_state_updater.cc b/ash/components/phonehub/multidevice_setup_state_updater.cc index 1e063589..3609fddf 100644 --- a/ash/components/phonehub/multidevice_setup_state_updater.cc +++ b/ash/components/phonehub/multidevice_setup_state_updater.cc
@@ -4,11 +4,11 @@ #include "ash/components/phonehub/multidevice_setup_state_updater.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/pref_names.h" #include "ash/components/phonehub/util/histogram_util.h" #include "ash/services/multidevice_setup/public/cpp/prefs.h" #include "base/callback_helpers.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/components/phonehub/notification_manager.cc b/ash/components/phonehub/notification_manager.cc index 67901a0..ef3f62d 100644 --- a/ash/components/phonehub/notification_manager.cc +++ b/ash/components/phonehub/notification_manager.cc
@@ -6,7 +6,7 @@ #include <sstream> -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" namespace ash { namespace phonehub {
diff --git a/ash/components/phonehub/notification_manager_impl.cc b/ash/components/phonehub/notification_manager_impl.cc index d992be44..7b746b59 100644 --- a/ash/components/phonehub/notification_manager_impl.cc +++ b/ash/components/phonehub/notification_manager_impl.cc
@@ -4,11 +4,11 @@ #include "ash/components/phonehub/notification_manager_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/message_sender.h" #include "ash/components/phonehub/notification.h" #include "ash/components/phonehub/user_action_recorder.h" #include "base/containers/flat_set.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace phonehub {
diff --git a/ash/components/phonehub/notification_processor.cc b/ash/components/phonehub/notification_processor.cc index a049952..5549268 100644 --- a/ash/components/phonehub/notification_processor.cc +++ b/ash/components/phonehub/notification_processor.cc
@@ -4,6 +4,7 @@ #include "ash/components/phonehub/notification_processor.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/notification.h" #include "ash/components/phonehub/notification_manager.h" #include "ash/constants/ash_features.h" @@ -11,7 +12,6 @@ #include "base/callback.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "services/data_decoder/public/cpp/decode_image.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/color_palette.h"
diff --git a/ash/components/phonehub/phone_status_model.cc b/ash/components/phonehub/phone_status_model.cc index 3c035a1f..d6235f69 100644 --- a/ash/components/phonehub/phone_status_model.cc +++ b/ash/components/phonehub/phone_status_model.cc
@@ -4,7 +4,7 @@ #include "ash/components/phonehub/phone_status_model.h" -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" namespace ash { namespace phonehub {
diff --git a/ash/components/phonehub/phone_status_processor_unittest.cc b/ash/components/phonehub/phone_status_processor_unittest.cc index 655eb0f3..86fc413 100644 --- a/ash/components/phonehub/phone_status_processor_unittest.cc +++ b/ash/components/phonehub/phone_status_processor_unittest.cc
@@ -9,6 +9,7 @@ #include <string> #include <utility> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/phonehub/fake_do_not_disturb_controller.h" #include "ash/components/phonehub/fake_feature_status_provider.h" #include "ash/components/phonehub/fake_find_my_device_controller.h" @@ -28,7 +29,6 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/image/image.h"
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl.cc b/ash/components/phonehub/recent_apps_interaction_handler_impl.cc index b1cd21db..cd2ec1e 100644 --- a/ash/components/phonehub/recent_apps_interaction_handler_impl.cc +++ b/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
@@ -4,9 +4,10 @@ #include "ash/components/phonehub/recent_apps_interaction_handler_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/notification.h" #include "ash/components/phonehub/pref_names.h" -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/constants/ash_features.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" @@ -182,6 +183,10 @@ ComputeAndUpdateUiState(); } +void RecentAppsInteractionHandlerImpl::OnAppsAccessChanged() { + ComputeAndUpdateUiState(); +} + void RecentAppsInteractionHandlerImpl::ComputeAndUpdateUiState() { ui_state_ = RecentAppsUiState::HIDDEN; @@ -195,7 +200,14 @@ // 3. Otherwise, no recent apps view will be shown. bool allow_streaming = multidevice_setup_client_->GetFeatureState( Feature::kEche) == FeatureState::kEnabledByUser; - if (!allow_streaming) { + + bool is_apps_access_required = + features::IsEchePhoneHubPermissionsOnboarding() && + multidevice_feature_access_manager_->GetAppsAccessStatus() == + phonehub::MultideviceFeatureAccessManager::AccessStatus:: + kAvailableButNotGranted; + + if (!allow_streaming || is_apps_access_required) { NotifyRecentAppsViewUiStateUpdated(); return; }
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl.h b/ash/components/phonehub/recent_apps_interaction_handler_impl.h index eb625eb..bf6e37c 100644 --- a/ash/components/phonehub/recent_apps_interaction_handler_impl.h +++ b/ash/components/phonehub/recent_apps_interaction_handler_impl.h
@@ -56,6 +56,7 @@ // MultideviceFeatureAccessManager::Observer: void OnNotificationAccessChanged() override; + void OnAppsAccessChanged() override; private: FRIEND_TEST_ALL_PREFIXES(RecentAppsInteractionHandlerTest, RecentAppsUpdated);
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc b/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc index 99faf543..0c3038e 100644 --- a/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc +++ b/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
@@ -9,8 +9,10 @@ #include "ash/components/phonehub/fake_multidevice_feature_access_manager.h" #include "ash/components/phonehub/notification.h" #include "ash/components/phonehub/pref_names.h" +#include "ash/constants/ash_features.h" #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" +#include "base/test/scoped_feature_list.h" #include "components/prefs/testing_pref_service.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkColor.h" @@ -58,6 +60,9 @@ // testing::Test: void SetUp() override { + feature_list_.InitWithFeatures( + /*enabled_features=*/{features::kEchePhoneHubPermissionsOnboarding}, + /*disabled_features=*/{}); RecentAppsInteractionHandlerImpl::RegisterPrefs(pref_service_.registry()); fake_multidevice_setup_client_ = std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>(); @@ -152,6 +157,13 @@ MultideviceFeatureAccessManager::AccessProhibitedReason::kUnknown); } + void SetAppsAccessStatus(bool enabled) { + fake_multidevice_feature_access_manager_.SetAppsAccessStatusInternal( + enabled ? MultideviceFeatureAccessManager::AccessStatus::kAccessGranted + : MultideviceFeatureAccessManager::AccessStatus:: + kAvailableButNotGranted); + } + std::vector<RecentAppsInteractionHandler::UserState> GetDefaultUserStates() { RecentAppsInteractionHandler::UserState user_state1; user_state1.user_id = 1; @@ -226,6 +238,7 @@ std::unique_ptr<RecentAppsInteractionHandlerImpl> interaction_handler_; TestingPrefServiceSimple pref_service_; FakeMultideviceFeatureAccessManager fake_multidevice_feature_access_manager_; + base::test::ScopedFeatureList feature_list_; }; TEST_F(RecentAppsInteractionHandlerTest, RecentAppsClicked) { @@ -438,6 +451,7 @@ OnFeatureStatesChangedToEnabledWithEmptyRecentAppsList) { SetEcheFeatureState(FeatureState::kEnabledByUser); SetPhoneHubNotificationsFeatureState(FeatureState::kEnabledByUser); + SetAppsAccessStatus(true); SetNotificationAccess(true); EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::PLACEHOLDER_VIEW, @@ -448,6 +462,7 @@ DisableNotificationAccessWithEmptyRecentAppsList) { SetEcheFeatureState(FeatureState::kEnabledByUser); SetPhoneHubNotificationsFeatureState(FeatureState::kEnabledByUser); + SetAppsAccessStatus(true); SetNotificationAccess(true); EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::PLACEHOLDER_VIEW, @@ -491,7 +506,7 @@ Notification::AppMetadata(app_visible_name1, package_name1, gfx::Image(), /*icon_color=*/absl::nullopt, /*icon_is_monochrome=*/true, expected_user_id1); - + SetAppsAccessStatus(true); handler().NotifyRecentAppAddedOrUpdated(app_metadata1, now); SetEcheFeatureState(FeatureState::kEnabledByUser); @@ -510,6 +525,7 @@ /*icon_color=*/absl::nullopt, /*icon_is_monochrome=*/true, expected_user_id1); + SetAppsAccessStatus(true); handler().NotifyRecentAppAddedOrUpdated(app_metadata1, now); SetEcheFeatureState(FeatureState::kEnabledByUser); @@ -541,6 +557,7 @@ UiStateChangedToVisibleWhenRecentAppBeAdded) { SetEcheFeatureState(FeatureState::kEnabledByUser); SetPhoneHubNotificationsFeatureState(FeatureState::kEnabledByUser); + SetAppsAccessStatus(true); SetNotificationAccess(true); EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::PLACEHOLDER_VIEW, @@ -560,6 +577,45 @@ handler().ui_state()); } +TEST_F(RecentAppsInteractionHandlerTest, DisableAppsAccess) { + GenerateDefaultAppMetadata(); + + // The apps access has not been granted yet so the UI state always HIDDEN. + SetAppsAccessStatus(true); + + SetPhoneHubNotificationsFeatureState(FeatureState::kEnabledByUser); + SetNotificationAccess(true); + + EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::HIDDEN, + handler().ui_state()); + + SetNotificationAccess(false); + + EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::HIDDEN, + handler().ui_state()); + + // Disable notification access permission on the local device. + SetNotificationAccess(true); + SetPhoneHubNotificationsFeatureState(FeatureState::kDisabledByUser); + + EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::HIDDEN, + handler().ui_state()); + + // Disable notification access permission on both devices. + SetNotificationAccess(false); + SetPhoneHubNotificationsFeatureState(FeatureState::kDisabledByUser); + + EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::HIDDEN, + handler().ui_state()); + + // Enable notification access permission back on both devices. + SetNotificationAccess(true); + SetPhoneHubNotificationsFeatureState(FeatureState::kEnabledByUser); + + EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::HIDDEN, + handler().ui_state()); +} + TEST_F(RecentAppsInteractionHandlerTest, PrefBeClearedWhenFeatureStatesChangedToUnavailableNoVerifiedHost) { SaveRecentAppsToPref();
diff --git a/ash/components/phonehub/tether_controller_impl.cc b/ash/components/phonehub/tether_controller_impl.cc index 07231a63..c10f234 100644 --- a/ash/components/phonehub/tether_controller_impl.cc +++ b/ash/components/phonehub/tether_controller_impl.cc
@@ -4,10 +4,10 @@ #include "ash/components/phonehub/tether_controller_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/phone_status_model.h" #include "ash/components/phonehub/user_action_recorder.h" #include "ash/components/phonehub/util/histogram_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/services/network_config/in_process_instance.h" namespace ash {
diff --git a/ash/components/proximity_auth/BUILD.gn b/ash/components/proximity_auth/BUILD.gn index 3e4185c..ece0cc00 100644 --- a/ash/components/proximity_auth/BUILD.gn +++ b/ash/components/proximity_auth/BUILD.gn
@@ -46,6 +46,8 @@ ] deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/components/proximity_auth/public/mojom", "//ash/constants", "//ash/public/cpp", @@ -54,8 +56,6 @@ "//ash/services/secure_channel/public/cpp/client", "//ash/services/secure_channel/public/mojom", "//base", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", "//chromeos/dbus/power", "//chromeos/dbus/session_manager", "//components/account_id", @@ -81,13 +81,13 @@ public_deps = [ ":proximity_auth" ] deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", + "//ash/components/multidevice/logging", "//ash/services/device_sync/proto", "//ash/services/secure_channel:test_support", "//ash/services/secure_channel/public/cpp/client", "//base", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", - "//chromeos/components/multidevice/logging", "//testing/gmock", ] } @@ -108,6 +108,9 @@ deps = [ ":proximity_auth", ":test_support", + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", + "//ash/components/multidevice/logging", "//ash/constants", "//ash/public/cpp", "//ash/services/multidevice_setup/public/cpp:prefs", @@ -116,9 +119,6 @@ "//ash/services/secure_channel/public/cpp/client:test_support", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", - "//chromeos/components/multidevice/logging", "//chromeos/dbus/power", "//chromeos/dbus/power:power_manager_proto", "//components/prefs:test_support",
diff --git a/ash/components/proximity_auth/fake_remote_device_life_cycle.h b/ash/components/proximity_auth/fake_remote_device_life_cycle.h index 0bc2debc..7808a11 100644 --- a/ash/components/proximity_auth/fake_remote_device_life_cycle.h +++ b/ash/components/proximity_auth/fake_remote_device_life_cycle.h
@@ -5,11 +5,11 @@ #ifndef ASH_COMPONENTS_PROXIMITY_AUTH_FAKE_REMOTE_DEVICE_LIFE_CYCLE_H_ #define ASH_COMPONENTS_PROXIMITY_AUTH_FAKE_REMOTE_DEVICE_LIFE_CYCLE_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/proximity_auth/remote_device_life_cycle.h" // TODO(https://crbug.com/1164001): move to forward declaration. #include "ash/services/secure_channel/public/cpp/client/client_channel.h" #include "base/observer_list.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace proximity_auth {
diff --git a/ash/components/proximity_auth/messenger_impl.cc b/ash/components/proximity_auth/messenger_impl.cc index 4b44eb9..d62ad4b 100644 --- a/ash/components/proximity_auth/messenger_impl.cc +++ b/ash/components/proximity_auth/messenger_impl.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/proximity_auth/messenger_observer.h" #include "ash/components/proximity_auth/remote_status_update.h" #include "base/base64url.h" @@ -16,7 +17,6 @@ #include "base/location.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace proximity_auth {
diff --git a/ash/components/proximity_auth/messenger_impl_unittest.cc b/ash/components/proximity_auth/messenger_impl_unittest.cc index 4f9439a..afb6c95 100644 --- a/ash/components/proximity_auth/messenger_impl_unittest.cc +++ b/ash/components/proximity_auth/messenger_impl_unittest.cc
@@ -6,13 +6,13 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/proximity_auth/messenger_observer.h" #include "ash/components/proximity_auth/remote_status_update.h" #include "ash/services/secure_channel/public/cpp/client/fake_client_channel.h" #include "base/callback.h" #include "base/test/scoped_feature_list.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc b/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc index da82de59..0454e7b 100644 --- a/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc +++ b/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
@@ -7,11 +7,11 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/proximity_auth/proximity_auth_pref_names.h" #include "ash/services/multidevice_setup/public/cpp/prefs.h" #include "base/logging.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h"
diff --git a/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc b/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc index 14e0b12..9472cd8a 100644 --- a/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc +++ b/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
@@ -7,11 +7,11 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/proximity_auth/proximity_auth_pref_names.h" #include "ash/services/multidevice_setup/public/cpp/prefs.h" #include "base/bind.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h"
diff --git a/ash/components/proximity_auth/proximity_auth_system.cc b/ash/components/proximity_auth/proximity_auth_system.cc index d6051a64..53e7b81 100644 --- a/ash/components/proximity_auth/proximity_auth_system.cc +++ b/ash/components/proximity_auth/proximity_auth_system.cc
@@ -4,11 +4,11 @@ #include "ash/components/proximity_auth/proximity_auth_system.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/proximity_auth/proximity_auth_client.h" #include "ash/components/proximity_auth/remote_device_life_cycle_impl.h" #include "ash/components/proximity_auth/unlock_manager_impl.h" #include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace proximity_auth {
diff --git a/ash/components/proximity_auth/proximity_auth_system.h b/ash/components/proximity_auth/proximity_auth_system.h index 4c1d8b5b..ef2f559 100644 --- a/ash/components/proximity_auth/proximity_auth_system.h +++ b/ash/components/proximity_auth/proximity_auth_system.h
@@ -8,8 +8,8 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/proximity_auth/screenlock_bridge.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "components/account_id/account_id.h" namespace ash {
diff --git a/ash/components/proximity_auth/proximity_auth_system_unittest.cc b/ash/components/proximity_auth/proximity_auth_system_unittest.cc index 0a96a80..4344099 100644 --- a/ash/components/proximity_auth/proximity_auth_system_unittest.cc +++ b/ash/components/proximity_auth/proximity_auth_system_unittest.cc
@@ -6,6 +6,10 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/components/proximity_auth/fake_lock_handler.h" #include "ash/components/proximity_auth/fake_remote_device_life_cycle.h" #include "ash/components/proximity_auth/mock_proximity_auth_client.h" @@ -17,10 +21,6 @@ #include "base/test/scoped_feature_list.h" #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/proximity_auth/proximity_monitor_impl.cc b/ash/components/proximity_auth/proximity_monitor_impl.cc index 824e717d..e2ba09c 100644 --- a/ash/components/proximity_auth/proximity_monitor_impl.cc +++ b/ash/components/proximity_auth/proximity_monitor_impl.cc
@@ -9,12 +9,12 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/proximity_auth/metrics.h" #include "ash/services/secure_channel/public/cpp/client/client_channel.h" #include "base/bind.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter_factory.h"
diff --git a/ash/components/proximity_auth/proximity_monitor_impl.h b/ash/components/proximity_auth/proximity_monitor_impl.h index 4ca05fc7..026f5d19 100644 --- a/ash/components/proximity_auth/proximity_monitor_impl.h +++ b/ash/components/proximity_auth/proximity_monitor_impl.h
@@ -7,10 +7,10 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/proximity_auth/proximity_monitor.h" #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "device/bluetooth/bluetooth_device.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/proximity_auth/proximity_monitor_impl_unittest.cc b/ash/components/proximity_auth/proximity_monitor_impl_unittest.cc index cb230b8..16b1fe0 100644 --- a/ash/components/proximity_auth/proximity_monitor_impl_unittest.cc +++ b/ash/components/proximity_auth/proximity_monitor_impl_unittest.cc
@@ -7,6 +7,10 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/components/proximity_auth/proximity_monitor_observer.h" #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" #include "ash/services/secure_channel/fake_connection.h" @@ -20,10 +24,6 @@ #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/ash/components/proximity_auth/remote_device_life_cycle.h b/ash/components/proximity_auth/remote_device_life_cycle.h index 5401c37..b0f97af 100644 --- a/ash/components/proximity_auth/remote_device_life_cycle.h +++ b/ash/components/proximity_auth/remote_device_life_cycle.h
@@ -7,7 +7,7 @@ #include <ostream> -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" namespace ash { namespace secure_channel {
diff --git a/ash/components/proximity_auth/remote_device_life_cycle_impl.cc b/ash/components/proximity_auth/remote_device_life_cycle_impl.cc index 4ac5f147..d439789a 100644 --- a/ash/components/proximity_auth/remote_device_life_cycle_impl.cc +++ b/ash/components/proximity_auth/remote_device_life_cycle_impl.cc
@@ -6,12 +6,12 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/proximity_auth/messenger_impl.h" #include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h" #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h" #include "base/bind.h" #include "base/threading/thread_task_runner_handle.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace proximity_auth {
diff --git a/ash/components/proximity_auth/remote_device_life_cycle_impl.h b/ash/components/proximity_auth/remote_device_life_cycle_impl.h index 767a491..8be6370 100644 --- a/ash/components/proximity_auth/remote_device_life_cycle_impl.h +++ b/ash/components/proximity_auth/remote_device_life_cycle_impl.h
@@ -7,6 +7,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/proximity_auth/messenger_observer.h" #include "ash/components/proximity_auth/remote_device_life_cycle.h" #include "ash/services/secure_channel/public/cpp/client/connection_attempt.h" @@ -15,7 +16,6 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash { namespace secure_channel {
diff --git a/ash/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc b/ash/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc index 2ccd652c..40e6691 100644 --- a/ash/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc +++ b/ash/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc
@@ -9,6 +9,8 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/proximity_auth/messenger.h" #include "ash/services/secure_channel/public/cpp/client/fake_client_channel.h" #include "ash/services/secure_channel/public/cpp/client/fake_connection_attempt.h" @@ -17,8 +19,6 @@ #include "base/test/scoped_feature_list.h" #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/proximity_auth/remote_status_update.cc b/ash/components/proximity_auth/remote_status_update.cc index 628397e..3bee9b5 100644 --- a/ash/components/proximity_auth/remote_status_update.cc +++ b/ash/components/proximity_auth/remote_status_update.cc
@@ -4,8 +4,8 @@ #include "ash/components/proximity_auth/remote_status_update.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace {
diff --git a/ash/components/proximity_auth/screenlock_bridge.cc b/ash/components/proximity_auth/screenlock_bridge.cc index 051241a..ecfbf87 100644 --- a/ash/components/proximity_auth/screenlock_bridge.cc +++ b/ash/components/proximity_auth/screenlock_bridge.cc
@@ -9,8 +9,8 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "build/build_config.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/dbus/session_manager/session_manager_client.h" namespace proximity_auth {
diff --git a/ash/components/proximity_auth/unlock_manager_impl.cc b/ash/components/proximity_auth/unlock_manager_impl.cc index 924c38a..388988bb 100644 --- a/ash/components/proximity_auth/unlock_manager_impl.cc +++ b/ash/components/proximity_auth/unlock_manager_impl.cc
@@ -6,6 +6,8 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/proximity_auth/messenger.h" #include "ash/components/proximity_auth/metrics.h" #include "ash/components/proximity_auth/proximity_auth_client.h" @@ -18,8 +20,6 @@ #include "base/time/default_clock.h" #include "base/time/time.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "device/bluetooth/bluetooth_adapter_factory.h" namespace proximity_auth {
diff --git a/ash/components/proximity_auth/unlock_manager_impl_unittest.cc b/ash/components/proximity_auth/unlock_manager_impl_unittest.cc index c80a5ab..277811d 100644 --- a/ash/components/proximity_auth/unlock_manager_impl_unittest.cc +++ b/ash/components/proximity_auth/unlock_manager_impl_unittest.cc
@@ -7,6 +7,8 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/proximity_auth/fake_lock_handler.h" #include "ash/components/proximity_auth/fake_remote_device_life_cycle.h" #include "ash/components/proximity_auth/messenger.h" @@ -23,8 +25,6 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/timer/mock_timer.h" #include "build/build_config.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power/power_manager_client.h" #include "chromeos/dbus/power_manager/suspend.pb.h"
diff --git a/ash/components/tether/BUILD.gn b/ash/components/tether/BUILD.gn index 6d1c7bb..25616c9b 100644 --- a/ash/components/tether/BUILD.gn +++ b/ash/components/tether/BUILD.gn
@@ -117,6 +117,7 @@ ] deps = [ + "//ash/components/multidevice/logging", "//ash/components/tether/proto", "//ash/constants", "//ash/services/device_sync/public/cpp", @@ -125,7 +126,6 @@ "//ash/services/secure_channel/public/cpp/shared", "//ash/services/secure_channel/public/mojom", "//base", - "//chromeos/components/multidevice/logging", "//chromeos/dbus/power", "//chromeos/login/login_state", "//chromeos/network", @@ -200,14 +200,14 @@ public_deps = [ ":tether" ] deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", "//ash/components/tether/proto", "//ash/services/device_sync/public/cpp:test_support", "//ash/services/secure_channel/public/cpp/client:test_support", "//ash/services/secure_channel/public/cpp/shared", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", "//chromeos/network:test_support", "//device/bluetooth", "//testing/gmock", @@ -262,6 +262,8 @@ deps = [ ":test_support", ":tether", + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", "//ash/components/tether/proto", "//ash/services/device_sync:test_support", "//ash/services/device_sync/public/cpp", @@ -273,8 +275,6 @@ "//ash/services/secure_channel/public/cpp/client:test_support", "//ash/services/secure_channel/public/cpp/shared", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", "//chromeos/dbus/power", "//chromeos/login/login_state", "//chromeos/network:test_support",
diff --git a/ash/components/tether/active_host.cc b/ash/components/tether/active_host.cc index 9137bb49..50fd3efbb 100644 --- a/ash/components/tether/active_host.cc +++ b/ash/components/tether/active_host.cc
@@ -4,13 +4,13 @@ #include "ash/components/tether/active_host.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/pref_names.h" #include "ash/components/tether/tether_host_fetcher.h" #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/components/tether/active_host.h b/ash/components/tether/active_host.h index 1fc436973..29849457 100644 --- a/ash/components/tether/active_host.h +++ b/ash/components/tether/active_host.h
@@ -7,10 +7,10 @@ #include <string> +#include "ash/components/multidevice/remote_device_ref.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" class PrefRegistrySimple;
diff --git a/ash/components/tether/active_host_network_state_updater.cc b/ash/components/tether/active_host_network_state_updater.cc index 7f676d1..574708de 100644 --- a/ash/components/tether/active_host_network_state_updater.cc +++ b/ash/components/tether/active_host_network_state_updater.cc
@@ -4,10 +4,10 @@ #include "ash/components/tether/active_host_network_state_updater.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/active_host.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "chromeos/network/network_state_handler.h" namespace ash {
diff --git a/ash/components/tether/active_host_unittest.cc b/ash/components/tether/active_host_unittest.cc index eef9239..0602392 100644 --- a/ash/components/tether/active_host_unittest.cc +++ b/ash/components/tether/active_host_unittest.cc
@@ -6,11 +6,11 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/fake_tether_host_fetcher.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/tether/asynchronous_shutdown_object_container_impl_unittest.cc b/ash/components/tether/asynchronous_shutdown_object_container_impl_unittest.cc index 46468049..bf6cd9f7 100644 --- a/ash/components/tether/asynchronous_shutdown_object_container_impl_unittest.cc +++ b/ash/components/tether/asynchronous_shutdown_object_container_impl_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/fake_disconnect_tethering_request_sender.h" #include "ash/components/tether/fake_tether_host_fetcher.h" #include "ash/components/tether/tether_component_impl.h" @@ -16,7 +17,6 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/connect_tethering_operation.cc b/ash/components/tether/connect_tethering_operation.cc index 6aa17c7..d447ff4 100644 --- a/ash/components/tether/connect_tethering_operation.cc +++ b/ash/components/tether/connect_tethering_operation.cc
@@ -4,13 +4,13 @@ #include "ash/components/tether/connect_tethering_operation.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/message_wrapper.h" #include "ash/components/tether/proto/tether.pb.h" #include "ash/components/tether/tether_host_response_recorder.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/time/default_clock.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/connect_tethering_operation.h b/ash/components/tether/connect_tethering_operation.h index ab3069cd..15c4835 100644 --- a/ash/components/tether/connect_tethering_operation.h +++ b/ash/components/tether/connect_tethering_operation.h
@@ -10,6 +10,7 @@ #include <map> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/message_transfer_operation.h" // TODO(https://crbug.com/1164001): move to forward declaration #include "ash/services/device_sync/public/cpp/device_sync_client.h" @@ -19,7 +20,6 @@ #include "base/observer_list.h" #include "base/time/clock.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/components/tether/connect_tethering_operation_unittest.cc b/ash/components/tether/connect_tethering_operation_unittest.cc index e54c3612..ff5523ec 100644 --- a/ash/components/tether/connect_tethering_operation_unittest.cc +++ b/ash/components/tether/connect_tethering_operation_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/message_wrapper.h" #include "ash/components/tether/mock_tether_host_response_recorder.h" #include "ash/components/tether/proto/tether.pb.h" @@ -22,7 +23,6 @@ #include "base/test/task_environment.h" #include "base/time/time.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/tether/connection_preserver_impl.cc b/ash/components/tether/connection_preserver_impl.cc index f3e4841..804acf4e 100644 --- a/ash/components/tether/connection_preserver_impl.cc +++ b/ash/components/tether/connection_preserver_impl.cc
@@ -4,11 +4,11 @@ #include "ash/components/tether/connection_preserver_impl.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/tether_host_response_recorder.h" #include "base/bind.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" #include "chromeos/network/network_type_pattern.h"
diff --git a/ash/components/tether/connection_preserver_impl_unittest.cc b/ash/components/tether/connection_preserver_impl_unittest.cc index b600ef0..e0f8f20 100644 --- a/ash/components/tether/connection_preserver_impl_unittest.cc +++ b/ash/components/tether/connection_preserver_impl_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/fake_active_host.h" #include "ash/components/tether/mock_tether_host_response_recorder.h" #include "ash/components/tether/timer_factory.h" @@ -18,7 +19,6 @@ #include "base/memory/ptr_util.h" #include "base/test/task_environment.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" #include "chromeos/network/network_state_test_helper.h"
diff --git a/ash/components/tether/crash_recovery_manager_impl.cc b/ash/components/tether/crash_recovery_manager_impl.cc index 2a5b725..5e75c5a7 100644 --- a/ash/components/tether/crash_recovery_manager_impl.cc +++ b/ash/components/tether/crash_recovery_manager_impl.cc
@@ -6,12 +6,12 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/host_scan_cache.h" #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/crash_recovery_manager_impl_unittest.cc b/ash/components/tether/crash_recovery_manager_impl_unittest.cc index 974d59c9..795d8c8 100644 --- a/ash/components/tether/crash_recovery_manager_impl_unittest.cc +++ b/ash/components/tether/crash_recovery_manager_impl_unittest.cc
@@ -7,13 +7,13 @@ #include <memory> #include <sstream> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/device_id_tether_network_guid_map.h" #include "ash/components/tether/fake_active_host.h" #include "ash/components/tether/fake_host_scan_cache.h" #include "ash/components/tether/host_scan_cache_entry.h" #include "base/bind.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "chromeos/network/network_state_handler.h" #include "chromeos/network/network_state_test_helper.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/device_status_util_unittest.cc b/ash/components/tether/device_status_util_unittest.cc index b05932c..b7b86c7 100644 --- a/ash/components/tether/device_status_util_unittest.cc +++ b/ash/components/tether/device_status_util_unittest.cc
@@ -6,12 +6,12 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/fake_tether_host_fetcher.h" #include "ash/components/tether/proto_test_util.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/prefs/testing_pref_service.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/disconnect_tethering_operation.cc b/ash/components/tether/disconnect_tethering_operation.cc index 93930b9..31b2389 100644 --- a/ash/components/tether/disconnect_tethering_operation.cc +++ b/ash/components/tether/disconnect_tethering_operation.cc
@@ -6,12 +6,12 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/message_wrapper.h" #include "ash/components/tether/proto/tether.pb.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/time/default_clock.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/disconnect_tethering_operation_unittest.cc b/ash/components/tether/disconnect_tethering_operation_unittest.cc index 506b49a..86aebc3 100644 --- a/ash/components/tether/disconnect_tethering_operation_unittest.cc +++ b/ash/components/tether/disconnect_tethering_operation_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/message_wrapper.h" #include "ash/components/tether/proto/tether.pb.h" #include "ash/components/tether/test_timer_factory.h" @@ -20,7 +21,6 @@ #include "base/test/simple_test_clock.h" #include "base/time/time.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/disconnect_tethering_request_sender_impl.cc b/ash/components/tether/disconnect_tethering_request_sender_impl.cc index 38c2100..864d305 100644 --- a/ash/components/tether/disconnect_tethering_request_sender_impl.cc +++ b/ash/components/tether/disconnect_tethering_request_sender_impl.cc
@@ -6,11 +6,11 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/tether_host_fetcher.h" #include "base/bind.h" #include "base/containers/contains.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/disconnect_tethering_request_sender_impl_unittest.cc b/ash/components/tether/disconnect_tethering_request_sender_impl_unittest.cc index 584844f..e43f2f7 100644 --- a/ash/components/tether/disconnect_tethering_request_sender_impl_unittest.cc +++ b/ash/components/tether/disconnect_tethering_request_sender_impl_unittest.cc
@@ -6,6 +6,8 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/disconnect_tethering_operation.h" #include "ash/components/tether/disconnect_tethering_request_sender.h" #include "ash/components/tether/fake_tether_host_fetcher.h" @@ -13,8 +15,6 @@ #include "ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h" #include "base/memory/ptr_util.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash {
diff --git a/ash/components/tether/fake_active_host.cc b/ash/components/tether/fake_active_host.cc index 65c06811..f75740d 100644 --- a/ash/components/tether/fake_active_host.cc +++ b/ash/components/tether/fake_active_host.cc
@@ -6,10 +6,10 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "base/base64.h" #include "base/bind.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/tether/fake_tether_host_fetcher.h b/ash/components/tether/fake_tether_host_fetcher.h index 1328e22..198dd92 100644 --- a/ash/components/tether/fake_tether_host_fetcher.h +++ b/ash/components/tether/fake_tether_host_fetcher.h
@@ -7,8 +7,8 @@ #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/tether_host_fetcher.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/components/tether/gms_core_notifications_state_tracker_impl.cc b/ash/components/tether/gms_core_notifications_state_tracker_impl.cc index 4aa8b7d..459c3b8 100644 --- a/ash/components/tether/gms_core_notifications_state_tracker_impl.cc +++ b/ash/components/tether/gms_core_notifications_state_tracker_impl.cc
@@ -6,7 +6,7 @@ #include <sstream> -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/gms_core_notifications_state_tracker_impl.h b/ash/components/tether/gms_core_notifications_state_tracker_impl.h index 024c8d3..4a13fa96 100644 --- a/ash/components/tether/gms_core_notifications_state_tracker_impl.h +++ b/ash/components/tether/gms_core_notifications_state_tracker_impl.h
@@ -9,9 +9,9 @@ #include <string> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/gms_core_notifications_state_tracker.h" #include "ash/components/tether/host_scanner_operation.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/components/tether/gms_core_notifications_state_tracker_impl_unittest.cc b/ash/components/tether/gms_core_notifications_state_tracker_impl_unittest.cc index 9eb80201..ed32dfd 100644 --- a/ash/components/tether/gms_core_notifications_state_tracker_impl_unittest.cc +++ b/ash/components/tether/gms_core_notifications_state_tracker_impl_unittest.cc
@@ -9,8 +9,8 @@ #include <string> #include <vector> -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/host_connection_metrics_logger_unittest.cc b/ash/components/tether/host_connection_metrics_logger_unittest.cc index 34bb783..2187974 100644 --- a/ash/components/tether/host_connection_metrics_logger_unittest.cc +++ b/ash/components/tether/host_connection_metrics_logger_unittest.cc
@@ -6,11 +6,11 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/fake_active_host.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/simple_test_clock.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash {
diff --git a/ash/components/tether/host_scan_device_prioritizer.h b/ash/components/tether/host_scan_device_prioritizer.h index 7525eee..72897332 100644 --- a/ash/components/tether/host_scan_device_prioritizer.h +++ b/ash/components/tether/host_scan_device_prioritizer.h
@@ -5,7 +5,7 @@ #ifndef ASH_COMPONENTS_TETHER_HOST_SCAN_DEVICE_PRIORITIZER_H_ #define ASH_COMPONENTS_TETHER_HOST_SCAN_DEVICE_PRIORITIZER_H_ -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/components/tether/host_scan_device_prioritizer_impl.h b/ash/components/tether/host_scan_device_prioritizer_impl.h index b3c39fa..7101547 100644 --- a/ash/components/tether/host_scan_device_prioritizer_impl.h +++ b/ash/components/tether/host_scan_device_prioritizer_impl.h
@@ -5,8 +5,8 @@ #ifndef ASH_COMPONENTS_TETHER_HOST_SCAN_DEVICE_PRIORITIZER_IMPL_H_ #define ASH_COMPONENTS_TETHER_HOST_SCAN_DEVICE_PRIORITIZER_IMPL_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/host_scan_device_prioritizer.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/components/tether/host_scan_device_prioritizer_impl_unittest.cc b/ash/components/tether/host_scan_device_prioritizer_impl_unittest.cc index e0b72305..a74e93d6 100644 --- a/ash/components/tether/host_scan_device_prioritizer_impl_unittest.cc +++ b/ash/components/tether/host_scan_device_prioritizer_impl_unittest.cc
@@ -6,9 +6,9 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/device_id_tether_network_guid_map.h" #include "ash/components/tether/tether_host_response_recorder.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/host_scan_scheduler_impl.cc b/ash/components/tether/host_scan_scheduler_impl.cc index c4d3acae..54c4cd1 100644 --- a/ash/components/tether/host_scan_scheduler_impl.cc +++ b/ash/components/tether/host_scan_scheduler_impl.cc
@@ -6,13 +6,13 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "ash/constants/ash_switches.h" #include "base/bind.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/default_clock.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/network_handler.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/host_scanner_impl.cc b/ash/components/tether/host_scanner_impl.cc index 81fb2a0..e738839 100644 --- a/ash/components/tether/host_scanner_impl.cc +++ b/ash/components/tether/host_scanner_impl.cc
@@ -6,6 +6,7 @@ #include <algorithm> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/connection_preserver.h" #include "ash/components/tether/device_id_tether_network_guid_map.h" #include "ash/components/tether/device_status_util.h" @@ -16,7 +17,6 @@ #include "ash/constants/ash_switches.h" #include "base/bind.h" #include "base/metrics/histogram_macros.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/network_state.h" #include "components/session_manager/core/session_manager.h"
diff --git a/ash/components/tether/host_scanner_impl.h b/ash/components/tether/host_scanner_impl.h index 4810e718..0313768 100644 --- a/ash/components/tether/host_scanner_impl.h +++ b/ash/components/tether/host_scanner_impl.h
@@ -9,6 +9,7 @@ #include <unordered_set> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/host_scanner.h" #include "ash/components/tether/host_scanner_operation.h" #include "ash/components/tether/notification_presenter.h" @@ -20,7 +21,6 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/time/clock.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "chromeos/network/network_state_handler.h" #include "components/session_manager/core/session_manager_observer.h"
diff --git a/ash/components/tether/host_scanner_impl_unittest.cc b/ash/components/tether/host_scanner_impl_unittest.cc index 3044129..78ae6b6 100644 --- a/ash/components/tether/host_scanner_impl_unittest.cc +++ b/ash/components/tether/host_scanner_impl_unittest.cc
@@ -8,6 +8,7 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/device_id_tether_network_guid_map.h" #include "ash/components/tether/fake_connection_preserver.h" #include "ash/components/tether/fake_host_scan_cache.h" @@ -27,7 +28,6 @@ #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "chromeos/network/network_state_test_helper.h" #include "components/session_manager/core/session_manager.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/host_scanner_operation.cc b/ash/components/tether/host_scanner_operation.cc index 9ff716e5..4f2d143b 100644 --- a/ash/components/tether/host_scanner_operation.cc +++ b/ash/components/tether/host_scanner_operation.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/connection_preserver.h" #include "ash/components/tether/host_scan_device_prioritizer.h" #include "ash/components/tether/message_wrapper.h" @@ -16,7 +17,6 @@ #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/time/default_clock.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/host_scanner_operation.h b/ash/components/tether/host_scanner_operation.h index 2c41206..890bc0a 100644 --- a/ash/components/tether/host_scanner_operation.h +++ b/ash/components/tether/host_scanner_operation.h
@@ -8,6 +8,7 @@ #include <map> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/message_transfer_operation.h" // TODO(https://crbug.com/1164001): move to forward declaration #include "ash/services/device_sync/public/cpp/device_sync_client.h" @@ -16,7 +17,6 @@ #include "base/gtest_prod_util.h" #include "base/observer_list.h" #include "base/time/clock.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/components/tether/host_scanner_operation_unittest.cc b/ash/components/tether/host_scanner_operation_unittest.cc index b7ab56b..409478d 100644 --- a/ash/components/tether/host_scanner_operation_unittest.cc +++ b/ash/components/tether/host_scanner_operation_unittest.cc
@@ -8,6 +8,7 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/fake_connection_preserver.h" #include "ash/components/tether/host_scan_device_prioritizer.h" #include "ash/components/tether/message_wrapper.h" @@ -23,7 +24,6 @@ #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" #include "base/test/test_simple_task_runner.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" using testing::_;
diff --git a/ash/components/tether/hotspot_usage_duration_tracker.cc b/ash/components/tether/hotspot_usage_duration_tracker.cc index c77bc17..d9e93160 100644 --- a/ash/components/tether/hotspot_usage_duration_tracker.cc +++ b/ash/components/tether/hotspot_usage_duration_tracker.cc
@@ -4,9 +4,9 @@ #include "ash/components/tether/hotspot_usage_duration_tracker.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/metrics/histogram_macros.h" #include "base/time/clock.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/keep_alive_operation.cc b/ash/components/tether/keep_alive_operation.cc index d6f56db2..bf2b89c 100644 --- a/ash/components/tether/keep_alive_operation.cc +++ b/ash/components/tether/keep_alive_operation.cc
@@ -4,12 +4,12 @@ #include "ash/components/tether/keep_alive_operation.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/message_wrapper.h" #include "ash/components/tether/proto/tether.pb.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/time/default_clock.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/keep_alive_operation_unittest.cc b/ash/components/tether/keep_alive_operation_unittest.cc index 798868a..3939c28 100644 --- a/ash/components/tether/keep_alive_operation_unittest.cc +++ b/ash/components/tether/keep_alive_operation_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/message_wrapper.h" #include "ash/components/tether/proto_test_util.h" #include "ash/components/tether/test_timer_factory.h" @@ -17,7 +18,6 @@ #include "base/memory/ptr_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/simple_test_clock.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/tether/keep_alive_scheduler_unittest.cc b/ash/components/tether/keep_alive_scheduler_unittest.cc index fdf3f47b..e3a60650 100644 --- a/ash/components/tether/keep_alive_scheduler_unittest.cc +++ b/ash/components/tether/keep_alive_scheduler_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/device_id_tether_network_guid_map.h" #include "ash/components/tether/fake_active_host.h" #include "ash/components/tether/fake_host_scan_cache.h" @@ -15,7 +16,6 @@ #include "ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h" #include "base/memory/ptr_util.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash {
diff --git a/ash/components/tether/message_transfer_operation.cc b/ash/components/tether/message_transfer_operation.cc index cb1f966..a0d89a1 100644 --- a/ash/components/tether/message_transfer_operation.cc +++ b/ash/components/tether/message_transfer_operation.cc
@@ -7,11 +7,11 @@ #include <memory> #include <set> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/message_wrapper.h" #include "ash/components/tether/timer_factory.h" #include "base/bind.h" #include "base/containers/contains.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/message_transfer_operation_unittest.cc b/ash/components/tether/message_transfer_operation_unittest.cc index a974aa4..0e9df3e 100644 --- a/ash/components/tether/message_transfer_operation_unittest.cc +++ b/ash/components/tether/message_transfer_operation_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/message_wrapper.h" #include "ash/components/tether/proto_test_util.h" #include "ash/components/tether/test_timer_factory.h" @@ -15,7 +16,6 @@ #include "ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h" #include "base/memory/ptr_util.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/mock_tether_host_response_recorder.h b/ash/components/tether/mock_tether_host_response_recorder.h index b64114d..452db1d 100644 --- a/ash/components/tether/mock_tether_host_response_recorder.h +++ b/ash/components/tether/mock_tether_host_response_recorder.h
@@ -7,8 +7,8 @@ #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/tether_host_response_recorder.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "testing/gmock/include/gmock/gmock.h" namespace ash {
diff --git a/ash/components/tether/network_configuration_remover.cc b/ash/components/tether/network_configuration_remover.cc index 58af15fb..34f8c10 100644 --- a/ash/components/tether/network_configuration_remover.cc +++ b/ash/components/tether/network_configuration_remover.cc
@@ -4,8 +4,8 @@ #include "ash/components/tether/network_configuration_remover.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/managed_network_configuration_handler.h" namespace {
diff --git a/ash/components/tether/network_connection_handler_tether_delegate.cc b/ash/components/tether/network_connection_handler_tether_delegate.cc index 7ca8680..2cd83f9 100644 --- a/ash/components/tether/network_connection_handler_tether_delegate.cc +++ b/ash/components/tether/network_connection_handler_tether_delegate.cc
@@ -4,13 +4,13 @@ #include "ash/components/tether/network_connection_handler_tether_delegate.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/active_host.h" #include "ash/components/tether/tether_connector.h" #include "ash/components/tether/tether_disconnector.h" #include "base/bind.h" #include "base/callback_helpers.h" #include "base/containers/contains.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/network_host_scan_cache.cc b/ash/components/tether/network_host_scan_cache.cc index 6061ec8..ae2b918 100644 --- a/ash/components/tether/network_host_scan_cache.cc +++ b/ash/components/tether/network_host_scan_cache.cc
@@ -4,10 +4,10 @@ #include "ash/components/tether/network_host_scan_cache.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/device_id_tether_network_guid_map.h" #include "ash/components/tether/tether_host_response_recorder.h" #include "base/containers/contains.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/network_list_sorter.h b/ash/components/tether/network_list_sorter.h index d105e39..ccd53beb 100644 --- a/ash/components/tether/network_list_sorter.h +++ b/ash/components/tether/network_list_sorter.h
@@ -5,7 +5,7 @@ #ifndef ASH_COMPONENTS_TETHER_NETWORK_LIST_SORTER_H_ #define ASH_COMPONENTS_TETHER_NETWORK_LIST_SORTER_H_ -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "chromeos/network/network_state_handler.h" namespace ash {
diff --git a/ash/components/tether/notification_presenter.h b/ash/components/tether/notification_presenter.h index 3421862..cf8c86ca 100644 --- a/ash/components/tether/notification_presenter.h +++ b/ash/components/tether/notification_presenter.h
@@ -5,7 +5,7 @@ #ifndef ASH_COMPONENTS_TETHER_NOTIFICATION_PRESENTER_H_ #define ASH_COMPONENTS_TETHER_NOTIFICATION_PRESENTER_H_ -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "chromeos/network/network_state.h" namespace ash {
diff --git a/ash/components/tether/notification_remover_unittest.cc b/ash/components/tether/notification_remover_unittest.cc index e47742d..54016bf 100644 --- a/ash/components/tether/notification_remover_unittest.cc +++ b/ash/components/tether/notification_remover_unittest.cc
@@ -6,13 +6,13 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/fake_active_host.h" #include "ash/components/tether/fake_host_scan_cache.h" #include "ash/components/tether/fake_notification_presenter.h" #include "ash/components/tether/host_scan_test_util.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "chromeos/network/network_state_test_helper.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
diff --git a/ash/components/tether/tether_component_impl.cc b/ash/components/tether/tether_component_impl.cc index c8418bd..b2cd76c 100644 --- a/ash/components/tether/tether_component_impl.cc +++ b/ash/components/tether/tether_component_impl.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/active_host.h" #include "ash/components/tether/asynchronous_shutdown_object_container_impl.h" #include "ash/components/tether/crash_recovery_manager_impl.h" @@ -21,7 +22,6 @@ #include "base/bind.h" #include "base/callback_helpers.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/pref_registry/pref_registry_syncable.h" namespace ash {
diff --git a/ash/components/tether/tether_connector_impl.cc b/ash/components/tether/tether_connector_impl.cc index 084971f..96f23ba4 100644 --- a/ash/components/tether/tether_connector_impl.cc +++ b/ash/components/tether/tether_connector_impl.cc
@@ -4,6 +4,7 @@ #include "ash/components/tether/tether_connector_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/active_host.h" #include "ash/components/tether/device_id_tether_network_guid_map.h" #include "ash/components/tether/disconnect_tethering_request_sender.h" @@ -16,7 +17,6 @@ #include "base/bind.h" #include "base/callback_helpers.h" #include "base/metrics/histogram_macros.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/network_handler.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/tether_connector_impl_unittest.cc b/ash/components/tether/tether_connector_impl_unittest.cc index 869d3518..d162f4b 100644 --- a/ash/components/tether/tether_connector_impl_unittest.cc +++ b/ash/components/tether/tether_connector_impl_unittest.cc
@@ -6,6 +6,8 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/connect_tethering_operation.h" #include "ash/components/tether/device_id_tether_network_guid_map.h" #include "ash/components/tether/fake_active_host.h" @@ -24,8 +26,6 @@ #include "base/memory/ptr_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "chromeos/network/network_connection_handler.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/tether_disconnector_impl.cc b/ash/components/tether/tether_disconnector_impl.cc index 2e002701..c48a311 100644 --- a/ash/components/tether/tether_disconnector_impl.cc +++ b/ash/components/tether/tether_disconnector_impl.cc
@@ -4,6 +4,7 @@ #include "ash/components/tether/tether_disconnector_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/active_host.h" #include "ash/components/tether/device_id_tether_network_guid_map.h" #include "ash/components/tether/disconnect_tethering_request_sender.h" @@ -12,7 +13,6 @@ #include "ash/components/tether/wifi_hotspot_disconnector.h" #include "base/metrics/histogram_macros.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/network_connection_handler.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/tether_disconnector_impl_unittest.cc b/ash/components/tether/tether_disconnector_impl_unittest.cc index f942d8bf..8e9e5230 100644 --- a/ash/components/tether/tether_disconnector_impl_unittest.cc +++ b/ash/components/tether/tether_disconnector_impl_unittest.cc
@@ -6,6 +6,8 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/tether/device_id_tether_network_guid_map.h" #include "ash/components/tether/fake_active_host.h" #include "ash/components/tether/fake_disconnect_tethering_request_sender.h" @@ -14,8 +16,6 @@ #include "ash/components/tether/fake_wifi_hotspot_disconnector.h" #include "ash/components/tether/tether_session_completion_logger.h" #include "base/bind.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash {
diff --git a/ash/components/tether/tether_host_fetcher.cc b/ash/components/tether/tether_host_fetcher.cc index e98af797..7ead01c 100644 --- a/ash/components/tether/tether_host_fetcher.cc +++ b/ash/components/tether/tether_host_fetcher.cc
@@ -6,8 +6,8 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "base/callback.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/tether_host_fetcher.h b/ash/components/tether/tether_host_fetcher.h index d0de5e6..77a67dc 100644 --- a/ash/components/tether/tether_host_fetcher.h +++ b/ash/components/tether/tether_host_fetcher.h
@@ -7,9 +7,9 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" #include "base/callback.h" #include "base/observer_list.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash {
diff --git a/ash/components/tether/tether_host_fetcher_impl.cc b/ash/components/tether/tether_host_fetcher_impl.cc index 7b5c659b..229c349 100644 --- a/ash/components/tether/tether_host_fetcher_impl.cc +++ b/ash/components/tether/tether_host_fetcher_impl.cc
@@ -6,8 +6,8 @@ #include <memory> +#include "ash/components/multidevice/remote_device.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/remote_device.h" namespace ash {
diff --git a/ash/components/tether/tether_host_fetcher_impl.h b/ash/components/tether/tether_host_fetcher_impl.h index eba6bc8..b5c07fb 100644 --- a/ash/components/tether/tether_host_fetcher_impl.h +++ b/ash/components/tether/tether_host_fetcher_impl.h
@@ -7,11 +7,11 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/tether_host_fetcher.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/components/tether/tether_host_fetcher_impl_unittest.cc b/ash/components/tether/tether_host_fetcher_impl_unittest.cc index 6490d243..348135a9 100644 --- a/ash/components/tether/tether_host_fetcher_impl_unittest.cc +++ b/ash/components/tether/tether_host_fetcher_impl_unittest.cc
@@ -7,16 +7,16 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/bind.h" #include "base/callback.h" -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/tether/tether_host_response_recorder.h b/ash/components/tether/tether_host_response_recorder.h index 4857bcc..8282e8f1 100644 --- a/ash/components/tether/tether_host_response_recorder.h +++ b/ash/components/tether/tether_host_response_recorder.h
@@ -8,8 +8,8 @@ #include <string> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" #include "base/observer_list.h" -#include "chromeos/components/multidevice/remote_device_ref.h" class PrefService;
diff --git a/ash/components/tether/tether_host_response_recorder_unittest.cc b/ash/components/tether/tether_host_response_recorder_unittest.cc index 44086edc..3a0c71c5 100644 --- a/ash/components/tether/tether_host_response_recorder_unittest.cc +++ b/ash/components/tether/tether_host_response_recorder_unittest.cc
@@ -6,8 +6,8 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/prefs/testing_pref_service.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/tether_network_disconnection_handler.cc b/ash/components/tether/tether_network_disconnection_handler.cc index 2dd23c1..19db361e 100644 --- a/ash/components/tether/tether_network_disconnection_handler.cc +++ b/ash/components/tether/tether_network_disconnection_handler.cc
@@ -4,6 +4,7 @@ #include "ash/components/tether/tether_network_disconnection_handler.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/disconnect_tethering_request_sender.h" #include "ash/components/tether/network_configuration_remover.h" #include "ash/components/tether/tether_disconnector.h" @@ -14,7 +15,6 @@ #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/threading/thread_task_runner_handle.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/network_handler.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/top_level_host_scan_cache.cc b/ash/components/tether/top_level_host_scan_cache.cc index bc405c4..bcf1565 100644 --- a/ash/components/tether/top_level_host_scan_cache.cc +++ b/ash/components/tether/top_level_host_scan_cache.cc
@@ -6,12 +6,12 @@ #include <algorithm> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/active_host.h" #include "ash/components/tether/persistent_host_scan_cache.h" #include "ash/components/tether/timer_factory.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/components/tether/wifi_hotspot_connector.cc b/ash/components/tether/wifi_hotspot_connector.cc index b8e586e..e7e23f781 100644 --- a/ash/components/tether/wifi_hotspot_connector.cc +++ b/ash/components/tether/wifi_hotspot_connector.cc
@@ -6,12 +6,12 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/guid.h" #include "base/metrics/histogram_macros.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/default_clock.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/device_state.h" #include "chromeos/network/network_connect.h" #include "chromeos/network/network_handler.h"
diff --git a/ash/components/tether/wifi_hotspot_disconnector_impl.cc b/ash/components/tether/wifi_hotspot_disconnector_impl.cc index a12c0fa..8b27c880 100644 --- a/ash/components/tether/wifi_hotspot_disconnector_impl.cc +++ b/ash/components/tether/wifi_hotspot_disconnector_impl.cc
@@ -4,11 +4,11 @@ #include "ash/components/tether/wifi_hotspot_disconnector_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/network_configuration_remover.h" #include "ash/components/tether/pref_names.h" #include "base/bind.h" #include "base/callback_helpers.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/network_connection_handler.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h"
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index 3da72786..66f2636 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -24,6 +24,11 @@ } // namespace +// Enables the UI and logic that minimizes the amount of time the device spends +// at full battery. This preserves battery lifetime. +const base::Feature kAdaptiveCharging{"AdaptiveCharging", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Adjusts portrait mode split view to avoid the input field in the bottom // window being occluded by the virtual keyboard. const base::Feature kAdjustSplitViewForVK{"AdjustSplitViewForVK", @@ -642,7 +647,7 @@ base::FEATURE_DISABLED_BY_DEFAULT}; // Enables the System Web App (SWA) version of file manager. -const base::Feature kFilesSWA{"FilesSWA", base::FEATURE_ENABLED_BY_DEFAULT}; +const base::Feature kFilesSWA{"FilesSWA", base::FEATURE_DISABLED_BY_DEFAULT}; // Enables partitioning of removable disks in file manager. const base::Feature kFilesSinglePartitionFormat{ @@ -1009,7 +1014,7 @@ // If enabled, EULA and ARC Terms of Service screens are skipped and merged // into Consolidated Consent Screen. const base::Feature kOobeConsolidatedConsent{"OobeConsolidatedConsent", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; // Enables or disables the Chrome OS OOBE HID Detection Revamp, which updates // the OOBE HID detection screen UI and related infrastructure. See @@ -1457,7 +1462,7 @@ // Enables or disables whether to store UMA logs per-user and whether metrics // consent is per-user. const base::Feature kPerUserMetrics{"PerUserMetricsConsent", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; //////////////////////////////////////////////////////////////////////////////// bool AreContextualNudgesEnabled() { @@ -1478,6 +1483,10 @@ return base::FeatureList::IsEnabled(kWindowsFollowCursor); } +bool IsAdaptiveChargingEnabled() { + return base::FeatureList::IsEnabled(kAdaptiveCharging); +} + bool IsAdjustSplitViewForVKEnabled() { return base::FeatureList::IsEnabled(kAdjustSplitViewForVK); }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h index 29461803..f75f9cab 100644 --- a/ash/constants/ash_features.h +++ b/ash/constants/ash_features.h
@@ -18,6 +18,8 @@ // being rolled out via Finch, add a comment in the .cc file. COMPONENT_EXPORT(ASH_CONSTANTS) +extern const base::Feature kAdaptiveCharging; +COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kAdjustSplitViewForVK; COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kAllowAmbientEQ; COMPONENT_EXPORT(ASH_CONSTANTS) @@ -564,6 +566,7 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool AreDesksTrackpadSwipeImprovementsEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool AreImprovedScreenCaptureSettingsEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool DoWindowsFollowCursor(); +COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAdaptiveChargingEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAdjustSplitViewForVKEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAllowAmbientEQEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAmbientModeAnimationEnabled();
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.cc b/ash/multi_device_setup/multi_device_notification_presenter.cc index c846a07b..d3dc1b0 100644 --- a/ash/multi_device_setup/multi_device_notification_presenter.cc +++ b/ash/multi_device_setup/multi_device_notification_presenter.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/public/cpp/notification_utils.h" #include "ash/public/cpp/system_tray_client.h" #include "ash/resources/vector_icons/vector_icons.h" @@ -19,7 +20,6 @@ #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" #include "base/strings/utf_string_conversions.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "ui/base/l10n/l10n_util.h" #include "ui/chromeos/devicetype_utils.h" #include "ui/message_center/message_center.h"
diff --git a/ash/public/cpp/desk_template.cc b/ash/public/cpp/desk_template.cc index c5b6e93..0c935816 100644 --- a/ash/public/cpp/desk_template.cc +++ b/ash/public/cpp/desk_template.cc
@@ -25,8 +25,8 @@ // static bool DeskTemplate::IsAppTypeSupported(aura::Window* window) { - // For now we'll crostini and lacros windows in desk template. We'll also - // ignore ARC apps unless the flag is turned on. + // For now we'll ignore crostini and lacros windows in desk template. We'll + // also ignore ARC apps unless the flag is turned on. const AppType app_type = static_cast<AppType>(window->GetProperty(aura::client::kAppType)); switch (app_type) {
diff --git a/ash/quick_pair/repository/fake_fast_pair_repository.cc b/ash/quick_pair/repository/fake_fast_pair_repository.cc index f77dc54..e3b69044 100644 --- a/ash/quick_pair/repository/fake_fast_pair_repository.cc +++ b/ash/quick_pair/repository/fake_fast_pair_repository.cc
@@ -76,6 +76,15 @@ } // Unimplemented. +void FakeFastPairRepository::CheckOptInStatus( + CheckOptInStatusCallback callback) {} + +// Unimplemented. +void FakeFastPairRepository::UpdateOptInStatus( + nearby::fastpair::OptInStatus opt_in_status, + UpdateOptInStatusCallback callback) {} + +// Unimplemented. void FakeFastPairRepository::FetchDeviceImages(scoped_refptr<Device> device) { return; }
diff --git a/ash/quick_pair/repository/fake_fast_pair_repository.h b/ash/quick_pair/repository/fake_fast_pair_repository.h index 8036598..509116b 100644 --- a/ash/quick_pair/repository/fake_fast_pair_repository.h +++ b/ash/quick_pair/repository/fake_fast_pair_repository.h
@@ -62,6 +62,9 @@ bool EvictDeviceImages(const device::BluetoothDevice* device) override; absl::optional<chromeos::bluetooth_config::DeviceImageInfo> GetImagesForDevice(const std::string& device_id) override; + void CheckOptInStatus(CheckOptInStatusCallback callback) override; + void UpdateOptInStatus(nearby::fastpair::OptInStatus opt_in_status, + UpdateOptInStatusCallback callback) override; private: static void SetInstance(FastPairRepository* instance);
diff --git a/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.cc b/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.cc index 1ce3afdd..e36bfa2a 100644 --- a/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.cc +++ b/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.cc
@@ -16,18 +16,47 @@ FakeFootprintsFetcher::~FakeFootprintsFetcher() = default; void FakeFootprintsFetcher::GetUserDevices(UserReadDevicesCallback callback) { + if (response_.has_value()) { + std::move(callback).Run(std::move(response_)); + return; + } + nearby::fastpair::UserReadDevicesResponse response; for (const auto& entry : account_key_to_info_map_) { *response.add_fast_pair_info() = entry.second; } + + if (add_user_result_) + *response.add_fast_pair_info() = opt_in_status_info_; std::move(callback).Run(std::move(response)); } -void FakeFootprintsFetcher::AddUserDevice(nearby::fastpair::FastPairInfo info, - AddDeviceCallback callback) { +void FakeFootprintsFetcher::SetGetUserDevicesResponse( + absl::optional<nearby::fastpair::UserReadDevicesResponse> response) { + response_ = response; +} + +void FakeFootprintsFetcher::SetAddUserFastPairInfoResult(bool add_user_result) { + add_user_result_ = add_user_result; +} + +void FakeFootprintsFetcher::AddUserFastPairInfo( + nearby::fastpair::FastPairInfo info, + AddDeviceCallback callback) { + if (info.has_opt_in_status() && add_user_result_) { + opt_in_status_info_ = info; + std::move(callback).Run(add_user_result_); + return; + } + + if (info.has_opt_in_status() && !add_user_result_) { + std::move(callback).Run(add_user_result_); + return; + } + account_key_to_info_map_[base::HexEncode( base::as_bytes(base::make_span(info.device().account_key())))] = info; - std::move(callback).Run(true); + std::move(callback).Run(add_user_result_); } void FakeFootprintsFetcher::DeleteUserDevice(const std::string& hex_account_key,
diff --git a/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.h b/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.h index 3bf7614..19baf5b 100644 --- a/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.h +++ b/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.h
@@ -23,14 +23,22 @@ // FootprintsFetcher:: void GetUserDevices(UserReadDevicesCallback callback) override; - void AddUserDevice(nearby::fastpair::FastPairInfo info, - AddDeviceCallback callback) override; + void AddUserFastPairInfo(nearby::fastpair::FastPairInfo info, + AddDeviceCallback callback) override; void DeleteUserDevice(const std::string& hex_account_key, DeleteDeviceCallback callback) override; bool ContainsKey(const std::vector<uint8_t>& account_key); + void SetGetUserDevicesResponse( + absl::optional<nearby::fastpair::UserReadDevicesResponse> response); + + void SetAddUserFastPairInfoResult(bool add_user_result); + private: + bool add_user_result_ = true; + absl::optional<nearby::fastpair::UserReadDevicesResponse> response_; + nearby::fastpair::FastPairInfo opt_in_status_info_; base::flat_map<std::string, nearby::fastpair::FastPairInfo> account_key_to_info_map_; };
diff --git a/ash/quick_pair/repository/fast_pair/footprints_fetcher.h b/ash/quick_pair/repository/fast_pair/footprints_fetcher.h index 650ef359..baeb22f7 100644 --- a/ash/quick_pair/repository/fast_pair/footprints_fetcher.h +++ b/ash/quick_pair/repository/fast_pair/footprints_fetcher.h
@@ -34,8 +34,8 @@ virtual ~FootprintsFetcher() = default; virtual void GetUserDevices(UserReadDevicesCallback callback) = 0; - virtual void AddUserDevice(nearby::fastpair::FastPairInfo info, - AddDeviceCallback callback) = 0; + virtual void AddUserFastPairInfo(nearby::fastpair::FastPairInfo info, + AddDeviceCallback callback) = 0; virtual void DeleteUserDevice(const std::string& hex_account_key, DeleteDeviceCallback callback) = 0; };
diff --git a/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc b/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc index faa0e062..957b60f0 100644 --- a/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc +++ b/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc
@@ -128,8 +128,9 @@ std::move(callback).Run(devices); } -void FootprintsFetcherImpl::AddUserDevice(nearby::fastpair::FastPairInfo info, - AddDeviceCallback callback) { +void FootprintsFetcherImpl::AddUserFastPairInfo( + nearby::fastpair::FastPairInfo info, + AddDeviceCallback callback) { auto http_fetcher = CreateHttpFetcher(); auto* raw_http_fetcher = http_fetcher.get(); raw_http_fetcher->ExecutePostRequest(
diff --git a/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.h b/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.h index 59635de..261129a 100644 --- a/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.h +++ b/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.h
@@ -27,8 +27,8 @@ ~FootprintsFetcherImpl() override; void GetUserDevices(UserReadDevicesCallback callback) override; - void AddUserDevice(nearby::fastpair::FastPairInfo info, - AddDeviceCallback callback) override; + void AddUserFastPairInfo(nearby::fastpair::FastPairInfo info, + AddDeviceCallback callback) override; void DeleteUserDevice(const std::string& hex_account_key, DeleteDeviceCallback callback) override;
diff --git a/ash/quick_pair/repository/fast_pair/proto_conversions.cc b/ash/quick_pair/repository/fast_pair/proto_conversions.cc index bbdaab8..845ad48 100644 --- a/ash/quick_pair/repository/fast_pair/proto_conversions.cc +++ b/ash/quick_pair/repository/fast_pair/proto_conversions.cc
@@ -97,5 +97,12 @@ return proto; } +nearby::fastpair::FastPairInfo BuildFastPairInfoForOptIn( + nearby::fastpair::OptInStatus opt_in_status) { + nearby::fastpair::FastPairInfo proto; + proto.set_opt_in_status(opt_in_status); + return proto; +} + } // namespace quick_pair } // namespace ash
diff --git a/ash/quick_pair/repository/fast_pair/proto_conversions.h b/ash/quick_pair/repository/fast_pair/proto_conversions.h index 3d813774..b9b422c0 100644 --- a/ash/quick_pair/repository/fast_pair/proto_conversions.h +++ b/ash/quick_pair/repository/fast_pair/proto_conversions.h
@@ -17,6 +17,9 @@ const std::vector<uint8_t>& account_key, DeviceMetadata* metadata); +nearby::fastpair::FastPairInfo BuildFastPairInfoForOptIn( + nearby::fastpair::OptInStatus opt_in_status); + } // namespace quick_pair } // namespace ash
diff --git a/ash/quick_pair/repository/fast_pair_repository.h b/ash/quick_pair/repository/fast_pair_repository.h index 0d3f9ac..f88fb6d8 100644 --- a/ash/quick_pair/repository/fast_pair_repository.h +++ b/ash/quick_pair/repository/fast_pair_repository.h
@@ -33,6 +33,9 @@ base::OnceCallback<void(absl::optional<PairingMetadata>)>; using DeviceMetadataCallback = base::OnceCallback<void(DeviceMetadata*, bool)>; using ValidModelIdCallback = base::OnceCallback<void(bool)>; +using CheckOptInStatusCallback = + base::OnceCallback<void(nearby::fastpair::OptInStatus)>; +using UpdateOptInStatusCallback = base::OnceCallback<void(bool)>; // The entry point for the Repository component in the Quick Pair system, // responsible for connecting to back-end services. @@ -80,6 +83,19 @@ virtual absl::optional<chromeos::bluetooth_config::DeviceImageInfo> GetImagesForDevice(const std::string& device_id) = 0; + // Fetches the opt in status from Footprints to determine the status for + // saving a user's devices to their account, which is synced all across a + // user's devices. + virtual void CheckOptInStatus(CheckOptInStatusCallback callback) = 0; + + // Updates the opt in status for saving a user's devices to their account, + // synced across all of a user's devices, based on |opt_in_status|. + // If |opt_in_status| reflects the user being opted out, then all devices + // saved to their account are removed. The success or failure of updating + // the opt in status is reflected in running |callback|. + virtual void UpdateOptInStatus(nearby::fastpair::OptInStatus opt_in_status, + UpdateOptInStatusCallback callback) = 0; + protected: static void SetInstance(FastPairRepository* instance); };
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl.cc b/ash/quick_pair/repository/fast_pair_repository_impl.cc index 7d5b1cd..74d4311 100644 --- a/ash/quick_pair/repository/fast_pair_repository_impl.cc +++ b/ash/quick_pair/repository/fast_pair_repository_impl.cc
@@ -204,12 +204,12 @@ DCHECK(device->classic_address()); GetDeviceMetadata( device->metadata_id, - base::BindOnce(&FastPairRepositoryImpl::AddToFootprints, + base::BindOnce(&FastPairRepositoryImpl::AddDeviceToFootprints, weak_ptr_factory_.GetWeakPtr(), device->metadata_id, device->classic_address().value(), account_key)); } -void FastPairRepositoryImpl::AddToFootprints( +void FastPairRepositoryImpl::AddDeviceToFootprints( const std::string& hex_model_id, const std::string& mac_address, const std::vector<uint8_t>& account_key, @@ -220,13 +220,13 @@ return; } - footprints_fetcher_->AddUserDevice( + footprints_fetcher_->AddUserFastPairInfo( BuildFastPairInfo(hex_model_id, account_key, metadata), - base::BindOnce(&FastPairRepositoryImpl::OnAddToFootprintsComplete, + base::BindOnce(&FastPairRepositoryImpl::OnAddDeviceToFootprintsComplete, weak_ptr_factory_.GetWeakPtr(), mac_address, account_key)); } -void FastPairRepositoryImpl::OnAddToFootprintsComplete( +void FastPairRepositoryImpl::OnAddDeviceToFootprintsComplete( const std::string& mac_address, const std::vector<uint8_t>& account_key, bool success) { @@ -238,6 +238,51 @@ saved_device_registry_->SaveAccountKey(mac_address, account_key); } +void FastPairRepositoryImpl::CheckOptInStatus( + CheckOptInStatusCallback callback) { + footprints_fetcher_->GetUserDevices( + base::BindOnce(&FastPairRepositoryImpl::OnCheckOptInStatus, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} + +void FastPairRepositoryImpl::OnCheckOptInStatus( + CheckOptInStatusCallback callback, + absl::optional<nearby::fastpair::UserReadDevicesResponse> user_devices) { + QP_LOG(INFO) << __func__; + if (!user_devices) { + QP_LOG(WARNING) + << __func__ + << ": Missing UserReadDevicesResponse from call to Footprints"; + std::move(callback).Run(nearby::fastpair::OptInStatus::STATUS_UNKNOWN); + return; + } + + for (const auto& info : user_devices->fast_pair_info()) { + if (info.has_opt_in_status()) { + std::move(callback).Run(info.opt_in_status()); + return; + } + } + + std::move(callback).Run(nearby::fastpair::OptInStatus::STATUS_UNKNOWN); +} + +void FastPairRepositoryImpl::UpdateOptInStatus( + nearby::fastpair::OptInStatus opt_in_status, + UpdateOptInStatusCallback callback) { + footprints_fetcher_->AddUserFastPairInfo( + BuildFastPairInfoForOptIn(opt_in_status), + base::BindOnce(&FastPairRepositoryImpl::OnUpdateOptInStatusComplete, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} + +void FastPairRepositoryImpl::OnUpdateOptInStatusComplete( + UpdateOptInStatusCallback callback, + bool success) { + QP_LOG(INFO) << __func__ << ": success=" << success; + std::move(callback).Run(success); +} + bool FastPairRepositoryImpl::DeleteAssociatedDevice( const device::BluetoothDevice* device) { absl::optional<const std::vector<uint8_t>> account_key =
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl.h b/ash/quick_pair/repository/fast_pair_repository_impl.h index 40f48da..6782edf 100644 --- a/ash/quick_pair/repository/fast_pair_repository_impl.h +++ b/ash/quick_pair/repository/fast_pair_repository_impl.h
@@ -68,6 +68,9 @@ bool EvictDeviceImages(const device::BluetoothDevice* device) override; absl::optional<chromeos::bluetooth_config::DeviceImageInfo> GetImagesForDevice(const std::string& device_id) override; + void CheckOptInStatus(CheckOptInStatusCallback callback) override; + void UpdateOptInStatus(nearby::fastpair::OptInStatus opt_in_status, + UpdateOptInStatusCallback callback) override; private: void CheckAccountKeysImpl(const AccountKeyFilter& account_key_filter, @@ -92,15 +95,20 @@ const std::vector<uint8_t> account_key, DeviceMetadata* device_metadata, bool has_retryable_error); - void AddToFootprints(const std::string& hex_model_id, - const std::string& mac_address, - const std::vector<uint8_t>& account_key, - DeviceMetadata* metadata, - bool has_retryable_error); - void OnAddToFootprintsComplete(const std::string& mac_address, - const std::vector<uint8_t>& account_key, - bool success); - // Fethces the |device_metadata| images to the DeviceImageStore for + void AddDeviceToFootprints(const std::string& hex_model_id, + const std::string& mac_address, + const std::vector<uint8_t>& account_key, + DeviceMetadata* metadata, + bool has_retryable_error); + void OnAddDeviceToFootprintsComplete(const std::string& mac_address, + const std::vector<uint8_t>& account_key, + bool success); + void OnCheckOptInStatus( + CheckOptInStatusCallback callback, + absl::optional<nearby::fastpair::UserReadDevicesResponse> user_devices); + void OnUpdateOptInStatusComplete(UpdateOptInStatusCallback callback, + bool success); + // Fetches the |device_metadata| images to the DeviceImageStore for // |hex_model_id|. void CompleteFetchDeviceImages(const std::string& hex_model_id, DeviceMetadata* device_metadata,
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc b/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc index bbc8ab5..ac569d57 100644 --- a/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc +++ b/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc
@@ -23,6 +23,7 @@ #include "base/memory/scoped_refptr.h" #include "base/strings/string_number_conversions.h" #include "base/test/gmock_callback_support.h" +#include "base/test/mock_callback.h" #include "components/prefs/testing_pref_service.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h" @@ -231,7 +232,7 @@ DeviceMetadata metadata(device, gfx::Image()); // FakeFootprintsFetcher APIs are actually synchronous. - footprints_fetcher_->AddUserDevice( + footprints_fetcher_->AddUserFastPairInfo( BuildFastPairInfo(kValidModelId, kAccountKey1, &metadata), base::DoNothing()); @@ -346,5 +347,76 @@ ASSERT_FALSE(device_id_map_->GetModelIdForDeviceId(kTestDeviceId)); } +TEST_F(FastPairRepositoryImplTest, UpdateOptInStatus_OptedIn) { + base::MockCallback<base::OnceCallback<void(bool)>> callback1; + EXPECT_CALL(callback1, Run(true)).Times(1); + fast_pair_repository_->UpdateOptInStatus( + nearby::fastpair::OptInStatus::STATUS_OPTED_IN, callback1.Get()); + base::RunLoop().RunUntilIdle(); + + base::MockCallback<base::OnceCallback<void(nearby::fastpair::OptInStatus)>> + callback2; + EXPECT_CALL(callback2, + Run(testing::Eq(nearby::fastpair::OptInStatus::STATUS_OPTED_IN))) + .Times(1); + fast_pair_repository_->CheckOptInStatus(callback2.Get()); +} + +TEST_F(FastPairRepositoryImplTest, UpdateOptInStatus_OptedOut) { + base::MockCallback<base::OnceCallback<void(bool)>> callback1; + EXPECT_CALL(callback1, Run(true)).Times(1); + fast_pair_repository_->UpdateOptInStatus( + nearby::fastpair::OptInStatus::STATUS_OPTED_OUT, callback1.Get()); + base::RunLoop().RunUntilIdle(); + + base::MockCallback<base::OnceCallback<void(nearby::fastpair::OptInStatus)>> + callback2; + EXPECT_CALL(callback2, + Run(testing::Eq(nearby::fastpair::OptInStatus::STATUS_OPTED_OUT))) + .Times(1); + fast_pair_repository_->CheckOptInStatus(callback2.Get()); +} + +TEST_F(FastPairRepositoryImplTest, UpdateOptInStatus_StatusUnknown) { + base::MockCallback<base::OnceCallback<void(bool)>> callback1; + EXPECT_CALL(callback1, Run(true)).Times(1); + fast_pair_repository_->UpdateOptInStatus( + nearby::fastpair::OptInStatus::STATUS_UNKNOWN, callback1.Get()); + base::RunLoop().RunUntilIdle(); + + base::MockCallback<base::OnceCallback<void(nearby::fastpair::OptInStatus)>> + callback2; + EXPECT_CALL(callback2, + Run(testing::Eq(nearby::fastpair::OptInStatus::STATUS_UNKNOWN))) + .Times(1); + fast_pair_repository_->CheckOptInStatus(callback2.Get()); +} + +TEST_F(FastPairRepositoryImplTest, UpdateOptInStatus_NoFootprintsResponse) { + footprints_fetcher_->SetGetUserDevicesResponse(absl::nullopt); + base::MockCallback<base::OnceCallback<void(nearby::fastpair::OptInStatus)>> + callback; + EXPECT_CALL(callback, + Run(testing::Eq(nearby::fastpair::OptInStatus::STATUS_UNKNOWN))) + .Times(1); + fast_pair_repository_->CheckOptInStatus(callback.Get()); +} + +TEST_F(FastPairRepositoryImplTest, UpdateOptInStatus_OptedInUpdateFailed) { + footprints_fetcher_->SetAddUserFastPairInfoResult(/*add_user_result=*/false); + base::MockCallback<base::OnceCallback<void(bool)>> callback1; + EXPECT_CALL(callback1, Run(false)).Times(1); + fast_pair_repository_->UpdateOptInStatus( + nearby::fastpair::OptInStatus::STATUS_OPTED_IN, callback1.Get()); + base::RunLoop().RunUntilIdle(); + + base::MockCallback<base::OnceCallback<void(nearby::fastpair::OptInStatus)>> + callback2; + EXPECT_CALL(callback2, + Run(testing::Eq(nearby::fastpair::OptInStatus::STATUS_UNKNOWN))) + .Times(1); + fast_pair_repository_->CheckOptInStatus(callback2.Get()); +} + } // namespace quick_pair } // namespace ash
diff --git a/ash/quick_pair/repository/mock_fast_pair_repository.h b/ash/quick_pair/repository/mock_fast_pair_repository.h index 475fe571..825574c 100644 --- a/ash/quick_pair/repository/mock_fast_pair_repository.h +++ b/ash/quick_pair/repository/mock_fast_pair_repository.h
@@ -55,6 +55,15 @@ GetImagesForDevice, (const std::string& device_id), (override)); + MOCK_METHOD(void, + CheckOptInStatus, + (CheckOptInStatusCallback callback), + (override)); + MOCK_METHOD(void, + UpdateOptInStatus, + (nearby::fastpair::OptInStatus opt_in_status, + UpdateOptInStatusCallback callback), + (override)); }; } // namespace quick_pair
diff --git a/ash/resources/BUILD.gn b/ash/resources/BUILD.gn index 3ccf85d0..6bb770f 100644 --- a/ash/resources/BUILD.gn +++ b/ash/resources/BUILD.gn
@@ -27,10 +27,10 @@ ] deps = [ + "//ash/components/multidevice/mojom:mojom_js", "//ash/services/cellular_setup/public/mojom:mojom_js", "//ash/services/device_sync/public/mojom:mojom_js", "//ash/services/multidevice_setup/public/mojom:mojom_js", - "//chromeos/components/multidevice/mojom:mojom_js", ] }
diff --git a/ash/resources/multidevice_resources.grdp b/ash/resources/multidevice_resources.grdp index 5ff8ffb..4d4051e 100644 --- a/ash/resources/multidevice_resources.grdp +++ b/ash/resources/multidevice_resources.grdp
@@ -2,13 +2,13 @@ <grit-part> <!-- Mojo JS files. --> <include name="IDR_MULTIDEVICE_MULTIDEVICE_TYPES_MOJOM_HTML" - file="${mojom_root}/chromeos/components/multidevice/mojom/multidevice_types.mojom.html" - resource_path="mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom.html" + file="${mojom_root}/ash/components/multidevice/mojom/multidevice_types.mojom.html" + resource_path="mojo/ash/components/multidevice/mojom/multidevice_types.mojom.html" use_base_dir="false" type="BINDATA" /> <include name="IDR_MULTIDEVICE_MULTIDEVICE_TYPES_MOJOM_LITE_JS" - file="${mojom_root}/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js" - resource_path="mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js" + file="${mojom_root}/ash/components/multidevice/mojom/multidevice_types.mojom-lite.js" + resource_path="mojo/ash/components/multidevice/mojom/multidevice_types.mojom-lite.js" use_base_dir="false" type="BINDATA" /> <include name="IDR_MULTIDEVICE_DEVICE_SYNC_MOJOM_HTML"
diff --git a/ash/services/device_sync/BUILD.gn b/ash/services/device_sync/BUILD.gn index 798f51b..0a59463c 100644 --- a/ash/services/device_sync/BUILD.gn +++ b/ash/services/device_sync/BUILD.gn
@@ -148,21 +148,21 @@ public_deps = [ ":feature_status_change", + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/services/device_sync/public/mojom", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", "//components/signin/public/identity_manager", "//services/network/public/cpp", ] deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/constants", "//ash/services/device_sync/proto:util", "//ash/services/device_sync/public/cpp", "//ash/services/device_sync/public/mojom", "//base", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", "//chromeos/network", "//components/gcm_driver", "//components/prefs", @@ -257,8 +257,8 @@ deps = [ ":device_sync", + "//ash/components/multidevice:stub_multidevice_util", "//base", - "//chromeos/components/multidevice:stub_multidevice_util", ] } @@ -305,6 +305,8 @@ ":device_sync", ":feature_status_change", ":test_support", + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", "//ash/constants", "//ash/services/device_sync/proto:test_support", "//ash/services/device_sync/proto:util", @@ -315,8 +317,6 @@ "//ash/services/device_sync/public/mojom:unit_tests", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", "//chromeos/dbus:test_support", "//chromeos/network", "//chromeos/network:test_support",
diff --git a/ash/services/device_sync/DEPS b/ash/services/device_sync/DEPS index 80f94885..da5f42b 100644 --- a/ash/services/device_sync/DEPS +++ b/ash/services/device_sync/DEPS
@@ -1,4 +1,5 @@ include_rules = [ + "+ash/components/multidevice", "+ash/constants", "+components/gcm_driver", "+components/signin/public",
diff --git a/ash/services/device_sync/OWNERS b/ash/services/device_sync/OWNERS index 4b6c4d12..6d67d25f 100644 --- a/ash/services/device_sync/OWNERS +++ b/ash/services/device_sync/OWNERS
@@ -1,4 +1,4 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS per-file *_type_converter*.*=set noparent per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
diff --git a/ash/services/device_sync/cryptauth_api_call_flow.cc b/ash/services/device_sync/cryptauth_api_call_flow.cc index 98f02f4..4eca841 100644 --- a/ash/services/device_sync/cryptauth_api_call_flow.cc +++ b/ash/services/device_sync/cryptauth_api_call_flow.cc
@@ -4,8 +4,8 @@ #include "ash/services/device_sync/cryptauth_api_call_flow.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/strings/string_number_conversions.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "net/base/url_util.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/ash/services/device_sync/cryptauth_client_impl.cc b/ash/services/device_sync/cryptauth_client_impl.cc index e128518..6232b0a 100644 --- a/ash/services/device_sync/cryptauth_client_impl.cc +++ b/ash/services/device_sync/cryptauth_client_impl.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h" #include "ash/services/device_sync/proto/cryptauth_enrollment.pb.h" #include "ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h" @@ -13,7 +14,6 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/signin/public/base/consent_level.h" #include "components/signin/public/identity_manager/access_token_info.h" #include "components/signin/public/identity_manager/identity_manager.h"
diff --git a/ash/services/device_sync/cryptauth_device.cc b/ash/services/device_sync/cryptauth_device.cc index 7731259..adf2235 100644 --- a/ash/services/device_sync/cryptauth_device.cc +++ b/ash/services/device_sync/cryptauth_device.cc
@@ -6,12 +6,12 @@ #include <sstream> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/proto/cryptauth_logging.h" #include "ash/services/device_sync/value_string_encoding.h" #include "base/i18n/time_formatting.h" #include "base/json/values_util.h" #include "base/strings/string_number_conversions.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_device.h b/ash/services/device_sync/cryptauth_device.h index 302d3513..bbb9f30 100644 --- a/ash/services/device_sync/cryptauth_device.h +++ b/ash/services/device_sync/cryptauth_device.h
@@ -9,11 +9,11 @@ #include <ostream> #include <string> +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h" #include "base/time/time.h" #include "base/values.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_device_activity_getter_impl.cc b/ash/services/device_sync/cryptauth_device_activity_getter_impl.cc index 1fe71246..f67773b 100644 --- a/ash/services/device_sync/cryptauth_device_activity_getter_impl.cc +++ b/ash/services/device_sync/cryptauth_device_activity_getter_impl.cc
@@ -7,6 +7,9 @@ #include <array> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/async_execution_time_metrics_logger.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/cryptauth_key_bundle.h" @@ -17,9 +20,6 @@ #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_device_manager_impl.cc b/ash/services/device_sync/cryptauth_device_manager_impl.cc index 8077dd9..165a012 100644 --- a/ash/services/device_sync/cryptauth_device_manager_impl.cc +++ b/ash/services/device_sync/cryptauth_device_manager_impl.cc
@@ -10,6 +10,8 @@ #include <stdexcept> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/pref_names.h" #include "ash/services/device_sync/proto/enum_util.h" @@ -21,8 +23,6 @@ #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h"
diff --git a/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc b/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc index 8f3c3ea..6fa5229 100644 --- a/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc +++ b/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc
@@ -9,6 +9,7 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/fake_cryptauth_gcm_manager.h" #include "ash/services/device_sync/mock_cryptauth_client.h" #include "ash/services/device_sync/mock_sync_scheduler.h" @@ -24,7 +25,6 @@ #include "base/test/gmock_move_support.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/simple_test_clock.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/prefs/testing_pref_service.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
diff --git a/ash/services/device_sync/cryptauth_device_notifier_impl.cc b/ash/services/device_sync/cryptauth_device_notifier_impl.cc index 76f58df9..26382e4 100644 --- a/ash/services/device_sync/cryptauth_device_notifier_impl.cc +++ b/ash/services/device_sync/cryptauth_device_notifier_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/async_execution_time_metrics_logger.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/cryptauth_key_bundle.h" @@ -13,7 +14,6 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_device_registry_impl.cc b/ash/services/device_sync/cryptauth_device_registry_impl.cc index 1cdbecb..6b35072b 100644 --- a/ash/services/device_sync/cryptauth_device_registry_impl.cc +++ b/ash/services/device_sync/cryptauth_device_registry_impl.cc
@@ -7,11 +7,11 @@ #include <string> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/cryptauth_device.h" #include "ash/services/device_sync/pref_names.h" #include "ash/services/device_sync/value_string_encoding.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/device_sync/cryptauth_device_registry_impl_unittest.cc b/ash/services/device_sync/cryptauth_device_registry_impl_unittest.cc index 3a180be..a808fbe4 100644 --- a/ash/services/device_sync/cryptauth_device_registry_impl_unittest.cc +++ b/ash/services/device_sync/cryptauth_device_registry_impl_unittest.cc
@@ -9,6 +9,8 @@ #include <string> #include <utility> +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/cryptauth_device_registry.h" #include "ash/services/device_sync/pref_names.h" #include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h" @@ -17,8 +19,6 @@ #include "base/no_destructor.h" #include "base/time/time.h" #include "base/values.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/prefs/testing_pref_service.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/device_sync/cryptauth_device_syncer_impl.cc b/ash/services/device_sync/cryptauth_device_syncer_impl.cc index 6e06649..b3586da98a 100644 --- a/ash/services/device_sync/cryptauth_device_syncer_impl.cc +++ b/ash/services/device_sync/cryptauth_device_syncer_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/async_execution_time_metrics_logger.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/cryptauth_ecies_encryptor_impl.h" @@ -23,7 +24,6 @@ #include "base/containers/flat_set.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_device_unittest.cc b/ash/services/device_sync/cryptauth_device_unittest.cc index 1763b082..db50f10 100644 --- a/ash/services/device_sync/cryptauth_device_unittest.cc +++ b/ash/services/device_sync/cryptauth_device_unittest.cc
@@ -6,11 +6,11 @@ #include <map> +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h" #include "ash/services/device_sync/proto/cryptauth_v2_test_util.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/device_sync/cryptauth_ecies_encryptor_impl.cc b/ash/services/device_sync/cryptauth_ecies_encryptor_impl.cc index 3f901ce..9c1e39be 100644 --- a/ash/services/device_sync/cryptauth_ecies_encryptor_impl.cc +++ b/ash/services/device_sync/cryptauth_ecies_encryptor_impl.cc
@@ -6,12 +6,12 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/services/device_sync/value_string_encoding.h" #include "base/bind.h" #include "base/containers/contains.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" #include "third_party/securemessage/proto/securemessage.pb.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_ecies_encryptor_impl_unittest.cc b/ash/services/device_sync/cryptauth_ecies_encryptor_impl_unittest.cc index 98e463f0..7c6ea35 100644 --- a/ash/services/device_sync/cryptauth_ecies_encryptor_impl_unittest.cc +++ b/ash/services/device_sync/cryptauth_ecies_encryptor_impl_unittest.cc
@@ -6,13 +6,13 @@ #include <string> +#include "ash/components/multidevice/fake_secure_message_delegate.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/services/device_sync/value_string_encoding.h" #include "base/bind.h" #include "base/containers/contains.h" #include "base/run_loop.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/securemessage/proto/securemessage.pb.h"
diff --git a/ash/services/device_sync/cryptauth_enroller_factory_impl.cc b/ash/services/device_sync/cryptauth_enroller_factory_impl.cc index a94354e..fed3df94 100644 --- a/ash/services/device_sync/cryptauth_enroller_factory_impl.cc +++ b/ash/services/device_sync/cryptauth_enroller_factory_impl.cc
@@ -6,8 +6,8 @@ #include <memory> +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/services/device_sync/cryptauth_enroller_impl.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_enroller_impl.cc b/ash/services/device_sync/cryptauth_enroller_impl.cc index 066d9476..f1f8c70 100644 --- a/ash/services/device_sync/cryptauth_enroller_impl.cc +++ b/ash/services/device_sync/cryptauth_enroller_impl.cc
@@ -6,11 +6,11 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate.h" #include "ash/services/device_sync/cryptauth_client_impl.h" #include "base/bind.h" #include "base/metrics/histogram_macros.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate.h" #include "crypto/sha2.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_enroller_impl_unittest.cc b/ash/services/device_sync/cryptauth_enroller_impl_unittest.cc index 2781246..4f79738 100644 --- a/ash/services/device_sync/cryptauth_enroller_impl_unittest.cc +++ b/ash/services/device_sync/cryptauth_enroller_impl_unittest.cc
@@ -6,11 +6,11 @@ #include <memory> +#include "ash/components/multidevice/fake_secure_message_delegate.h" #include "ash/services/device_sync/mock_cryptauth_client.h" #include "ash/services/device_sync/network_request_error.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_;
diff --git a/ash/services/device_sync/cryptauth_enrollment_manager_impl.cc b/ash/services/device_sync/cryptauth_enrollment_manager_impl.cc index 485d558..8721ac3 100644 --- a/ash/services/device_sync/cryptauth_enrollment_manager_impl.cc +++ b/ash/services/device_sync/cryptauth_enrollment_manager_impl.cc
@@ -8,6 +8,8 @@ #include <sstream> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate.h" #include "ash/services/device_sync/cryptauth_enroller.h" #include "ash/services/device_sync/pref_names.h" #include "ash/services/device_sync/proto/enum_util.h" @@ -18,8 +20,6 @@ #include "base/time/clock.h" #include "base/time/time.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc b/ash/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc index eeed04e..fcbfd2f0 100644 --- a/ash/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc +++ b/ash/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/fake_secure_message_delegate.h" #include "ash/services/device_sync/cryptauth_enroller.h" #include "ash/services/device_sync/fake_cryptauth_gcm_manager.h" #include "ash/services/device_sync/mock_sync_scheduler.h" @@ -18,7 +19,6 @@ #include "base/test/simple_test_clock.h" #include "base/time/clock.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" #include "components/prefs/testing_pref_service.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/device_sync/cryptauth_feature_status_getter.h b/ash/services/device_sync/cryptauth_feature_status_getter.h index af4b86e..622a464 100644 --- a/ash/services/device_sync/cryptauth_feature_status_getter.h +++ b/ash/services/device_sync/cryptauth_feature_status_getter.h
@@ -8,13 +8,13 @@ #include <map> #include <string> +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/cryptauth_device_sync_result.h" #include "base/callback.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" namespace cryptauthv2 { class RequestContext;
diff --git a/ash/services/device_sync/cryptauth_feature_status_getter_impl.cc b/ash/services/device_sync/cryptauth_feature_status_getter_impl.cc index b07956b..427a9e84 100644 --- a/ash/services/device_sync/cryptauth_feature_status_getter_impl.cc +++ b/ash/services/device_sync/cryptauth_feature_status_getter_impl.cc
@@ -7,6 +7,9 @@ #include <array> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/async_execution_time_metrics_logger.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/cryptauth_feature_type.h" @@ -15,9 +18,6 @@ #include "base/containers/contains.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc b/ash/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc index 0c15b52..44428f0 100644 --- a/ash/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc +++ b/ash/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
@@ -8,6 +8,8 @@ #include <string> #include <utility> +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/cryptauth_device.h" #include "ash/services/device_sync/cryptauth_device_sync_result.h" @@ -23,8 +25,6 @@ #include "base/containers/flat_set.h" #include "base/no_destructor.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/device_sync/cryptauth_feature_status_setter.h b/ash/services/device_sync/cryptauth_feature_status_setter.h index 1683094..aa617983 100644 --- a/ash/services/device_sync/cryptauth_feature_status_setter.h +++ b/ash/services/device_sync/cryptauth_feature_status_setter.h
@@ -7,10 +7,10 @@ #include <string> +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/device_sync/network_request_error.h" #include "base/callback.h" -#include "chromeos/components/multidevice/software_feature.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_feature_status_setter_impl.cc b/ash/services/device_sync/cryptauth_feature_status_setter_impl.cc index b3659f1a..bbe3888d 100644 --- a/ash/services/device_sync/cryptauth_feature_status_setter_impl.cc +++ b/ash/services/device_sync/cryptauth_feature_status_setter_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/async_execution_time_metrics_logger.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/cryptauth_feature_type.h" @@ -15,7 +16,6 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_feature_status_setter_impl.h b/ash/services/device_sync/cryptauth_feature_status_setter_impl.h index 663a53f6..738da3d 100644 --- a/ash/services/device_sync/cryptauth_feature_status_setter_impl.h +++ b/ash/services/device_sync/cryptauth_feature_status_setter_impl.h
@@ -9,6 +9,7 @@ #include <ostream> #include <string> +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/cryptauth_feature_status_setter.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/device_sync/network_request_error.h" @@ -17,7 +18,6 @@ #include "base/containers/queue.h" #include "base/time/time.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/software_feature.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_feature_type.h b/ash/services/device_sync/cryptauth_feature_type.h index 5a82642..c452009 100644 --- a/ash/services/device_sync/cryptauth_feature_type.h +++ b/ash/services/device_sync/cryptauth_feature_type.h
@@ -8,8 +8,8 @@ #include <ostream> #include <string> +#include "ash/components/multidevice/software_feature.h" #include "base/containers/flat_set.h" -#include "chromeos/components/multidevice/software_feature.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_gcm_manager_impl.cc b/ash/services/device_sync/cryptauth_gcm_manager_impl.cc index 7c29eef..6fb5910 100644 --- a/ash/services/device_sync/cryptauth_gcm_manager_impl.cc +++ b/ash/services/device_sync/cryptauth_gcm_manager_impl.cc
@@ -4,6 +4,7 @@ #include "ash/services/device_sync/cryptauth_gcm_manager_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/cryptauth_feature_type.h" #include "ash/services/device_sync/cryptauth_key_bundle.h" #include "ash/services/device_sync/pref_names.h" @@ -14,7 +15,6 @@ #include "base/metrics/histogram_functions.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/gcm_driver/gcm_driver.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/device_sync/cryptauth_group_private_key_sharer_impl.cc b/ash/services/device_sync/cryptauth_group_private_key_sharer_impl.cc index 02797507..b8d9f91 100644 --- a/ash/services/device_sync/cryptauth_group_private_key_sharer_impl.cc +++ b/ash/services/device_sync/cryptauth_group_private_key_sharer_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/async_execution_time_metrics_logger.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/cryptauth_ecies_encryptor_impl.h" @@ -14,7 +15,6 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "crypto/sha2.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_key_bundle.cc b/ash/services/device_sync/cryptauth_key_bundle.cc index eaf20ed..d82ac6c 100644 --- a/ash/services/device_sync/cryptauth_key_bundle.cc +++ b/ash/services/device_sync/cryptauth_key_bundle.cc
@@ -4,12 +4,12 @@ #include "ash/services/device_sync/cryptauth_key_bundle.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/cryptauth_enrollment_constants.h" #include "ash/services/device_sync/value_string_encoding.h" #include "base/containers/contains.h" #include "base/no_destructor.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_key_creator_impl.cc b/ash/services/device_sync/cryptauth_key_creator_impl.cc index d38f3ae..8fd69f8d 100644 --- a/ash/services/device_sync/cryptauth_key_creator_impl.cc +++ b/ash/services/device_sync/cryptauth_key_creator_impl.cc
@@ -4,12 +4,12 @@ #include "ash/services/device_sync/cryptauth_key_creator_impl.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/services/device_sync/cryptauth_enrollment_constants.h" #include "ash/services/device_sync/proto/cryptauth_common.pb.h" #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/strings/string_util.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" #include "crypto/hkdf.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_key_creator_impl_unittest.cc b/ash/services/device_sync/cryptauth_key_creator_impl_unittest.cc index 9572705..ef77593d 100644 --- a/ash/services/device_sync/cryptauth_key_creator_impl_unittest.cc +++ b/ash/services/device_sync/cryptauth_key_creator_impl_unittest.cc
@@ -8,6 +8,8 @@ #include <string> #include <utility> +#include "ash/components/multidevice/fake_secure_message_delegate.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/services/device_sync/cryptauth_enrollment_constants.h" #include "ash/services/device_sync/cryptauth_key.h" #include "ash/services/device_sync/cryptauth_key_bundle.h" @@ -15,8 +17,6 @@ #include "ash/services/device_sync/proto/cryptauth_common.pb.h" #include "base/bind.h" #include "base/containers/flat_map.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" #include "crypto/hkdf.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/device_sync/cryptauth_key_proof_computer_impl.cc b/ash/services/device_sync/cryptauth_key_proof_computer_impl.cc index 8f9bdb75..ffcfca6 100644 --- a/ash/services/device_sync/cryptauth_key_proof_computer_impl.cc +++ b/ash/services/device_sync/cryptauth_key_proof_computer_impl.cc
@@ -6,12 +6,12 @@ #include <vector> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/cryptauth_key.h" #include "ash/services/device_sync/proto/cryptauth_common.pb.h" #include "base/containers/span.h" #include "base/memory/ptr_util.h" #include "base/notreached.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "crypto/ec_private_key.h" #include "crypto/ec_signature_creator.h" #include "crypto/hkdf.h"
diff --git a/ash/services/device_sync/cryptauth_key_registry_impl.cc b/ash/services/device_sync/cryptauth_key_registry_impl.cc index 6ed9706..019dea06 100644 --- a/ash/services/device_sync/cryptauth_key_registry_impl.cc +++ b/ash/services/device_sync/cryptauth_key_registry_impl.cc
@@ -4,9 +4,9 @@ #include "ash/services/device_sync/cryptauth_key_registry_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/pref_names.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/device_sync/cryptauth_metadata_syncer_impl.cc b/ash/services/device_sync/cryptauth_metadata_syncer_impl.cc index 6c26f22..b4c4c715 100644 --- a/ash/services/device_sync/cryptauth_metadata_syncer_impl.cc +++ b/ash/services/device_sync/cryptauth_metadata_syncer_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/async_execution_time_metrics_logger.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/cryptauth_ecies_encryptor_impl.h" @@ -18,7 +19,6 @@ #include "base/containers/contains.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/device_sync/cryptauth_scheduler_impl.cc b/ash/services/device_sync/cryptauth_scheduler_impl.cc index 80b0000c..6dbe3e4 100644 --- a/ash/services/device_sync/cryptauth_scheduler_impl.cc +++ b/ash/services/device_sync/cryptauth_scheduler_impl.cc
@@ -7,12 +7,12 @@ #include <algorithm> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/pref_names.h" #include "ash/services/device_sync/proto/cryptauth_logging.h" #include "ash/services/device_sync/value_string_encoding.h" #include "base/base64.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" #include "components/prefs/pref_registry_simple.h"
diff --git a/ash/services/device_sync/cryptauth_v2_device_manager_impl.cc b/ash/services/device_sync/cryptauth_v2_device_manager_impl.cc index 138854f..5efedf7 100644 --- a/ash/services/device_sync/cryptauth_v2_device_manager_impl.cc +++ b/ash/services/device_sync/cryptauth_v2_device_manager_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/cryptauth_device_syncer_impl.h" #include "ash/services/device_sync/cryptauth_key_registry.h" @@ -13,7 +14,6 @@ #include "ash/services/device_sync/synced_bluetooth_address_tracker_impl.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_v2_device_sync_test_devices.cc b/ash/services/device_sync/cryptauth_v2_device_sync_test_devices.cc index bac87a0..78734f30 100644 --- a/ash/services/device_sync/cryptauth_v2_device_sync_test_devices.cc +++ b/ash/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
@@ -4,14 +4,14 @@ #include "ash/services/device_sync/cryptauth_v2_device_sync_test_devices.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/cryptauth_device.h" #include "ash/services/device_sync/fake_ecies_encryption.h" #include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h" #include "ash/services/device_sync/proto/cryptauth_v2_test_util.h" #include "base/check_op.h" #include "base/no_destructor.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_v2_enroller_impl.cc b/ash/services/device_sync/cryptauth_v2_enroller_impl.cc index 6020e02..f003a38 100644 --- a/ash/services/device_sync/cryptauth_v2_enroller_impl.cc +++ b/ash/services/device_sync/cryptauth_v2_enroller_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/async_execution_time_metrics_logger.h" #include "ash/services/device_sync/cryptauth_client.h" #include "ash/services/device_sync/cryptauth_enrollment_constants.h" @@ -19,7 +20,6 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/no_destructor.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc b/ash/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc index 7204437..a97402c 100644 --- a/ash/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc +++ b/ash/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
@@ -4,6 +4,7 @@ #include "ash/services/device_sync/cryptauth_v2_enrollment_manager_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/cryptauth_enrollment_constants.h" #include "ash/services/device_sync/cryptauth_key_registry.h" #include "ash/services/device_sync/cryptauth_task_metrics_logger.h" @@ -18,7 +19,6 @@ #include "base/strings/string_number_conversions.h" #include "base/time/clock.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/device_sync/device_sync_impl.cc b/ash/services/device_sync/device_sync_impl.cc index 7658896b..ae019da 100644 --- a/ash/services/device_sync/device_sync_impl.cc +++ b/ash/services/device_sync/device_sync_impl.cc
@@ -4,6 +4,8 @@ #include "ash/services/device_sync/device_sync_impl.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/cryptauth_client_impl.h" #include "ash/services/device_sync/cryptauth_device_activity_getter_impl.h" @@ -37,8 +39,6 @@ #include "base/time/default_clock.h" #include "base/timer/timer.h" #include "base/unguessable_token.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/signin/public/base/consent_level.h"
diff --git a/ash/services/device_sync/device_sync_service_unittest.cc b/ash/services/device_sync/device_sync_service_unittest.cc index 8299673..5245a85 100644 --- a/ash/services/device_sync/device_sync_service_unittest.cc +++ b/ash/services/device_sync/device_sync_service_unittest.cc
@@ -7,6 +7,8 @@ #include <tuple> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/secure_message_delegate.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/cryptauth_device_manager_impl.h" #include "ash/services/device_sync/cryptauth_device_registry_impl.h" @@ -50,8 +52,6 @@ #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/secure_message_delegate.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/network/network_handler_test_helper.h" #include "components/gcm_driver/fake_gcm_driver.h"
diff --git a/ash/services/device_sync/fake_cryptauth_feature_status_setter.h b/ash/services/device_sync/fake_cryptauth_feature_status_setter.h index 6a9fc22..5a013db 100644 --- a/ash/services/device_sync/fake_cryptauth_feature_status_setter.h +++ b/ash/services/device_sync/fake_cryptauth_feature_status_setter.h
@@ -9,12 +9,12 @@ #include <string> #include <vector> +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/cryptauth_feature_status_setter.h" #include "ash/services/device_sync/cryptauth_feature_status_setter_impl.h" #include "ash/services/device_sync/network_request_error.h" #include "base/callback.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/software_feature.h" namespace chromeos {
diff --git a/ash/services/device_sync/fake_cryptauth_v2_enroller.cc b/ash/services/device_sync/fake_cryptauth_v2_enroller.cc index 5109ff7c..6b366943 100644 --- a/ash/services/device_sync/fake_cryptauth_v2_enroller.cc +++ b/ash/services/device_sync/fake_cryptauth_v2_enroller.cc
@@ -4,7 +4,7 @@ #include "ash/services/device_sync/fake_cryptauth_v2_enroller.h" -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/fake_device_sync.cc b/ash/services/device_sync/fake_device_sync.cc index c0e088f6..2b1d4ba 100644 --- a/ash/services/device_sync/fake_device_sync.cc +++ b/ash/services/device_sync/fake_device_sync.cc
@@ -6,8 +6,8 @@ #include "ash/services/device_sync/fake_device_sync.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos {
diff --git a/ash/services/device_sync/fake_remote_device_v2_loader.h b/ash/services/device_sync/fake_remote_device_v2_loader.h index 2e405d7..e6af4a2fc 100644 --- a/ash/services/device_sync/fake_remote_device_v2_loader.h +++ b/ash/services/device_sync/fake_remote_device_v2_loader.h
@@ -9,10 +9,10 @@ #include <string> #include <vector> +#include "ash/components/multidevice/remote_device.h" #include "ash/services/device_sync/cryptauth_device_registry.h" #include "ash/services/device_sync/remote_device_v2_loader.h" #include "ash/services/device_sync/remote_device_v2_loader_impl.h" -#include "chromeos/components/multidevice/remote_device.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos {
diff --git a/ash/services/device_sync/fake_software_feature_manager.h b/ash/services/device_sync/fake_software_feature_manager.h index ba096ea..97240c94 100644 --- a/ash/services/device_sync/fake_software_feature_manager.h +++ b/ash/services/device_sync/fake_software_feature_manager.h
@@ -9,11 +9,11 @@ #include <string> #include <vector> +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/device_sync/network_request_error.h" #include "ash/services/device_sync/software_feature_manager.h" #include "base/callback.h" -#include "chromeos/components/multidevice/software_feature.h" namespace chromeos {
diff --git a/ash/services/device_sync/public/cpp/BUILD.gn b/ash/services/device_sync/public/cpp/BUILD.gn index 2cb3508..857cdfe9 100644 --- a/ash/services/device_sync/public/cpp/BUILD.gn +++ b/ash/services/device_sync/public/cpp/BUILD.gn
@@ -20,12 +20,12 @@ ] public_deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/constants", "//ash/services/device_sync:feature_status_change", "//ash/services/device_sync/public/mojom", "//base", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", ] deps = [ "//mojo/public/cpp/bindings" ] @@ -38,12 +38,12 @@ ] public_deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/constants", "//ash/services/device_sync:feature_status_change", "//ash/services/device_sync/public/mojom", "//base", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", ] deps = [ @@ -78,12 +78,12 @@ ":cpp", ":prefs", ":test_support", + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", "//ash/services/device_sync", "//ash/services/device_sync:test_support", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", "//components/gcm_driver:test_support", "//components/prefs:test_support", "//components/signin/public/identity_manager:test_support",
diff --git a/ash/services/device_sync/public/cpp/device_sync_client.cc b/ash/services/device_sync/public/cpp/device_sync_client.cc index 1874444f..57ea220 100644 --- a/ash/services/device_sync/public/cpp/device_sync_client.cc +++ b/ash/services/device_sync/public/cpp/device_sync_client.cc
@@ -4,7 +4,7 @@ #include "ash/services/device_sync/public/cpp/device_sync_client.h" -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/public/cpp/device_sync_client.h b/ash/services/device_sync/public/cpp/device_sync_client.h index c6c7760..0274d6fa 100644 --- a/ash/services/device_sync/public/cpp/device_sync_client.h +++ b/ash/services/device_sync/public/cpp/device_sync_client.h
@@ -9,14 +9,14 @@ #include <string> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/device_sync/proto/cryptauth_common.pb.h" #include "ash/services/device_sync/public/mojom/device_sync.mojom.h" #include "base/callback.h" #include "base/memory/scoped_refptr.h" #include "base/observer_list.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/software_feature.h" #include "mojo/public/cpp/bindings/remote.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/device_sync/public/cpp/device_sync_client_impl.cc b/ash/services/device_sync/public/cpp/device_sync_client_impl.cc index f1e2d85b..ece8d9a 100644 --- a/ash/services/device_sync/public/cpp/device_sync_client_impl.cc +++ b/ash/services/device_sync/public/cpp/device_sync_client_impl.cc
@@ -8,14 +8,14 @@ #include <utility> #include <vector> +#include "ash/components/multidevice/expiring_remote_device_cache.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/public/mojom/device_sync.mojom.h" #include "base/base64url.h" #include "base/bind.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/components/multidevice/expiring_remote_device_cache.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device.h" namespace chromeos {
diff --git a/ash/services/device_sync/public/cpp/device_sync_client_impl.h b/ash/services/device_sync/public/cpp/device_sync_client_impl.h index dd1c58a1..e17ce0e 100644 --- a/ash/services/device_sync/public/cpp/device_sync_client_impl.h +++ b/ash/services/device_sync/public/cpp/device_sync_client_impl.h
@@ -9,6 +9,8 @@ #include <string> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/device_sync/proto/cryptauth_common.pb.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" @@ -16,8 +18,6 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/software_feature.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h"
diff --git a/ash/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc b/ash/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc index 2af5180..eb3f0b53 100644 --- a/ash/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc +++ b/ash/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
@@ -10,6 +10,8 @@ #include <utility> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/device_sync_impl.h" #include "ash/services/device_sync/fake_device_sync.h" #include "ash/services/device_sync/feature_status_change.h" @@ -26,8 +28,6 @@ #include "base/test/null_task_runner.h" #include "base/test/task_environment.h" #include "base/test/test_simple_task_runner.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature.h" #include "components/gcm_driver/fake_gcm_driver.h" #include "components/prefs/testing_pref_service.h" #include "components/signin/public/identity_manager/identity_test_environment.h"
diff --git a/ash/services/device_sync/public/cpp/fake_device_sync_client.cc b/ash/services/device_sync/public/cpp/fake_device_sync_client.cc index dcef4a3..ec796c1 100644 --- a/ash/services/device_sync/public/cpp/fake_device_sync_client.cc +++ b/ash/services/device_sync/public/cpp/fake_device_sync_client.cc
@@ -4,8 +4,8 @@ #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device_cache.h" namespace chromeos {
diff --git a/ash/services/device_sync/public/cpp/fake_device_sync_client.h b/ash/services/device_sync/public/cpp/fake_device_sync_client.h index d8b2396..abfc085 100644 --- a/ash/services/device_sync/public/cpp/fake_device_sync_client.h +++ b/ash/services/device_sync/public/cpp/fake_device_sync_client.h
@@ -9,14 +9,14 @@ #include <string> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/device_sync/proto/cryptauth_common.pb.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/device_sync/public/mojom/device_sync.mojom.h" #include "base/callback.h" #include "base/containers/circular_deque.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/software_feature.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos {
diff --git a/ash/services/device_sync/public/mojom/BUILD.gn b/ash/services/device_sync/public/mojom/BUILD.gn index 826abb02..adbc7ec 100644 --- a/ash/services/device_sync/public/mojom/BUILD.gn +++ b/ash/services/device_sync/public/mojom/BUILD.gn
@@ -11,7 +11,7 @@ sources = [ "device_sync.mojom" ] public_deps = [ - "//chromeos/components/multidevice/mojom", + "//ash/components/multidevice/mojom", "//mojo/public/mojom/base", ]
diff --git a/ash/services/device_sync/public/mojom/device_sync.mojom b/ash/services/device_sync/public/mojom/device_sync.mojom index 2719a50..366d8ab 100644 --- a/ash/services/device_sync/public/mojom/device_sync.mojom +++ b/ash/services/device_sync/public/mojom/device_sync.mojom
@@ -4,7 +4,7 @@ module chromeos.device_sync.mojom; -import "chromeos/components/multidevice/mojom/multidevice_types.mojom"; +import "ash/components/multidevice/mojom/multidevice_types.mojom"; import "mojo/public/mojom/base/time.mojom"; // Denotes the outcome of an HTTP request, where results like kBadRequest map
diff --git a/ash/services/device_sync/remote_device_loader.cc b/ash/services/device_sync/remote_device_loader.cc index 4a6d498c..00a2c8ea 100644 --- a/ash/services/device_sync/remote_device_loader.cc +++ b/ash/services/device_sync/remote_device_loader.cc
@@ -7,14 +7,14 @@ #include <algorithm> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/secure_message_delegate.h" +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/proto/enum_util.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/secure_message_delegate.h" -#include "chromeos/components/multidevice/software_feature.h" namespace chromeos {
diff --git a/ash/services/device_sync/remote_device_loader.h b/ash/services/device_sync/remote_device_loader.h index 2b531f5..1e56b2b7 100644 --- a/ash/services/device_sync/remote_device_loader.h +++ b/ash/services/device_sync/remote_device_loader.h
@@ -8,10 +8,10 @@ #include <memory> #include <string> +#include "ash/components/multidevice/remote_device.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/remote_device.h" namespace chromeos {
diff --git a/ash/services/device_sync/remote_device_loader_unittest.cc b/ash/services/device_sync/remote_device_loader_unittest.cc index dc45c81..2a16873 100644 --- a/ash/services/device_sync/remote_device_loader_unittest.cc +++ b/ash/services/device_sync/remote_device_loader_unittest.cc
@@ -9,9 +9,9 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/fake_secure_message_delegate.h" #include "ash/services/device_sync/proto/enum_util.h" #include "base/bind.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/device_sync/remote_device_provider.h b/ash/services/device_sync/remote_device_provider.h index eba0aa69..b1b1961 100644 --- a/ash/services/device_sync/remote_device_provider.h +++ b/ash/services/device_sync/remote_device_provider.h
@@ -5,8 +5,8 @@ #ifndef ASH_SERVICES_DEVICE_SYNC_REMOTE_DEVICE_PROVIDER_H_ #define ASH_SERVICES_DEVICE_SYNC_REMOTE_DEVICE_PROVIDER_H_ +#include "ash/components/multidevice/remote_device.h" #include "base/observer_list.h" -#include "chromeos/components/multidevice/remote_device.h" namespace chromeos {
diff --git a/ash/services/device_sync/remote_device_provider_impl.cc b/ash/services/device_sync/remote_device_provider_impl.cc index 078a6cf0..3936337 100644 --- a/ash/services/device_sync/remote_device_provider_impl.cc +++ b/ash/services/device_sync/remote_device_provider_impl.cc
@@ -6,6 +6,8 @@ #include <algorithm> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/remote_device_loader.h" #include "ash/services/device_sync/remote_device_v2_loader_impl.h" @@ -13,8 +15,6 @@ #include "base/feature_list.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" namespace chromeos {
diff --git a/ash/services/device_sync/remote_device_provider_impl_unittest.cc b/ash/services/device_sync/remote_device_provider_impl_unittest.cc index 9b03028..c684eae 100644 --- a/ash/services/device_sync/remote_device_provider_impl_unittest.cc +++ b/ash/services/device_sync/remote_device_provider_impl_unittest.cc
@@ -7,6 +7,10 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/beacon_seed.h" +#include "ash/components/multidevice/fake_secure_message_delegate.h" +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/cryptauth_device.h" #include "ash/services/device_sync/cryptauth_device_manager.h" @@ -22,10 +26,6 @@ #include "base/memory/ptr_util.h" #include "base/no_destructor.h" #include "base/test/scoped_feature_list.h" -#include "chromeos/components/multidevice/beacon_seed.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos {
diff --git a/ash/services/device_sync/remote_device_v2_loader.h b/ash/services/device_sync/remote_device_v2_loader.h index 9c18209..ec3aef2 100644 --- a/ash/services/device_sync/remote_device_v2_loader.h +++ b/ash/services/device_sync/remote_device_v2_loader.h
@@ -7,9 +7,9 @@ #include <string> +#include "ash/components/multidevice/remote_device.h" #include "ash/services/device_sync/cryptauth_device_registry.h" #include "base/callback.h" -#include "chromeos/components/multidevice/remote_device.h" namespace chromeos {
diff --git a/ash/services/device_sync/remote_device_v2_loader_impl.cc b/ash/services/device_sync/remote_device_v2_loader_impl.cc index 0bd9d77..7ee067c 100644 --- a/ash/services/device_sync/remote_device_v2_loader_impl.cc +++ b/ash/services/device_sync/remote_device_v2_loader_impl.cc
@@ -6,13 +6,13 @@ #include <utility> +#include "ash/components/multidevice/beacon_seed.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/services/device_sync/async_execution_time_metrics_logger.h" #include "ash/services/device_sync/cryptauth_task_metrics_logger.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/beacon_seed.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" namespace chromeos {
diff --git a/ash/services/device_sync/remote_device_v2_loader_impl.h b/ash/services/device_sync/remote_device_v2_loader_impl.h index 5141bab7..e9370f7 100644 --- a/ash/services/device_sync/remote_device_v2_loader_impl.h +++ b/ash/services/device_sync/remote_device_v2_loader_impl.h
@@ -8,12 +8,12 @@ #include <memory> #include <string> +#include "ash/components/multidevice/remote_device.h" #include "ash/services/device_sync/cryptauth_device.h" #include "ash/services/device_sync/cryptauth_device_registry.h" #include "ash/services/device_sync/remote_device_v2_loader.h" #include "base/callback.h" #include "base/containers/flat_set.h" -#include "chromeos/components/multidevice/remote_device.h" namespace chromeos {
diff --git a/ash/services/device_sync/remote_device_v2_loader_impl_unittest.cc b/ash/services/device_sync/remote_device_v2_loader_impl_unittest.cc index a4a2cae..93f1e3e4 100644 --- a/ash/services/device_sync/remote_device_v2_loader_impl_unittest.cc +++ b/ash/services/device_sync/remote_device_v2_loader_impl_unittest.cc
@@ -7,13 +7,13 @@ #include <string> #include <utility> +#include "ash/components/multidevice/fake_secure_message_delegate.h" +#include "ash/components/multidevice/remote_device.h" #include "ash/services/device_sync/cryptauth_device.h" #include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h" #include "ash/services/device_sync/proto/cryptauth_v2_test_util.h" #include "ash/services/device_sync/remote_device_v2_loader_impl.h" #include "base/bind.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" -#include "chromeos/components/multidevice/remote_device.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos {
diff --git a/ash/services/device_sync/software_feature_manager.h b/ash/services/device_sync/software_feature_manager.h index 2121842..07707c4 100644 --- a/ash/services/device_sync/software_feature_manager.h +++ b/ash/services/device_sync/software_feature_manager.h
@@ -7,10 +7,10 @@ #include <string> +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/device_sync/network_request_error.h" #include "base/callback.h" -#include "chromeos/components/multidevice/software_feature.h" namespace chromeos {
diff --git a/ash/services/device_sync/software_feature_manager_impl.h b/ash/services/device_sync/software_feature_manager_impl.h index ebe7653..f724801b 100644 --- a/ash/services/device_sync/software_feature_manager_impl.h +++ b/ash/services/device_sync/software_feature_manager_impl.h
@@ -9,6 +9,7 @@ #include <string> #include <vector> +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/device_sync/network_request_error.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" @@ -17,7 +18,6 @@ #include "base/callback_forward.h" #include "base/containers/queue.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/software_feature.h" namespace chromeos {
diff --git a/ash/services/device_sync/software_feature_manager_impl_unittest.cc b/ash/services/device_sync/software_feature_manager_impl_unittest.cc index 33d9a887..9afc3430 100644 --- a/ash/services/device_sync/software_feature_manager_impl_unittest.cc +++ b/ash/services/device_sync/software_feature_manager_impl_unittest.cc
@@ -4,14 +4,14 @@ #include "ash/services/device_sync/software_feature_manager_impl.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/fake_cryptauth_feature_status_setter.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/device_sync/mock_cryptauth_client.h" #include "ash/services/device_sync/proto/enum_util.h" #include "base/bind.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/device_sync/stub_device_sync.cc b/ash/services/device_sync/stub_device_sync.cc index 0cf2306..497d2af 100644 --- a/ash/services/device_sync/stub_device_sync.cc +++ b/ash/services/device_sync/stub_device_sync.cc
@@ -7,14 +7,14 @@ #include <utility> #include <vector> +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/stub_multidevice_util.h" #include "ash/services/device_sync/device_sync_base.h" #include "ash/services/device_sync/device_sync_impl.h" #include "ash/services/device_sync/public/mojom/device_sync.mojom.h" #include "base/memory/ptr_util.h" #include "base/no_destructor.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/stub_multidevice_util.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/device_sync/sync_scheduler.cc b/ash/services/device_sync/sync_scheduler.cc index 30ff26c3..9ff672d 100644 --- a/ash/services/device_sync/sync_scheduler.cc +++ b/ash/services/device_sync/sync_scheduler.cc
@@ -4,7 +4,7 @@ #include "ash/services/device_sync/sync_scheduler.h" -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/sync_scheduler_impl.cc b/ash/services/device_sync/sync_scheduler_impl.cc index 36fe043..ccc2e1f 100644 --- a/ash/services/device_sync/sync_scheduler_impl.cc +++ b/ash/services/device_sync/sync_scheduler_impl.cc
@@ -9,11 +9,11 @@ #include <limits> #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/numerics/safe_conversions.h" #include "base/rand_util.h" #include "base/strings/stringprintf.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/device_sync/synced_bluetooth_address_tracker_impl.cc b/ash/services/device_sync/synced_bluetooth_address_tracker_impl.cc index 286f913..f30756d0 100644 --- a/ash/services/device_sync/synced_bluetooth_address_tracker_impl.cc +++ b/ash/services/device_sync/synced_bluetooth_address_tracker_impl.cc
@@ -6,13 +6,13 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/cryptauth_scheduler.h" #include "ash/services/device_sync/pref_names.h" #include "ash/services/device_sync/proto/cryptauth_common.pb.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "device/bluetooth/bluetooth_adapter_factory.h"
diff --git a/ash/services/device_sync/value_string_encoding.cc b/ash/services/device_sync/value_string_encoding.cc index a2cf3b25..0662454 100644 --- a/ash/services/device_sync/value_string_encoding.cc +++ b/ash/services/device_sync/value_string_encoding.cc
@@ -4,8 +4,8 @@ #include "ash/services/device_sync/value_string_encoding.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/base64url.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/ash/services/multidevice_setup/BUILD.gn b/ash/services/multidevice_setup/BUILD.gn index d4232a5..e7192c18 100644 --- a/ash/services/multidevice_setup/BUILD.gn +++ b/ash/services/multidevice_setup/BUILD.gn
@@ -60,6 +60,8 @@ ] deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/constants", "//ash/services/device_sync/proto:util", "//ash/services/device_sync/public/cpp", @@ -73,8 +75,6 @@ "//ash/services/secure_channel/public/cpp/client", "//ash/services/secure_channel/public/mojom", "//base", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", "//components/pref_registry", "//components/prefs:prefs", "//components/session_manager/core", @@ -122,9 +122,9 @@ deps = [ ":multidevice_setup", + "//ash/components/multidevice", "//ash/services/multidevice_setup/public/mojom", "//base", - "//chromeos/components/multidevice", "//testing/gmock", "//testing/gtest", ] @@ -153,6 +153,8 @@ deps = [ ":multidevice_setup", ":test_support", + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", "//ash/constants", "//ash/services/device_sync/public/cpp:test_support", "//ash/services/multidevice_setup/public/cpp:oobe_completion_tracker", @@ -163,8 +165,6 @@ "//ash/services/secure_channel/public/cpp/client:test_support", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", "//components/session_manager/core", "//components/sync_preferences:test_support", "//testing/gmock",
diff --git a/ash/services/multidevice_setup/DEPS b/ash/services/multidevice_setup/DEPS index 929d3e4..9e98753 100644 --- a/ash/services/multidevice_setup/DEPS +++ b/ash/services/multidevice_setup/DEPS
@@ -1,4 +1,5 @@ include_rules = [ + "+ash/components/multidevice", "+ash/constants", "+ash/services/device_sync", "+components/keyed_service/core",
diff --git a/ash/services/multidevice_setup/OWNERS b/ash/services/multidevice_setup/OWNERS index 7027ab73..18377141 100644 --- a/ash/services/multidevice_setup/OWNERS +++ b/ash/services/multidevice_setup/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc b/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc index 67d00745..a755e613 100644 --- a/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc +++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc
@@ -4,8 +4,8 @@ #include "ash/services/multidevice_setup/account_status_change_delegate_notifier.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/logging.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc index bfdbd64..21eefeb 100644 --- a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc +++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc
@@ -7,11 +7,11 @@ #include <set> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/multidevice_setup/host_device_timestamp_manager.h" #include "ash/services/multidevice_setup/host_status_provider_impl.h" #include "base/memory/ptr_util.h" #include "base/time/clock.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc index f746571..6d45ffe 100644 --- a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc +++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc
@@ -7,6 +7,8 @@ #include <string> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/fake_account_status_change_delegate.h" #include "ash/services/multidevice_setup/fake_host_device_timestamp_manager.h" @@ -16,8 +18,6 @@ #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc b/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc index 8a0b6af..dee3f0e 100644 --- a/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc +++ b/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc
@@ -4,11 +4,11 @@ #include "ash/services/multidevice_setup/android_sms_app_installing_status_observer.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/multidevice_setup/host_status_provider.h" #include "ash/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc b/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc index daef06b..28c4886 100644 --- a/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc +++ b/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
@@ -6,11 +6,11 @@ #include <string> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/multidevice_setup/fake_feature_state_manager.h" #include "ash/services/multidevice_setup/fake_host_status_provider.h" #include "ash/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/multidevice_setup/device_with_connectivity_status.h b/ash/services/multidevice_setup/device_with_connectivity_status.h index 50fc6f9..6719e406 100644 --- a/ash/services/multidevice_setup/device_with_connectivity_status.h +++ b/ash/services/multidevice_setup/device_with_connectivity_status.h
@@ -9,8 +9,8 @@ #include <string> #include <vector> +#include "ash/components/multidevice/remote_device.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" -#include "chromeos/components/multidevice/remote_device.h" namespace ash {
diff --git a/ash/services/multidevice_setup/eligible_host_devices_provider.h b/ash/services/multidevice_setup/eligible_host_devices_provider.h index 414ef785..c0a3b1a3 100644 --- a/ash/services/multidevice_setup/eligible_host_devices_provider.h +++ b/ash/services/multidevice_setup/eligible_host_devices_provider.h
@@ -5,8 +5,8 @@ #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_ELIGIBLE_HOST_DEVICES_PROVIDER_H_ #define ASH_SERVICES_MULTIDEVICE_SETUP_ELIGIBLE_HOST_DEVICES_PROVIDER_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/multidevice_setup/device_with_connectivity_status.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/services/multidevice_setup/eligible_host_devices_provider_impl.cc b/ash/services/multidevice_setup/eligible_host_devices_provider_impl.cc index 12e46e7..2e8c103 100644 --- a/ash/services/multidevice_setup/eligible_host_devices_provider_impl.cc +++ b/ash/services/multidevice_setup/eligible_host_devices_provider_impl.cc
@@ -4,11 +4,11 @@ #include "ash/services/multidevice_setup/eligible_host_devices_provider_impl.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/constants/ash_features.h" #include "base/feature_list.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" namespace ash {
diff --git a/ash/services/multidevice_setup/eligible_host_devices_provider_impl.h b/ash/services/multidevice_setup/eligible_host_devices_provider_impl.h index 807c94b..e36032e1 100644 --- a/ash/services/multidevice_setup/eligible_host_devices_provider_impl.h +++ b/ash/services/multidevice_setup/eligible_host_devices_provider_impl.h
@@ -5,9 +5,9 @@ #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_ELIGIBLE_HOST_DEVICES_PROVIDER_IMPL_H_ #define ASH_SERVICES_MULTIDEVICE_SETUP_ELIGIBLE_HOST_DEVICES_PROVIDER_IMPL_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/multidevice_setup/eligible_host_devices_provider.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc b/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc index 3a67a839..22ab028 100644 --- a/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc +++ b/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc
@@ -6,6 +6,9 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" @@ -13,9 +16,6 @@ #include "base/containers/flat_set.h" #include "base/test/scoped_feature_list.h" #include "base/time/time_override.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash {
diff --git a/ash/services/multidevice_setup/fake_eligible_host_devices_provider.h b/ash/services/multidevice_setup/fake_eligible_host_devices_provider.h index ba0666d..3a7a5032 100644 --- a/ash/services/multidevice_setup/fake_eligible_host_devices_provider.h +++ b/ash/services/multidevice_setup/fake_eligible_host_devices_provider.h
@@ -5,8 +5,8 @@ #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_FAKE_ELIGIBLE_HOST_DEVICES_PROVIDER_H_ #define ASH_SERVICES_MULTIDEVICE_SETUP_FAKE_ELIGIBLE_HOST_DEVICES_PROVIDER_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/multidevice_setup/eligible_host_devices_provider.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/services/multidevice_setup/fake_host_backend_delegate.h b/ash/services/multidevice_setup/fake_host_backend_delegate.h index 23b7ee02a..71527215 100644 --- a/ash/services/multidevice_setup/fake_host_backend_delegate.h +++ b/ash/services/multidevice_setup/fake_host_backend_delegate.h
@@ -5,8 +5,8 @@ #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_BACKEND_DELEGATE_H_ #define ASH_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_BACKEND_DELEGATE_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/multidevice_setup/host_backend_delegate.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash {
diff --git a/ash/services/multidevice_setup/feature_state_manager.cc b/ash/services/multidevice_setup/feature_state_manager.cc index 6fc2ce15..7806639 100644 --- a/ash/services/multidevice_setup/feature_state_manager.cc +++ b/ash/services/multidevice_setup/feature_state_manager.cc
@@ -4,7 +4,7 @@ #include "ash/services/multidevice_setup/feature_state_manager.h" -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/services/multidevice_setup/feature_state_manager_impl.cc b/ash/services/multidevice_setup/feature_state_manager_impl.cc index 2566eb1..e823a4f 100644 --- a/ash/services/multidevice_setup/feature_state_manager_impl.cc +++ b/ash/services/multidevice_setup/feature_state_manager_impl.cc
@@ -6,6 +6,9 @@ #include <array> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/software_feature.h" #include "ash/constants/ash_features.h" #include "ash/services/multidevice_setup/global_state_feature_manager.h" #include "ash/services/multidevice_setup/public/cpp/prefs.h" @@ -16,9 +19,6 @@ #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/software_feature.h" #include "components/prefs/pref_service.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc b/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc index 8498e4e2..ba0ccce 100644 --- a/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc +++ b/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/fake_feature_state_manager.h" @@ -17,7 +18,6 @@ #include "base/containers/contains.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/multidevice_setup/global_state_feature_manager_impl.cc b/ash/services/multidevice_setup/global_state_feature_manager_impl.cc index 564213c..a7067fd 100644 --- a/ash/services/multidevice_setup/global_state_feature_manager_impl.cc +++ b/ash/services/multidevice_setup/global_state_feature_manager_impl.cc
@@ -9,6 +9,10 @@ #include <string> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" @@ -24,10 +28,6 @@ #include "base/metrics/histogram_functions.h" #include "base/time/time.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/multidevice_setup/global_state_feature_manager_impl.h b/ash/services/multidevice_setup/global_state_feature_manager_impl.h index 550d18e..2cdf99a 100644 --- a/ash/services/multidevice_setup/global_state_feature_manager_impl.h +++ b/ash/services/multidevice_setup/global_state_feature_manager_impl.h
@@ -8,6 +8,8 @@ #include <memory> #include <string> +// TODO(https://crbug.com/1164001): move to forward declaration +#include "ash/components/multidevice/software_feature.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/device_sync/public/mojom/device_sync.mojom.h" #include "ash/services/multidevice_setup/global_state_feature_manager.h" @@ -15,8 +17,6 @@ #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/memory/weak_ptr.h" #include "base/timer/timer.h" -// TODO(https://crbug.com/1164001): move to forward declaration -#include "chromeos/components/multidevice/software_feature.h" class PrefRegistrySimple; class PrefService;
diff --git a/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc b/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc index a027db67..b47c9964 100644 --- a/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc +++ b/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc
@@ -8,6 +8,10 @@ #include <utility> #include <vector> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/fake_host_status_provider.h" @@ -18,10 +22,6 @@ #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.cc b/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.cc index 3f872c7..6384fc45 100644 --- a/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.cc +++ b/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.cc
@@ -4,12 +4,12 @@ #include "ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler_unittest.cc b/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler_unittest.cc index 77091d8..69e41e27 100644 --- a/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler_unittest.cc +++ b/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler_unittest.cc
@@ -6,11 +6,11 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/fake_host_backend_delegate.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/multidevice_setup/host_backend_delegate.h b/ash/services/multidevice_setup/host_backend_delegate.h index 980439b..989c162 100644 --- a/ash/services/multidevice_setup/host_backend_delegate.h +++ b/ash/services/multidevice_setup/host_backend_delegate.h
@@ -5,8 +5,8 @@ #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_H_ #define ASH_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "base/observer_list.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash {
diff --git a/ash/services/multidevice_setup/host_backend_delegate_impl.cc b/ash/services/multidevice_setup/host_backend_delegate_impl.cc index b6fea4c02..187bc9c 100644 --- a/ash/services/multidevice_setup/host_backend_delegate_impl.cc +++ b/ash/services/multidevice_setup/host_backend_delegate_impl.cc
@@ -7,15 +7,15 @@ #include <algorithm> #include <sstream> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/feature_status_change.h" #include "ash/services/multidevice_setup/eligible_host_devices_provider.h" #include "base/bind.h" #include "base/containers/contains.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/multidevice_setup/host_backend_delegate_impl.h b/ash/services/multidevice_setup/host_backend_delegate_impl.h index c3d57f90..f5378b0 100644 --- a/ash/services/multidevice_setup/host_backend_delegate_impl.h +++ b/ash/services/multidevice_setup/host_backend_delegate_impl.h
@@ -5,11 +5,11 @@ #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_IMPL_H_ #define ASH_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_IMPL_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/multidevice_setup/host_backend_delegate.h" #include "base/memory/weak_ptr.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" class PrefRegistrySimple;
diff --git a/ash/services/multidevice_setup/host_backend_delegate_impl_unittest.cc b/ash/services/multidevice_setup/host_backend_delegate_impl_unittest.cc index f22b999..b84ae660 100644 --- a/ash/services/multidevice_setup/host_backend_delegate_impl_unittest.cc +++ b/ash/services/multidevice_setup/host_backend_delegate_impl_unittest.cc
@@ -6,6 +6,9 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/fake_eligible_host_devices_provider.h" @@ -14,9 +17,6 @@ #include "base/test/scoped_feature_list.h" #include "base/timer/mock_timer.h" #include "base/unguessable_token.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc b/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc index a4e76a9..c08cc40 100644 --- a/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc +++ b/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc
@@ -4,9 +4,9 @@ #include "ash/services/multidevice_setup/host_device_timestamp_manager_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/memory/ptr_util.h" #include "base/time/clock.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc b/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc index 6f3a95d..43c1e93 100644 --- a/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc +++ b/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc
@@ -6,10 +6,10 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/multidevice_setup/fake_host_status_provider.h" #include "base/test/simple_test_clock.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/multidevice_setup/host_status_provider.cc b/ash/services/multidevice_setup/host_status_provider.cc index b55f2b1..f775f5ef 100644 --- a/ash/services/multidevice_setup/host_status_provider.cc +++ b/ash/services/multidevice_setup/host_status_provider.cc
@@ -4,8 +4,8 @@ #include "ash/services/multidevice_setup/host_status_provider.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/logging.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/services/multidevice_setup/host_status_provider.h b/ash/services/multidevice_setup/host_status_provider.h index a8b831b1..1be8c78 100644 --- a/ash/services/multidevice_setup/host_status_provider.h +++ b/ash/services/multidevice_setup/host_status_provider.h
@@ -5,9 +5,9 @@ #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_HOST_STATUS_PROVIDER_H_ #define ASH_SERVICES_MULTIDEVICE_SETUP_HOST_STATUS_PROVIDER_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/observer_list.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash {
diff --git a/ash/services/multidevice_setup/host_status_provider_impl.cc b/ash/services/multidevice_setup/host_status_provider_impl.cc index cd5a6b6..930d877 100644 --- a/ash/services/multidevice_setup/host_status_provider_impl.cc +++ b/ash/services/multidevice_setup/host_status_provider_impl.cc
@@ -6,12 +6,12 @@ #include <algorithm> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/multidevice_setup/eligible_host_devices_provider.h" #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/services/multidevice_setup/host_status_provider_impl.h b/ash/services/multidevice_setup/host_status_provider_impl.h index cef7eeb1..0103ffc 100644 --- a/ash/services/multidevice_setup/host_status_provider_impl.h +++ b/ash/services/multidevice_setup/host_status_provider_impl.h
@@ -5,12 +5,12 @@ #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_HOST_STATUS_PROVIDER_IMPL_H_ #define ASH_SERVICES_MULTIDEVICE_SETUP_HOST_STATUS_PROVIDER_IMPL_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/multidevice_setup/host_backend_delegate.h" #include "ash/services/multidevice_setup/host_status_provider.h" #include "ash/services/multidevice_setup/host_verifier.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash {
diff --git a/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc b/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc index c75f9e9..65913f6 100644 --- a/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc +++ b/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/fake_eligible_host_devices_provider.h" #include "ash/services/multidevice_setup/fake_host_backend_delegate.h" @@ -13,7 +14,6 @@ #include "ash/services/multidevice_setup/fake_host_verifier.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash {
diff --git a/ash/services/multidevice_setup/host_verifier.cc b/ash/services/multidevice_setup/host_verifier.cc index a4bf4d8..77b8e1b 100644 --- a/ash/services/multidevice_setup/host_verifier.cc +++ b/ash/services/multidevice_setup/host_verifier.cc
@@ -4,8 +4,8 @@ #include "ash/services/multidevice_setup/host_verifier.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/logging.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/services/multidevice_setup/host_verifier_impl.cc b/ash/services/multidevice_setup/host_verifier_impl.cc index 9fe20d6..2b4e309f 100644 --- a/ash/services/multidevice_setup/host_verifier_impl.cc +++ b/ash/services/multidevice_setup/host_verifier_impl.cc
@@ -6,6 +6,8 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/software_feature.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/proto/cryptauth_common.pb.h" #include "base/bind.h" @@ -13,8 +15,6 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/software_feature.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/services/multidevice_setup/host_verifier_impl.h b/ash/services/multidevice_setup/host_verifier_impl.h index 7550277..258d5b9 100644 --- a/ash/services/multidevice_setup/host_verifier_impl.h +++ b/ash/services/multidevice_setup/host_verifier_impl.h
@@ -7,13 +7,13 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/multidevice_setup/host_backend_delegate.h" #include "ash/services/multidevice_setup/host_verifier.h" #include "base/memory/weak_ptr.h" #include "base/time/default_clock.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/remote_device_ref.h" class PrefRegistrySimple; class PrefService;
diff --git a/ash/services/multidevice_setup/host_verifier_impl_unittest.cc b/ash/services/multidevice_setup/host_verifier_impl_unittest.cc index d778b95..8f50786 100644 --- a/ash/services/multidevice_setup/host_verifier_impl_unittest.cc +++ b/ash/services/multidevice_setup/host_verifier_impl_unittest.cc
@@ -8,6 +8,9 @@ #include <string> #include <utility> +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/proto/cryptauth_common.pb.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" @@ -16,9 +19,6 @@ #include "base/test/scoped_feature_list.h" #include "base/test/simple_test_clock.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/multidevice_setup/multidevice_setup_impl.cc b/ash/services/multidevice_setup/multidevice_setup_impl.cc index a9e8177..511734c 100644 --- a/ash/services/multidevice_setup/multidevice_setup_impl.cc +++ b/ash/services/multidevice_setup/multidevice_setup_impl.cc
@@ -7,6 +7,7 @@ #include "ash/services/multidevice_setup/multidevice_setup_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/constants/ash_features.h" #include "ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.h" #include "ash/services/multidevice_setup/android_sms_app_installing_status_observer.h" @@ -31,7 +32,6 @@ #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/time/default_clock.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc b/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc index ff35582..3f7bdbf8 100644 --- a/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc +++ b/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/device_sync/public/cpp/fake_gcm_device_info_provider.h" @@ -43,7 +44,6 @@ #include "base/run_loop.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/multidevice_setup/multidevice_setup_initializer.cc b/ash/services/multidevice_setup/multidevice_setup_initializer.cc index 451687c..d3d0870 100644 --- a/ash/services/multidevice_setup/multidevice_setup_initializer.cc +++ b/ash/services/multidevice_setup/multidevice_setup_initializer.cc
@@ -6,12 +6,12 @@ #include "ash/services/multidevice_setup/multidevice_setup_initializer.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/multidevice_setup/multidevice_setup_impl.h" #include "ash/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h" #include "ash/services/multidevice_setup/public/cpp/android_sms_pairing_state_tracker.h" #include "base/check.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/services/multidevice_setup/multidevice_setup_service.cc b/ash/services/multidevice_setup/multidevice_setup_service.cc index 74f2542db..35fd78ef 100644 --- a/ash/services/multidevice_setup/multidevice_setup_service.cc +++ b/ash/services/multidevice_setup/multidevice_setup_service.cc
@@ -4,6 +4,7 @@ #include "ash/services/multidevice_setup/multidevice_setup_service.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.h" #include "ash/services/multidevice_setup/android_sms_app_installing_status_observer.h" #include "ash/services/multidevice_setup/global_state_feature_manager_impl.h" @@ -19,7 +20,6 @@ #include "ash/services/multidevice_setup/public/cpp/prefs.h" #include "ash/services/multidevice_setup/wifi_sync_notification_controller.h" #include "base/bind.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc b/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc index 914c275a..9b269b35 100644 --- a/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc +++ b/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc
@@ -4,6 +4,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/device_sync/public/cpp/fake_gcm_device_info_provider.h" #include "ash/services/multidevice_setup/fake_account_status_change_delegate.h" @@ -21,7 +22,6 @@ #include "base/callback_helpers.h" #include "base/run_loop.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "mojo/public/cpp/bindings/remote.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/multidevice_setup/privileged_host_device_setter_impl.cc b/ash/services/multidevice_setup/privileged_host_device_setter_impl.cc index ed4e02d3..ef0e2b9 100644 --- a/ash/services/multidevice_setup/privileged_host_device_setter_impl.cc +++ b/ash/services/multidevice_setup/privileged_host_device_setter_impl.cc
@@ -4,9 +4,9 @@ #include "ash/services/multidevice_setup/privileged_host_device_setter_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/multidevice_setup/multidevice_setup_base.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/services/multidevice_setup/public/cpp/BUILD.gn b/ash/services/multidevice_setup/public/cpp/BUILD.gn index 44971d2..5af24e7 100644 --- a/ash/services/multidevice_setup/public/cpp/BUILD.gn +++ b/ash/services/multidevice_setup/public/cpp/BUILD.gn
@@ -15,10 +15,10 @@ ] public_deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/services/multidevice_setup/public/mojom", "//base", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", "//mojo/public/cpp/bindings", ] } @@ -135,13 +135,13 @@ deps = [ ":cpp", ":test_support", + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", "//ash/services/multidevice_setup", "//ash/services/multidevice_setup:test_support", "//ash/services/multidevice_setup/public/mojom", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", "//testing/gtest", ] }
diff --git a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc index 454a1a5..fa2ec8f 100644 --- a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc +++ b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc
@@ -6,8 +6,8 @@ #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.h" +#include "ash/components/multidevice/remote_device.h" #include "base/containers/flat_map.h" -#include "chromeos/components/multidevice/remote_device.h" #include "mojo/public/cpp/bindings/pending_receiver.h" namespace ash {
diff --git a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h index 35bb840..3b7cd99 100644 --- a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h +++ b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h
@@ -10,10 +10,10 @@ #include <string> #include <tuple> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/callback.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash {
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc index 706f0ba..1fc4ef7 100644 --- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc +++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc
@@ -4,8 +4,8 @@ #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/check.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash {
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h index 0ad327c..2495fc2 100644 --- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h +++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h
@@ -8,11 +8,11 @@ #include <memory> #include <string> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/callback.h" #include "base/containers/flat_map.h" #include "base/observer_list.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash {
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc index c716f678..8d7d8ea 100644 --- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc +++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
@@ -7,12 +7,12 @@ #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace {
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h index 7f36b25..48e483f 100644 --- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h +++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h
@@ -8,13 +8,13 @@ #include <memory> #include <string> +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/callback.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/remote_device_cache.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h"
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc index 99296d5e..501c063 100644 --- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc +++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
@@ -8,6 +8,7 @@ #include <tuple> #include <utility> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/multidevice_setup/multidevice_setup_initializer.h" #include "ash/services/multidevice_setup/multidevice_setup_service.h" #include "ash/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h" @@ -20,7 +21,6 @@ #include "base/task/single_thread_task_runner.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom b/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom index 873771af..b874d56f 100644 --- a/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom +++ b/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom
@@ -4,7 +4,7 @@ module ash.multidevice_setup.mojom; -import "chromeos/components/multidevice/mojom/multidevice_types.mojom"; +import "ash/components/multidevice/mojom/multidevice_types.mojom"; import "ash/services/device_sync/public/mojom/device_sync.mojom"; // Enumeration of possible opt-in entry points for Phone Hub Camera Roll
diff --git a/ash/services/multidevice_setup/wifi_sync_notification_controller.cc b/ash/services/multidevice_setup/wifi_sync_notification_controller.cc index 6fc7c40..72bb76c4 100644 --- a/ash/services/multidevice_setup/wifi_sync_notification_controller.cc +++ b/ash/services/multidevice_setup/wifi_sync_notification_controller.cc
@@ -6,6 +6,10 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/multidevice_setup/account_status_change_delegate_notifier.h" #include "ash/services/multidevice_setup/global_state_feature_manager.h" @@ -14,10 +18,6 @@ #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/memory/ptr_util.h" #include "base/power_monitor/power_monitor.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/session_manager/core/session_manager.h"
diff --git a/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc b/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc index bf378183..7384607 100644 --- a/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc +++ b/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc
@@ -6,6 +6,9 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/fake_account_status_change_delegate.h" @@ -16,9 +19,6 @@ #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/session_manager/core/session_manager.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/secure_channel/BUILD.gn b/ash/services/secure_channel/BUILD.gn index 5054be7..0dd2f39 100644 --- a/ash/services/secure_channel/BUILD.gn +++ b/ash/services/secure_channel/BUILD.gn
@@ -172,14 +172,14 @@ ] deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/constants", "//ash/services/device_sync/proto", "//ash/services/secure_channel/public/cpp/shared", "//ash/services/secure_channel/public/mojom", "//base", "//chromeos", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", "//crypto", "//device/bluetooth", "//device/bluetooth/public/cpp", @@ -275,12 +275,12 @@ public_deps = [ ":secure_channel" ] deps = [ + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/services/secure_channel/public/cpp/shared", "//ash/services/secure_channel/public/mojom", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", "//device/bluetooth", "//testing/gmock", ] @@ -339,15 +339,15 @@ deps = [ ":test_support", + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", + "//ash/components/multidevice/logging", "//ash/services/secure_channel/public/cpp/client:test_support", "//ash/services/secure_channel/public/cpp/client:unit_tests", "//ash/services/secure_channel/public/cpp/shared", "//ash/services/secure_channel/public/mojom", "//ash/services/secure_channel/public/mojom:unit_tests", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", - "//chromeos/components/multidevice/logging", "//device/bluetooth:mocks", "//testing/gmock", "//testing/gtest",
diff --git a/ash/services/secure_channel/DEPS b/ash/services/secure_channel/DEPS index e918f60..7d47adf 100644 --- a/ash/services/secure_channel/DEPS +++ b/ash/services/secure_channel/DEPS
@@ -1,4 +1,5 @@ include_rules = [ + "+ash/components/multidevice", "+ash/constants", "+ash/services/device_sync", "+ash/services/multidevice_setup/public/cpp",
diff --git a/ash/services/secure_channel/OWNERS b/ash/services/secure_channel/OWNERS index 7027ab73..18377141 100644 --- a/ash/services/secure_channel/OWNERS +++ b/ash/services/secure_channel/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/ash/services/secure_channel/active_connection_manager.cc b/ash/services/secure_channel/active_connection_manager.cc index 3b22733..244d99f1 100644 --- a/ash/services/secure_channel/active_connection_manager.cc +++ b/ash/services/secure_channel/active_connection_manager.cc
@@ -4,11 +4,11 @@ #include "ash/services/secure_channel/active_connection_manager.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/authenticated_channel.h" #include "ash/services/secure_channel/client_connection_parameters.h" #include "ash/services/secure_channel/connection_details.h" #include "base/logging.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/active_connection_manager_impl.cc b/ash/services/secure_channel/active_connection_manager_impl.cc index d532f82..6d07835 100644 --- a/ash/services/secure_channel/active_connection_manager_impl.cc +++ b/ash/services/secure_channel/active_connection_manager_impl.cc
@@ -4,9 +4,9 @@ #include "ash/services/secure_channel/active_connection_manager_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/multiplexed_channel_impl.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/authenticated_channel_impl.cc b/ash/services/secure_channel/authenticated_channel_impl.cc index 0b671b7..f5705b8 100644 --- a/ash/services/secure_channel/authenticated_channel_impl.cc +++ b/ash/services/secure_channel/authenticated_channel_impl.cc
@@ -4,6 +4,7 @@ #include "ash/services/secure_channel/authenticated_channel_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/file_transfer_update_callback.h" #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h" #include "base/bind.h" @@ -11,7 +12,6 @@ #include "base/containers/contains.h" #include "base/logging.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/authenticated_channel_impl_unittest.cc b/ash/services/secure_channel/authenticated_channel_impl_unittest.cc index f5d0e57fb..c576905 100644 --- a/ash/services/secure_channel/authenticated_channel_impl_unittest.cc +++ b/ash/services/secure_channel/authenticated_channel_impl_unittest.cc
@@ -11,6 +11,7 @@ #include <utility> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/fake_authenticated_channel.h" #include "ash/services/secure_channel/fake_connection.h" #include "ash/services/secure_channel/fake_secure_channel_connection.h" @@ -21,7 +22,6 @@ #include "base/containers/contains.h" #include "base/test/bind.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/background_eid_generator.cc b/ash/services/secure_channel/background_eid_generator.cc index 02f7467..dc8dfdf 100644 --- a/ash/services/secure_channel/background_eid_generator.cc +++ b/ash/services/secure_channel/background_eid_generator.cc
@@ -7,6 +7,9 @@ #include <cstring> #include <memory> +#include "ash/components/multidevice/beacon_seed.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/secure_channel/data_with_timestamp.h" #include "ash/services/secure_channel/raw_eid_generator.h" @@ -16,9 +19,6 @@ #include "base/time/clock.h" #include "base/time/default_clock.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/beacon_seed.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/background_eid_generator.h b/ash/services/secure_channel/background_eid_generator.h index b899c678..d9c003e 100644 --- a/ash/services/secure_channel/background_eid_generator.h +++ b/ash/services/secure_channel/background_eid_generator.h
@@ -10,7 +10,7 @@ #include <vector> // TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" namespace base { class Clock;
diff --git a/ash/services/secure_channel/background_eid_generator_unittest.cc b/ash/services/secure_channel/background_eid_generator_unittest.cc index 3edabca..40649ea8 100644 --- a/ash/services/secure_channel/background_eid_generator_unittest.cc +++ b/ash/services/secure_channel/background_eid_generator_unittest.cc
@@ -7,15 +7,15 @@ #include <memory> #include <string> +#include "ash/components/multidevice/beacon_seed.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/secure_channel/data_with_timestamp.h" #include "ash/services/secure_channel/raw_eid_generator_impl.h" #include "base/strings/string_util.h" #include "base/test/simple_test_clock.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/beacon_seed.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/secure_channel/ble_advertisement_generator.cc b/ash/services/secure_channel/ble_advertisement_generator.cc index 2a709148..5b3688a 100644 --- a/ash/services/secure_channel/ble_advertisement_generator.cc +++ b/ash/services/secure_channel/ble_advertisement_generator.cc
@@ -7,10 +7,10 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/secure_channel/data_with_timestamp.h" #include "ash/services/secure_channel/foreground_eid_generator.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/ble_advertisement_generator.h b/ash/services/secure_channel/ble_advertisement_generator.h index 65d5b89..e81e7e6 100644 --- a/ash/services/secure_channel/ble_advertisement_generator.h +++ b/ash/services/secure_channel/ble_advertisement_generator.h
@@ -8,7 +8,7 @@ #include <memory> // TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/ble_advertisement_generator_unittest.cc b/ash/services/secure_channel/ble_advertisement_generator_unittest.cc index f4c57030..48fce485 100644 --- a/ash/services/secure_channel/ble_advertisement_generator_unittest.cc +++ b/ash/services/secure_channel/ble_advertisement_generator_unittest.cc
@@ -6,13 +6,13 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/secure_channel/data_with_timestamp.h" #include "ash/services/secure_channel/mock_foreground_eid_generator.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/secure_channel/ble_advertiser_impl.cc b/ash/services/secure_channel/ble_advertiser_impl.cc index 577e94d5..a6c1154 100644 --- a/ash/services/secure_channel/ble_advertiser_impl.cc +++ b/ash/services/secure_channel/ble_advertiser_impl.cc
@@ -4,6 +4,7 @@ #include "ash/services/secure_channel/ble_advertiser_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/bluetooth_helper.h" #include "ash/services/secure_channel/error_tolerant_ble_advertisement_impl.h" #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h" @@ -15,7 +16,6 @@ #include "base/memory/ptr_util.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/ble_characteristics_finder.cc b/ash/services/secure_channel/ble_characteristics_finder.cc index b0f26b0..4881883 100644 --- a/ash/services/secure_channel/ble_characteristics_finder.cc +++ b/ash/services/secure_channel/ble_characteristics_finder.cc
@@ -4,10 +4,10 @@ #include "ash/services/secure_channel/ble_characteristics_finder.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/background_eid_generator.h" #include "base/bind.h" #include "base/strings/string_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_device.h" #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
diff --git a/ash/services/secure_channel/ble_characteristics_finder.h b/ash/services/secure_channel/ble_characteristics_finder.h index bc92d21..af2bcef 100644 --- a/ash/services/secure_channel/ble_characteristics_finder.h +++ b/ash/services/secure_channel/ble_characteristics_finder.h
@@ -5,13 +5,13 @@ #ifndef ASH_SERVICES_SECURE_CHANNEL_BLE_CHARACTERISTICS_FINDER_H_ #define ASH_SERVICES_SECURE_CHANNEL_BLE_CHARACTERISTICS_FINDER_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/secure_channel/remote_attribute.h" #include "base/callback.h" #include "base/containers/flat_set.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/threading/thread_task_runner_handle.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_device.h" #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
diff --git a/ash/services/secure_channel/ble_characteristics_finder_unittest.cc b/ash/services/secure_channel/ble_characteristics_finder_unittest.cc index c16a733..3e8c0bc22 100644 --- a/ash/services/secure_channel/ble_characteristics_finder_unittest.cc +++ b/ash/services/secure_channel/ble_characteristics_finder_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/background_eid_generator.h" #include "ash/services/secure_channel/fake_background_eid_generator.h" #include "ash/services/secure_channel/remote_attribute.h" @@ -15,7 +16,6 @@ #include "base/test/task_environment.h" #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/public/cpp/bluetooth_uuid.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h"
diff --git a/ash/services/secure_channel/ble_connection_manager.cc b/ash/services/secure_channel/ble_connection_manager.cc index 98b57d99..abc7ad66 100644 --- a/ash/services/secure_channel/ble_connection_manager.cc +++ b/ash/services/secure_channel/ble_connection_manager.cc
@@ -4,11 +4,11 @@ #include "ash/services/secure_channel/ble_connection_manager.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/secure_channel/authenticated_channel.h" #include "base/containers/contains.h" #include "base/logging.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/ble_connection_manager_impl.cc b/ash/services/secure_channel/ble_connection_manager_impl.cc index af65904..04cb90e 100644 --- a/ash/services/secure_channel/ble_connection_manager_impl.cc +++ b/ash/services/secure_channel/ble_connection_manager_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/authenticated_channel_impl.h" #include "ash/services/secure_channel/ble_advertiser_impl.h" #include "ash/services/secure_channel/ble_constants.h" @@ -19,7 +20,6 @@ #include "base/containers/contains.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "device/bluetooth/public/cpp/bluetooth_uuid.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/ble_connection_manager_impl_unittest.cc b/ash/services/secure_channel/ble_connection_manager_impl_unittest.cc index 4ca41461..a921a23 100644 --- a/ash/services/secure_channel/ble_connection_manager_impl_unittest.cc +++ b/ash/services/secure_channel/ble_connection_manager_impl_unittest.cc
@@ -8,6 +8,7 @@ #include <tuple> #include <utility> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/authenticated_channel_impl.h" #include "ash/services/secure_channel/ble_advertiser_impl.h" #include "ash/services/secure_channel/ble_constants.h" @@ -31,7 +32,6 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "device/bluetooth/public/cpp/bluetooth_uuid.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/ash/services/secure_channel/ble_scanner.cc b/ash/services/secure_channel/ble_scanner.cc index 71ea2f9..6cc87aa 100644 --- a/ash/services/secure_channel/ble_scanner.cc +++ b/ash/services/secure_channel/ble_scanner.cc
@@ -4,10 +4,10 @@ #include "ash/services/secure_channel/ble_scanner.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/containers/contains.h" #include "base/logging.h" #include "base/notreached.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/ble_scanner.h b/ash/services/secure_channel/ble_scanner.h index 954c2ff..ebd2dc0 100644 --- a/ash/services/secure_channel/ble_scanner.h +++ b/ash/services/secure_channel/ble_scanner.h
@@ -7,6 +7,7 @@ #include <ostream> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/secure_channel/connection_attempt_details.h" #include "ash/services/secure_channel/connection_role.h" #include "ash/services/secure_channel/device_id_pair.h" @@ -14,7 +15,6 @@ #include "base/containers/flat_set.h" #include "base/observer_list.h" #include "base/observer_list_types.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace device { class BluetoothDevice;
diff --git a/ash/services/secure_channel/ble_scanner_impl.cc b/ash/services/secure_channel/ble_scanner_impl.cc index eb1cf801..d07e6e8 100644 --- a/ash/services/secure_channel/ble_scanner_impl.cc +++ b/ash/services/secure_channel/ble_scanner_impl.cc
@@ -7,6 +7,8 @@ #include <iostream> #include <sstream> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/secure_channel/ble_constants.h" #include "ash/services/secure_channel/ble_synchronizer_base.h" @@ -15,8 +17,6 @@ #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "device/bluetooth/bluetooth_device.h" #include "device/bluetooth/bluetooth_discovery_session.h" #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
diff --git a/ash/services/secure_channel/ble_scanner_impl_unittest.cc b/ash/services/secure_channel/ble_scanner_impl_unittest.cc index 2ca499a5..b2fa6ea6 100644 --- a/ash/services/secure_channel/ble_scanner_impl_unittest.cc +++ b/ash/services/secure_channel/ble_scanner_impl_unittest.cc
@@ -9,6 +9,7 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/ble_constants.h" #include "ash/services/secure_channel/connection_role.h" #include "ash/services/secure_channel/fake_ble_scanner.h" @@ -17,7 +18,6 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/test/bind.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "device/bluetooth/test/mock_bluetooth_device.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/ash/services/secure_channel/ble_synchronizer.cc b/ash/services/secure_channel/ble_synchronizer.cc index eee4a16a..6bdd1a0 100644 --- a/ash/services/secure_channel/ble_synchronizer.cc +++ b/ash/services/secure_channel/ble_synchronizer.cc
@@ -6,12 +6,12 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/default_clock.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/ble_weave_client_connection.cc b/ash/services/secure_channel/ble_weave_client_connection.cc index aacae577..3c0c2d3 100644 --- a/ash/services/secure_channel/ble_weave_client_connection.cc +++ b/ash/services/secure_channel/ble_weave_client_connection.cc
@@ -8,6 +8,7 @@ #include <sstream> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/background_eid_generator.h" #include "ash/services/secure_channel/ble_weave_packet_generator.h" #include "ash/services/secure_channel/ble_weave_packet_receiver.h" @@ -22,7 +23,6 @@ #include "base/task/task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "device/bluetooth/bluetooth_gatt_connection.h" namespace ash::secure_channel::weave {
diff --git a/ash/services/secure_channel/ble_weave_client_connection_unittest.cc b/ash/services/secure_channel/ble_weave_client_connection_unittest.cc index d1940f77..262d6f5 100644 --- a/ash/services/secure_channel/ble_weave_client_connection_unittest.cc +++ b/ash/services/secure_channel/ble_weave_client_connection_unittest.cc
@@ -9,6 +9,9 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/ble_weave_packet_generator.h" #include "ash/services/secure_channel/ble_weave_packet_receiver.h" #include "ash/services/secure_channel/connection_observer.h" @@ -24,9 +27,6 @@ #include "base/test/test_simple_task_runner.h" #include "base/timer/mock_timer.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "device/bluetooth/test/mock_bluetooth_device.h"
diff --git a/ash/services/secure_channel/ble_weave_packet_receiver.cc b/ash/services/secure_channel/ble_weave_packet_receiver.cc index 5796e289..0a9a509 100644 --- a/ash/services/secure_channel/ble_weave_packet_receiver.cc +++ b/ash/services/secure_channel/ble_weave_packet_receiver.cc
@@ -12,9 +12,9 @@ #include <netinet/in.h> #endif +#include "ash/components/multidevice/logging/logging.h" #include "base/check_op.h" #include "base/notreached.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel::weave {
diff --git a/ash/services/secure_channel/bluetooth_helper.cc b/ash/services/secure_channel/bluetooth_helper.cc index ebe94b5..7b0c406e 100644 --- a/ash/services/secure_channel/bluetooth_helper.cc +++ b/ash/services/secure_channel/bluetooth_helper.cc
@@ -4,9 +4,9 @@ #include "ash/services/secure_channel/bluetooth_helper.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/logging.h" #include "base/notreached.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/bluetooth_helper.h b/ash/services/secure_channel/bluetooth_helper.h index 4ad8536..de0ce7e 100644 --- a/ash/services/secure_channel/bluetooth_helper.h +++ b/ash/services/secure_channel/bluetooth_helper.h
@@ -9,9 +9,9 @@ #include <string> #include <utility> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/secure_channel/data_with_timestamp.h" #include "ash/services/secure_channel/device_id_pair.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/bluetooth_helper_impl.cc b/ash/services/secure_channel/bluetooth_helper_impl.cc index 18c107b..2ef9ef50 100644 --- a/ash/services/secure_channel/bluetooth_helper_impl.cc +++ b/ash/services/secure_channel/bluetooth_helper_impl.cc
@@ -4,6 +4,10 @@ #include "ash/services/secure_channel/bluetooth_helper_impl.h" +#include "ash/components/multidevice/beacon_seed.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/constants/ash_features.h" #include "ash/services/secure_channel/background_eid_generator.h" #include "ash/services/secure_channel/ble_advertisement_generator.h" @@ -11,10 +15,6 @@ #include "ash/services/secure_channel/foreground_eid_generator.h" #include "base/containers/flat_map.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/beacon_seed.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_cache.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/bluetooth_helper_impl.h b/ash/services/secure_channel/bluetooth_helper_impl.h index 1b5525a6..897001d 100644 --- a/ash/services/secure_channel/bluetooth_helper_impl.h +++ b/ash/services/secure_channel/bluetooth_helper_impl.h
@@ -8,9 +8,9 @@ #include <memory> #include <string> -#include "ash/services/secure_channel/bluetooth_helper.h" // TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/remote_device_cache.h" +#include "ash/services/secure_channel/bluetooth_helper.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/bluetooth_helper_impl_unittest.cc b/ash/services/secure_channel/bluetooth_helper_impl_unittest.cc index 76b992d5..8a37d87 100644 --- a/ash/services/secure_channel/bluetooth_helper_impl_unittest.cc +++ b/ash/services/secure_channel/bluetooth_helper_impl_unittest.cc
@@ -6,6 +6,8 @@ #include <memory> +#include "ash/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/ble_advertisement_generator.h" #include "ash/services/secure_channel/device_id_pair.h" #include "ash/services/secure_channel/fake_background_eid_generator.h" @@ -14,8 +16,6 @@ #include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/remote_device_cache.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/client_connection_parameters.cc b/ash/services/secure_channel/client_connection_parameters.cc index 133baeaf6..1fe9ec6 100644 --- a/ash/services/secure_channel/client_connection_parameters.cc +++ b/ash/services/secure_channel/client_connection_parameters.cc
@@ -4,7 +4,7 @@ #include "ash/services/secure_channel/client_connection_parameters.h" -#include "chromeos/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/connect_to_device_operation.h b/ash/services/secure_channel/connect_to_device_operation.h index dc208d61..80e06155 100644 --- a/ash/services/secure_channel/connect_to_device_operation.h +++ b/ash/services/secure_channel/connect_to_device_operation.h
@@ -5,10 +5,10 @@ #ifndef ASH_SERVICES_SECURE_CHANNEL_CONNECT_TO_DEVICE_OPERATION_H_ #define ASH_SERVICES_SECURE_CHANNEL_CONNECT_TO_DEVICE_OPERATION_H_ +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h" #include "base/callback.h" #include "base/logging.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/connection.cc b/ash/services/secure_channel/connection.cc index f0745f2..2eba632a 100644 --- a/ash/services/secure_channel/connection.cc +++ b/ash/services/secure_channel/connection.cc
@@ -7,13 +7,13 @@ #include <sstream> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/connection_observer.h" #include "ash/services/secure_channel/file_transfer_update_callback.h" #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h" #include "ash/services/secure_channel/wire_message.h" #include "base/callback.h" #include "base/logging.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/connection.h b/ash/services/secure_channel/connection.h index 1c2555ab..8ec5ece 100644 --- a/ash/services/secure_channel/connection.h +++ b/ash/services/secure_channel/connection.h
@@ -8,11 +8,11 @@ #include <memory> #include <ostream> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/secure_channel/file_transfer_update_callback.h" #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom-forward.h" #include "base/callback_forward.h" #include "base/observer_list.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/connection_attempt.h b/ash/services/secure_channel/connection_attempt.h index 136a61c..801063d 100644 --- a/ash/services/secure_channel/connection_attempt.h +++ b/ash/services/secure_channel/connection_attempt.h
@@ -10,6 +10,7 @@ #include <utility> #include <vector> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/client_connection_parameters.h" #include "ash/services/secure_channel/connection_attempt_delegate.h" #include "ash/services/secure_channel/connection_attempt_details.h" @@ -17,7 +18,6 @@ #include "ash/services/secure_channel/pending_connection_request.h" #include "ash/services/secure_channel/pending_connection_request_delegate.h" #include "base/time/clock.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/connection_attempt_base.h b/ash/services/secure_channel/connection_attempt_base.h index 96369fe9..b5e6e28 100644 --- a/ash/services/secure_channel/connection_attempt_base.h +++ b/ash/services/secure_channel/connection_attempt_base.h
@@ -5,6 +5,7 @@ #ifndef ASH_SERVICES_SECURE_CHANNEL_CONNECTION_ATTEMPT_BASE_H_ #define ASH_SERVICES_SECURE_CHANNEL_CONNECTION_ATTEMPT_BASE_H_ +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/authenticated_channel.h" #include "ash/services/secure_channel/connect_to_device_operation.h" #include "ash/services/secure_channel/connection_attempt.h" @@ -18,7 +19,6 @@ #include "base/logging.h" #include "base/memory/weak_ptr.h" #include "base/time/default_clock.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/connection_attempt_details.cc b/ash/services/secure_channel/connection_attempt_details.cc index 3365df5..9609e506 100644 --- a/ash/services/secure_channel/connection_attempt_details.cc +++ b/ash/services/secure_channel/connection_attempt_details.cc
@@ -4,8 +4,8 @@ #include "ash/services/secure_channel/connection_attempt_details.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "base/check.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/connection_details.cc b/ash/services/secure_channel/connection_details.cc index e87cfd5..3226e4c 100644 --- a/ash/services/secure_channel/connection_details.cc +++ b/ash/services/secure_channel/connection_details.cc
@@ -6,8 +6,8 @@ #include <tuple> +#include "ash/components/multidevice/remote_device_ref.h" #include "base/check.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/connection_unittest.cc b/ash/services/secure_channel/connection_unittest.cc index 231399b4..51a20ab 100644 --- a/ash/services/secure_channel/connection_unittest.cc +++ b/ash/services/secure_channel/connection_unittest.cc
@@ -4,6 +4,8 @@ #include "ash/services/secure_channel/connection.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/connection_observer.h" #include "ash/services/secure_channel/file_transfer_update_callback.h" #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h" @@ -12,8 +14,6 @@ #include "base/callback.h" #include "base/memory/ptr_util.h" #include "base/test/bind.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/secure_channel/device_id_pair.cc b/ash/services/secure_channel/device_id_pair.cc index 4a381043..33d8a1af 100644 --- a/ash/services/secure_channel/device_id_pair.cc +++ b/ash/services/secure_channel/device_id_pair.cc
@@ -6,8 +6,8 @@ #include <functional> +#include "ash/components/multidevice/remote_device_ref.h" #include "base/hash/hash.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_authenticator.cc b/ash/services/secure_channel/device_to_device_authenticator.cc index 9b0b8df..de8a4c12 100644 --- a/ash/services/secure_channel/device_to_device_authenticator.cc +++ b/ash/services/secure_channel/device_to_device_authenticator.cc
@@ -7,6 +7,8 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate.h" #include "ash/services/secure_channel/authenticator.h" #include "ash/services/secure_channel/connection.h" #include "ash/services/secure_channel/device_to_device_initiator_helper.h" @@ -17,8 +19,6 @@ #include "base/memory/ptr_util.h" #include "base/time/time.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_authenticator.h b/ash/services/secure_channel/device_to_device_authenticator.h index b4c70e32..cfc91b8e 100644 --- a/ash/services/secure_channel/device_to_device_authenticator.h +++ b/ash/services/secure_channel/device_to_device_authenticator.h
@@ -7,13 +7,13 @@ #include <memory> +// TODO(https://crbug.com/1164001): move to forward declaration. +#include "ash/components/multidevice/secure_message_delegate.h" #include "ash/services/secure_channel/authenticator.h" #include "ash/services/secure_channel/connection.h" #include "ash/services/secure_channel/connection_observer.h" #include "ash/services/secure_channel/session_keys.h" #include "base/memory/weak_ptr.h" -// TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/secure_message_delegate.h" namespace base { class OneShotTimer;
diff --git a/ash/services/secure_channel/device_to_device_authenticator_unittest.cc b/ash/services/secure_channel/device_to_device_authenticator_unittest.cc index 54d3297..865831e 100644 --- a/ash/services/secure_channel/device_to_device_authenticator_unittest.cc +++ b/ash/services/secure_channel/device_to_device_authenticator_unittest.cc
@@ -8,6 +8,8 @@ #include <utility> #include <vector> +#include "ash/components/multidevice/fake_secure_message_delegate.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/authenticator.h" #include "ash/services/secure_channel/connection.h" #include "ash/services/secure_channel/device_to_device_responder_operations.h" @@ -22,8 +24,6 @@ #include "base/memory/ptr_util.h" #include "base/rand_util.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/secure_channel/device_to_device_initiator_helper.cc b/ash/services/secure_channel/device_to_device_initiator_helper.cc index 95d9285..9c2e35f 100644 --- a/ash/services/secure_channel/device_to_device_initiator_helper.cc +++ b/ash/services/secure_channel/device_to_device_initiator_helper.cc
@@ -4,11 +4,11 @@ #include "ash/services/secure_channel/device_to_device_initiator_helper.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "base/bind.h" #include "base/callback.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_initiator_helper.h b/ash/services/secure_channel/device_to_device_initiator_helper.h index 0f863e64..c0404961 100644 --- a/ash/services/secure_channel/device_to_device_initiator_helper.h +++ b/ash/services/secure_channel/device_to_device_initiator_helper.h
@@ -8,11 +8,11 @@ #include <memory> #include <string> +// TODO(https://crbug.com/1164001): move to forward declaration. +#include "ash/components/multidevice/secure_message_delegate.h" #include "ash/services/secure_channel/session_keys.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" -// TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/secure_message_delegate.h" #include "third_party/ukey2/proto/device_to_device_messages.pb.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_operations_unittest.cc b/ash/services/secure_channel/device_to_device_operations_unittest.cc index 6a6aa0c4..ec2b3c3 100644 --- a/ash/services/secure_channel/device_to_device_operations_unittest.cc +++ b/ash/services/secure_channel/device_to_device_operations_unittest.cc
@@ -4,12 +4,12 @@ #include <memory> +#include "ash/components/multidevice/fake_secure_message_delegate.h" #include "ash/services/secure_channel/device_to_device_initiator_helper.h" #include "ash/services/secure_channel/device_to_device_responder_operations.h" #include "ash/services/secure_channel/session_keys.h" #include "base/base64url.h" #include "base/bind.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_responder_operations.cc b/ash/services/secure_channel/device_to_device_responder_operations.cc index dc7ffdd0..6649a28 100644 --- a/ash/services/secure_channel/device_to_device_responder_operations.cc +++ b/ash/services/secure_channel/device_to_device_responder_operations.cc
@@ -4,12 +4,12 @@ #include "ash/services/secure_channel/device_to_device_responder_operations.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/secure_channel/session_keys.h" #include "base/bind.h" #include "base/callback.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate.h" #include "third_party/securemessage/proto/securemessage.pb.h" #include "third_party/ukey2/proto/device_to_device_messages.pb.h"
diff --git a/ash/services/secure_channel/device_to_device_responder_operations.h b/ash/services/secure_channel/device_to_device_responder_operations.h index 5d110ab..40a13da 100644 --- a/ash/services/secure_channel/device_to_device_responder_operations.h +++ b/ash/services/secure_channel/device_to_device_responder_operations.h
@@ -7,9 +7,9 @@ #include <string> -#include "base/callback_forward.h" // TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/secure_message_delegate.h" +#include "ash/components/multidevice/secure_message_delegate.h" +#include "base/callback_forward.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_secure_context.cc b/ash/services/secure_channel/device_to_device_secure_context.cc index 7877d47..e6fe354 100644 --- a/ash/services/secure_channel/device_to_device_secure_context.cc +++ b/ash/services/secure_channel/device_to_device_secure_context.cc
@@ -6,12 +6,12 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/secure_channel/session_keys.h" #include "base/bind.h" #include "base/callback.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate.h" #include "third_party/securemessage/proto/securemessage.pb.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_secure_context.h b/ash/services/secure_channel/device_to_device_secure_context.h index 50c69742..2abc6fd3 100644 --- a/ash/services/secure_channel/device_to_device_secure_context.h +++ b/ash/services/secure_channel/device_to_device_secure_context.h
@@ -9,10 +9,10 @@ #include <queue> #include <vector> +// TODO(https://crbug.com/1164001): move to forward declaration. +#include "ash/components/multidevice/secure_message_delegate.h" #include "ash/services/secure_channel/secure_context.h" #include "base/memory/weak_ptr.h" -// TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/secure_message_delegate.h" #include "third_party/ukey2/proto/device_to_device_messages.pb.h" namespace securemessage {
diff --git a/ash/services/secure_channel/device_to_device_secure_context_unittest.cc b/ash/services/secure_channel/device_to_device_secure_context_unittest.cc index 6aab6b2..e5b473c6 100644 --- a/ash/services/secure_channel/device_to_device_secure_context_unittest.cc +++ b/ash/services/secure_channel/device_to_device_secure_context_unittest.cc
@@ -7,12 +7,12 @@ #include <list> #include <memory> +#include "ash/components/multidevice/fake_secure_message_delegate.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/secure_channel/session_keys.h" #include "base/bind.h" #include "base/callback.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/securemessage/proto/securemessage.pb.h"
diff --git a/ash/services/secure_channel/error_tolerant_ble_advertisement_impl.cc b/ash/services/secure_channel/error_tolerant_ble_advertisement_impl.cc index 46af006..89000393 100644 --- a/ash/services/secure_channel/error_tolerant_ble_advertisement_impl.cc +++ b/ash/services/secure_channel/error_tolerant_ble_advertisement_impl.cc
@@ -6,13 +6,13 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/secure_channel/ble_constants.h" #include "ash/services/secure_channel/ble_synchronizer_base.h" #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/fake_ble_advertisement_generator.cc b/ash/services/secure_channel/fake_ble_advertisement_generator.cc index 08189e3..43fd608 100644 --- a/ash/services/secure_channel/fake_ble_advertisement_generator.cc +++ b/ash/services/secure_channel/fake_ble_advertisement_generator.cc
@@ -4,7 +4,7 @@ #include "ash/services/secure_channel/fake_ble_advertisement_generator.h" -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/fake_bluetooth_helper.h b/ash/services/secure_channel/fake_bluetooth_helper.h index b2162e4..0e0a9aae 100644 --- a/ash/services/secure_channel/fake_bluetooth_helper.h +++ b/ash/services/secure_channel/fake_bluetooth_helper.h
@@ -10,9 +10,9 @@ #include <unordered_map> #include <utility> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/secure_channel/bluetooth_helper.h" #include "ash/services/secure_channel/data_with_timestamp.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/fake_secure_channel.h b/ash/services/secure_channel/fake_secure_channel.h index ce7d991..c284dae 100644 --- a/ash/services/secure_channel/fake_secure_channel.h +++ b/ash/services/secure_channel/fake_secure_channel.h
@@ -10,9 +10,9 @@ #include <tuple> #include <vector> +#include "ash/components/multidevice/remote_device_cache.h" #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h" #include "ash/services/secure_channel/secure_channel_base.h" -#include "chromeos/components/multidevice/remote_device_cache.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h"
diff --git a/ash/services/secure_channel/foreground_eid_generator.cc b/ash/services/secure_channel/foreground_eid_generator.cc index 5f11d2d..26070bd 100644 --- a/ash/services/secure_channel/foreground_eid_generator.cc +++ b/ash/services/secure_channel/foreground_eid_generator.cc
@@ -7,6 +7,8 @@ #include <cstring> #include <memory> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/secure_channel/raw_eid_generator.h" #include "ash/services/secure_channel/raw_eid_generator_impl.h" @@ -16,8 +18,6 @@ #include "base/time/clock.h" #include "base/time/default_clock.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/foreground_eid_generator_unittest.cc b/ash/services/secure_channel/foreground_eid_generator_unittest.cc index c7f50f4..725e73e6 100644 --- a/ash/services/secure_channel/foreground_eid_generator_unittest.cc +++ b/ash/services/secure_channel/foreground_eid_generator_unittest.cc
@@ -6,12 +6,12 @@ #include <memory> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/secure_channel/raw_eid_generator_impl.h" #include "base/strings/string_util.h" #include "base/test/simple_test_clock.h" #include "base/time/time.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/secure_channel/multiplexed_channel_impl.cc b/ash/services/secure_channel/multiplexed_channel_impl.cc index d8d628af..dc9a656 100644 --- a/ash/services/secure_channel/multiplexed_channel_impl.cc +++ b/ash/services/secure_channel/multiplexed_channel_impl.cc
@@ -4,6 +4,7 @@ #include "ash/services/secure_channel/multiplexed_channel_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/file_transfer_update_callback.h" #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h" #include "ash/services/secure_channel/single_client_proxy_impl.h" @@ -11,7 +12,6 @@ #include "base/containers/contains.h" #include "base/logging.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/nearby_connection.cc b/ash/services/secure_channel/nearby_connection.cc index 323c21ea..dd60511 100644 --- a/ash/services/secure_channel/nearby_connection.cc +++ b/ash/services/secure_channel/nearby_connection.cc
@@ -4,6 +4,7 @@ #include "ash/services/secure_channel/nearby_connection.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/file_transfer_update_callback.h" #include "ash/services/secure_channel/public/mojom/nearby_connector.mojom.h" #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h" @@ -15,7 +16,6 @@ #include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver_set.h"
diff --git a/ash/services/secure_channel/nearby_connection_manager.cc b/ash/services/secure_channel/nearby_connection_manager.cc index bb2c529..2b2709b0 100644 --- a/ash/services/secure_channel/nearby_connection_manager.cc +++ b/ash/services/secure_channel/nearby_connection_manager.cc
@@ -4,9 +4,9 @@ #include "ash/services/secure_channel/nearby_connection_manager.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/authenticated_channel.h" #include "base/containers/contains.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/nearby_connection_manager_impl.cc b/ash/services/secure_channel/nearby_connection_manager_impl.cc index ef75600..0c41889 100644 --- a/ash/services/secure_channel/nearby_connection_manager_impl.cc +++ b/ash/services/secure_channel/nearby_connection_manager_impl.cc
@@ -4,13 +4,13 @@ #include "ash/services/secure_channel/nearby_connection_manager_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/authenticated_channel_impl.h" #include "ash/services/secure_channel/nearby_connection.h" #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h" #include "ash/services/secure_channel/secure_channel_disconnector.h" #include "base/containers/contains.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/nearby_connection_manager_impl_unittest.cc b/ash/services/secure_channel/nearby_connection_manager_impl_unittest.cc index 2441c12..360c8cc 100644 --- a/ash/services/secure_channel/nearby_connection_manager_impl_unittest.cc +++ b/ash/services/secure_channel/nearby_connection_manager_impl_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/authenticated_channel_impl.h" #include "ash/services/secure_channel/fake_authenticated_channel.h" #include "ash/services/secure_channel/fake_ble_scanner.h" @@ -16,7 +17,6 @@ #include "ash/services/secure_channel/public/cpp/client/fake_nearby_connector.h" #include "ash/services/secure_channel/secure_channel.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/nearby_connection_unittest.cc b/ash/services/secure_channel/nearby_connection_unittest.cc index 60e16fdc..1fe0024 100644 --- a/ash/services/secure_channel/nearby_connection_unittest.cc +++ b/ash/services/secure_channel/nearby_connection_unittest.cc
@@ -4,6 +4,7 @@ #include "ash/services/secure_channel/nearby_connection.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/connection_observer.h" #include "ash/services/secure_channel/file_transfer_update_callback.h" #include "ash/services/secure_channel/public/cpp/client/fake_nearby_connector.h" @@ -15,7 +16,6 @@ #include "base/files/file_util.h" #include "base/test/bind.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/pending_ble_initiator_connection_request.cc b/ash/services/secure_channel/pending_ble_initiator_connection_request.cc index 586e93a..c3a889b 100644 --- a/ash/services/secure_channel/pending_ble_initiator_connection_request.cc +++ b/ash/services/secure_channel/pending_ble_initiator_connection_request.cc
@@ -6,11 +6,11 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/client_connection_parameters.h" #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h" #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/pending_ble_listener_connection_request.cc b/ash/services/secure_channel/pending_ble_listener_connection_request.cc index eed4cf9..92b72a5c 100644 --- a/ash/services/secure_channel/pending_ble_listener_connection_request.cc +++ b/ash/services/secure_channel/pending_ble_listener_connection_request.cc
@@ -6,10 +6,10 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/client_connection_parameters.h" #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/pending_connection_request_base.h b/ash/services/secure_channel/pending_connection_request_base.h index 3549833..8e5c31bc 100644 --- a/ash/services/secure_channel/pending_connection_request_base.h +++ b/ash/services/secure_channel/pending_connection_request_base.h
@@ -7,13 +7,13 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/client_connection_parameters.h" #include "ash/services/secure_channel/pending_connection_request.h" #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h" #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h" #include "base/logging.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/pending_nearby_initiator_connection_request.cc b/ash/services/secure_channel/pending_nearby_initiator_connection_request.cc index 6009ba7..e933d71a 100644 --- a/ash/services/secure_channel/pending_nearby_initiator_connection_request.cc +++ b/ash/services/secure_channel/pending_nearby_initiator_connection_request.cc
@@ -4,9 +4,9 @@ #include "ash/services/secure_channel/pending_nearby_initiator_connection_request.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "device/bluetooth/bluetooth_adapter.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/presence_monitor_delegate.cc b/ash/services/secure_channel/presence_monitor_delegate.cc index e2955c9..93426294 100644 --- a/ash/services/secure_channel/presence_monitor_delegate.cc +++ b/ash/services/secure_channel/presence_monitor_delegate.cc
@@ -4,13 +4,13 @@ #include "ash/services/secure_channel/presence_monitor_delegate.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_cache.h" #include "ash/services/secure_channel/ble_scanner_impl.h" #include "ash/services/secure_channel/ble_synchronizer.h" #include "ash/services/secure_channel/bluetooth_helper_impl.h" #include "ash/services/secure_channel/connection_role.h" #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_cache.h" #include "device/bluetooth/bluetooth_adapter.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/presence_monitor_delegate.h b/ash/services/secure_channel/presence_monitor_delegate.h index f7d6b67..03a6cba 100644 --- a/ash/services/secure_channel/presence_monitor_delegate.h +++ b/ash/services/secure_channel/presence_monitor_delegate.h
@@ -5,12 +5,12 @@ #ifndef ASH_SERVICES_SECURE_CHANNEL_PRESENCE_MONITOR_DELEGATE_H_ #define ASH_SERVICES_SECURE_CHANNEL_PRESENCE_MONITOR_DELEGATE_H_ +// TODO(https://crbug.com/1164001): move to forward declaration. +#include "ash/components/multidevice/remote_device_cache.h" +// TODO(https://crbug.com/1164001): move to forward declaration. +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/secure_channel/ble_scanner.h" #include "ash/services/secure_channel/public/cpp/shared/presence_monitor.h" -// TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/remote_device_cache.h" -// TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/remote_device_ref.h" namespace device { class BluetoothAdapter;
diff --git a/ash/services/secure_channel/presence_monitor_impl.cc b/ash/services/secure_channel/presence_monitor_impl.cc index 2de86dc..679795c 100644 --- a/ash/services/secure_channel/presence_monitor_impl.cc +++ b/ash/services/secure_channel/presence_monitor_impl.cc
@@ -4,8 +4,8 @@ #include "ash/services/secure_channel/presence_monitor_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/presence_monitor_delegate.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter_factory.h"
diff --git a/ash/services/secure_channel/public/cpp/client/BUILD.gn b/ash/services/secure_channel/public/cpp/client/BUILD.gn index 5dac9dc..759c0a8 100644 --- a/ash/services/secure_channel/public/cpp/client/BUILD.gn +++ b/ash/services/secure_channel/public/cpp/client/BUILD.gn
@@ -31,13 +31,13 @@ ] deps = [ + "//ash/components/multidevice/logging", "//ash/constants", "//ash/services/device_sync/public/cpp", "//ash/services/multidevice_setup/public/cpp", "//ash/services/secure_channel/public/cpp/shared", "//ash/services/secure_channel/public/mojom", "//base", - "//chromeos/components/multidevice/logging", "//mojo/public/cpp/bindings", ] } @@ -80,6 +80,8 @@ deps = [ ":client", ":test_support", + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", "//ash/constants", "//ash/services/device_sync/public/cpp:test_support", "//ash/services/multidevice_setup/public/cpp:test_support", @@ -89,8 +91,6 @@ "//ash/services/secure_channel/public/mojom", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", "//testing/gtest", ] }
diff --git a/ash/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc b/ash/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc index 6a745d2..caa5e35d 100644 --- a/ash/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc +++ b/ash/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc
@@ -6,6 +6,7 @@ #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/fake_channel.h" #include "ash/services/secure_channel/fake_secure_channel.h" #include "ash/services/secure_channel/public/cpp/client/client_channel_impl.h" @@ -30,7 +31,6 @@ #include "base/test/null_task_runner.h" #include "base/test/task_environment.h" #include "base/test/test_simple_task_runner.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/secure_channel/public/cpp/client/connection_attempt.h b/ash/services/secure_channel/public/cpp/client/connection_attempt.h index 1985ad69..1a758fd 100644 --- a/ash/services/secure_channel/public/cpp/client/connection_attempt.h +++ b/ash/services/secure_channel/public/cpp/client/connection_attempt.h
@@ -5,8 +5,8 @@ #ifndef ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_CLIENT_CONNECTION_ATTEMPT_H_ #define ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_CLIENT_CONNECTION_ATTEMPT_H_ +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc b/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc index acd16bb..49144c8 100644 --- a/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc +++ b/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" @@ -20,7 +21,6 @@ #include "base/test/simple_test_clock.h" #include "base/time/time.h" #include "base/timer/mock_timer.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.cc b/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.cc index 6f5739d81..b7ef619c 100644 --- a/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.cc +++ b/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.cc
@@ -4,7 +4,7 @@ #include "ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h" -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/public/cpp/client/presence_monitor_client.h b/ash/services/secure_channel/public/cpp/client/presence_monitor_client.h index 48a462d2..e2af3f90b0 100644 --- a/ash/services/secure_channel/public/cpp/client/presence_monitor_client.h +++ b/ash/services/secure_channel/public/cpp/client/presence_monitor_client.h
@@ -5,9 +5,9 @@ #ifndef ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_CLIENT_PRESENCE_MONITOR_CLIENT_H_ #define ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_CLIENT_PRESENCE_MONITOR_CLIENT_H_ -#include "ash/services/secure_channel/public/cpp/shared/presence_monitor.h" // TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/services/secure_channel/public/cpp/shared/presence_monitor.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/public/cpp/client/secure_channel_client.h b/ash/services/secure_channel/public/cpp/client/secure_channel_client.h index 164db07..6968f492 100644 --- a/ash/services/secure_channel/public/cpp/client/secure_channel_client.h +++ b/ash/services/secure_channel/public/cpp/client/secure_channel_client.h
@@ -8,7 +8,7 @@ #include <string> // TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc b/ash/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc index 6166bb6..acedfe56 100644 --- a/ash/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc +++ b/ash/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc
@@ -4,6 +4,7 @@ #include "ash/services/secure_channel/public/cpp/client/secure_channel_client_impl.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/fake_channel.h" #include "ash/services/secure_channel/fake_secure_channel.h" #include "ash/services/secure_channel/public/cpp/client/client_channel_impl.h" @@ -20,7 +21,6 @@ #include "base/test/null_task_runner.h" #include "base/test/task_environment.h" #include "base/test/test_simple_task_runner.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/secure_channel/public/cpp/shared/BUILD.gn b/ash/services/secure_channel/public/cpp/shared/BUILD.gn index 522d06f..9fd079ae 100644 --- a/ash/services/secure_channel/public/cpp/shared/BUILD.gn +++ b/ash/services/secure_channel/public/cpp/shared/BUILD.gn
@@ -16,11 +16,10 @@ ] public_deps = [ - "//base", - # TODO(https://crbug.com/1164001): remove dep. when multidevice is migrated # to namespace ash and the included header can be changed to a forward # declaration. - "//chromeos/components/multidevice", + "//ash/components/multidevice", + "//base", ] }
diff --git a/ash/services/secure_channel/public/cpp/shared/presence_monitor.h b/ash/services/secure_channel/public/cpp/shared/presence_monitor.h index 14f001f..4d85e12 100644 --- a/ash/services/secure_channel/public/cpp/shared/presence_monitor.h +++ b/ash/services/secure_channel/public/cpp/shared/presence_monitor.h
@@ -5,10 +5,9 @@ #ifndef ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_SHARED_PRESENCE_MONITOR_H_ #define ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_SHARED_PRESENCE_MONITOR_H_ -#include "base/callback.h" - // TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device.h" +#include "base/callback.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/public/mojom/BUILD.gn b/ash/services/secure_channel/public/mojom/BUILD.gn index 400ffd73..8db7f85be 100644 --- a/ash/services/secure_channel/public/mojom/BUILD.gn +++ b/ash/services/secure_channel/public/mojom/BUILD.gn
@@ -15,7 +15,7 @@ ] public_deps = [ - "//chromeos/components/multidevice/mojom", + "//ash/components/multidevice/mojom", "//mojo/public/mojom/base", ]
diff --git a/ash/services/secure_channel/public/mojom/secure_channel.mojom b/ash/services/secure_channel/public/mojom/secure_channel.mojom index 4d3d4084..56b657e 100644 --- a/ash/services/secure_channel/public/mojom/secure_channel.mojom +++ b/ash/services/secure_channel/public/mojom/secure_channel.mojom
@@ -4,9 +4,9 @@ module ash.secure_channel.mojom; +import "ash/components/multidevice/mojom/multidevice_types.mojom"; import "ash/services/secure_channel/public/mojom/nearby_connector.mojom"; import "ash/services/secure_channel/public/mojom/secure_channel_types.mojom"; -import "chromeos/components/multidevice/mojom/multidevice_types.mojom"; enum ConnectionAttemptFailureReason { // The local device could not authenticate with the remote device. This likely
diff --git a/ash/services/secure_channel/secure_channel.cc b/ash/services/secure_channel/secure_channel.cc index 91ea806..92e4f27 100644 --- a/ash/services/secure_channel/secure_channel.cc +++ b/ash/services/secure_channel/secure_channel.cc
@@ -6,14 +6,14 @@ #include <memory> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/services/secure_channel/file_transfer_update_callback.h" #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h" #include "ash/services/secure_channel/wire_message.h" #include "base/bind.h" #include "base/callback.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/secure_channel.h b/ash/services/secure_channel/secure_channel.h index 01f6acf..bea0d4fb 100644 --- a/ash/services/secure_channel/secure_channel.h +++ b/ash/services/secure_channel/secure_channel.h
@@ -5,6 +5,7 @@ #ifndef ASH_SERVICES_SECURE_CHANNEL_SECURE_CHANNEL_H_ #define ASH_SERVICES_SECURE_CHANNEL_SECURE_CHANNEL_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/secure_channel/authenticator.h" #include "ash/services/secure_channel/connection.h" #include "ash/services/secure_channel/connection_observer.h" @@ -15,7 +16,6 @@ #include "base/callback.h" #include "base/containers/queue.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/secure_channel_disconnector_impl.cc b/ash/services/secure_channel/secure_channel_disconnector_impl.cc index a1082c5..ec7aa2b 100644 --- a/ash/services/secure_channel/secure_channel_disconnector_impl.cc +++ b/ash/services/secure_channel/secure_channel_disconnector_impl.cc
@@ -4,8 +4,8 @@ #include "ash/services/secure_channel/secure_channel_disconnector_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/secure_channel_disconnector_impl_unittest.cc b/ash/services/secure_channel/secure_channel_disconnector_impl_unittest.cc index 3bab62e..b26558c4 100644 --- a/ash/services/secure_channel/secure_channel_disconnector_impl_unittest.cc +++ b/ash/services/secure_channel/secure_channel_disconnector_impl_unittest.cc
@@ -6,13 +6,13 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/fake_connection.h" #include "ash/services/secure_channel/fake_secure_channel_connection.h" #include "base/bind.h" #include "base/containers/contains.h" #include "base/containers/flat_set.h" #include "base/unguessable_token.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/secure_channel_impl.cc b/ash/services/secure_channel/secure_channel_impl.cc index 8d37281..906e5539 100644 --- a/ash/services/secure_channel/secure_channel_impl.cc +++ b/ash/services/secure_channel/secure_channel_impl.cc
@@ -7,6 +7,7 @@ #include <ostream> #include <sstream> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/active_connection_manager_impl.h" #include "ash/services/secure_channel/authenticated_channel.h" #include "ash/services/secure_channel/ble_connection_manager_impl.h" @@ -20,7 +21,6 @@ #include "ash/services/secure_channel/secure_channel_disconnector_impl.h" #include "ash/services/secure_channel/timer_factory_impl.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "device/bluetooth/bluetooth_adapter.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/secure_channel_impl.h b/ash/services/secure_channel/secure_channel_impl.h index 466ec956..4dd8d6fd 100644 --- a/ash/services/secure_channel/secure_channel_impl.h +++ b/ash/services/secure_channel/secure_channel_impl.h
@@ -10,14 +10,14 @@ #include <tuple> #include <vector> +// TODO(https://crbug.com/1164001): move to forward declaration. +#include "ash/components/multidevice/remote_device_cache.h" #include "ash/services/secure_channel/active_connection_manager.h" #include "ash/services/secure_channel/connection_attempt_details.h" #include "ash/services/secure_channel/pending_connection_manager.h" #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h" #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h" #include "base/containers/flat_map.h" -// TODO(https://crbug.com/1164001): move to forward declaration. -#include "chromeos/components/multidevice/remote_device_cache.h" namespace device { class BluetoothAdapter;
diff --git a/ash/services/secure_channel/secure_channel_initializer.cc b/ash/services/secure_channel/secure_channel_initializer.cc index 3ebee30b..38bb4ab 100644 --- a/ash/services/secure_channel/secure_channel_initializer.cc +++ b/ash/services/secure_channel/secure_channel_initializer.cc
@@ -4,10 +4,10 @@ #include "ash/services/secure_channel/secure_channel_initializer.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/secure_channel_impl.h" #include "base/bind.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/dbus/bluez_dbus_manager.h"
diff --git a/ash/services/secure_channel/secure_channel_service_unittest.cc b/ash/services/secure_channel/secure_channel_service_unittest.cc index 756859a..92f6885 100644 --- a/ash/services/secure_channel/secure_channel_service_unittest.cc +++ b/ash/services/secure_channel/secure_channel_service_unittest.cc
@@ -4,6 +4,8 @@ #include <memory> +#include "ash/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/secure_channel/active_connection_manager_impl.h" #include "ash/services/secure_channel/ble_connection_manager_impl.h" #include "ash/services/secure_channel/ble_scanner_impl.h" @@ -37,8 +39,6 @@ #include "base/run_loop.h" #include "base/test/task_environment.h" #include "base/test/test_simple_task_runner.h" -#include "chromeos/components/multidevice/remote_device_cache.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/dbus/bluez_dbus_manager.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h"
diff --git a/ash/services/secure_channel/secure_channel_unittest.cc b/ash/services/secure_channel/secure_channel_unittest.cc index 4d5babd..c6139776 100644 --- a/ash/services/secure_channel/secure_channel_unittest.cc +++ b/ash/services/secure_channel/secure_channel_unittest.cc
@@ -7,6 +7,10 @@ #include <memory> #include <string> +#include "ash/components/multidevice/fake_secure_message_delegate.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/secure_message_delegate_impl.h" #include "ash/services/secure_channel/fake_authenticator.h" #include "ash/services/secure_channel/fake_connection.h" #include "ash/services/secure_channel/fake_secure_context.h" @@ -17,10 +21,6 @@ #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" #include "base/test/bind.h" -#include "chromeos/components/multidevice/fake_secure_message_delegate.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/secure_message_delegate_impl.h" #include "testing/gtest/include/gtest/gtest.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/shared_resource_scheduler.cc b/ash/services/secure_channel/shared_resource_scheduler.cc index 85e968dd..52fb4059 100644 --- a/ash/services/secure_channel/shared_resource_scheduler.cc +++ b/ash/services/secure_channel/shared_resource_scheduler.cc
@@ -4,10 +4,10 @@ #include "ash/services/secure_channel/shared_resource_scheduler.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/containers/contains.h" #include "base/logging.h" #include "base/notreached.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/wire_message.cc b/ash/services/secure_channel/wire_message.cc index 10fafd41..3c0cf14 100644 --- a/ash/services/secure_channel/wire_message.cc +++ b/ash/services/secure_channel/wire_message.cc
@@ -9,6 +9,7 @@ #include <limits> +#include "ash/components/multidevice/logging/logging.h" #include "base/base64url.h" #include "base/big_endian.h" #include "base/containers/contains.h" @@ -17,7 +18,6 @@ #include "base/memory/ptr_util.h" #include "base/notreached.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash::secure_channel {
diff --git a/ash/shell.cc b/ash/shell.cc index 463d9ef..7ebc967 100644 --- a/ash/shell.cc +++ b/ash/shell.cc
@@ -127,6 +127,7 @@ #include "ash/system/network/sms_observer.h" #include "ash/system/night_light/night_light_controller_impl.h" #include "ash/system/pcie_peripheral/pcie_peripheral_notification_controller.h" +#include "ash/system/power/adaptive_charging_controller.h" #include "ash/system/power/backlights_forced_off_setter.h" #include "ash/system/power/peripheral_battery_notifier.h" #include "ash/system/power/power_button_controller.h" @@ -739,6 +740,8 @@ // Accesses root window containers. logout_confirmation_controller_.reset(); + adaptive_charging_controller_.reset(); + // Drag-and-drop must be canceled prior to close all windows. drag_drop_controller_.reset(); @@ -1197,6 +1200,11 @@ AddPreTargetHandler(mouse_cursor_filter_.get(), ui::EventTarget::Priority::kAccessibility); + if (features::IsAdaptiveChargingEnabled()) { + adaptive_charging_controller_ = + std::make_unique<AdaptiveChargingController>(); + } + // Create Controllers that may need root window. // TODO(oshima): Move as many controllers before creating // RootWindowController as possible.
diff --git a/ash/shell.h b/ash/shell.h index 9e20e2c..4fa6c2d 100644 --- a/ash/shell.h +++ b/ash/shell.h
@@ -86,6 +86,7 @@ class AccessibilityControllerImpl; class AccessibilityDelegate; class AccessibilityFocusRingControllerImpl; +class AdaptiveChargingController; class AmbientController; class AppListControllerImpl; class AppListFeatureUsageMetrics; @@ -742,6 +743,7 @@ std::unique_ptr<AccessibilityDelegate> accessibility_delegate_; std::unique_ptr<AccessibilityFocusRingControllerImpl> accessibility_focus_ring_controller_; + std::unique_ptr<AdaptiveChargingController> adaptive_charging_controller_; std::unique_ptr<AmbientController> ambient_controller_; std::unique_ptr<AppListControllerImpl> app_list_controller_; std::unique_ptr<AppListFeatureUsageMetrics> app_list_feature_usage_metrics_;
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h index d4d5280f..d1e9423 100644 --- a/ash/shell_delegate.h +++ b/ash/shell_delegate.h
@@ -16,6 +16,7 @@ #include "services/device/public/mojom/bluetooth_system.mojom-forward.h" #include "services/device/public/mojom/fingerprint.mojom-forward.h" #include "services/media_session/public/cpp/media_session_service.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" #include "ui/gfx/native_widget_types.h" #include "url/gurl.h" @@ -65,6 +66,11 @@ virtual std::unique_ptr<DesksTemplatesDelegate> CreateDesksTemplatesDelegate() const = 0; + // Returns the geolocation loader factory used to initialize geolocation + // provider. + virtual scoped_refptr<network::SharedURLLoaderFactory> + GetGeolocationSharedURLLoaderFactory() const = 0; + // Check whether the current tab of the browser window can go back. virtual bool CanGoBack(gfx::NativeWindow window) const = 0;
diff --git a/ash/system/geolocation/test_geolocation_url_loader_factory.cc b/ash/system/geolocation/test_geolocation_url_loader_factory.cc new file mode 100644 index 0000000..18b3c9d --- /dev/null +++ b/ash/system/geolocation/test_geolocation_url_loader_factory.cc
@@ -0,0 +1,71 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/system/geolocation/test_geolocation_url_loader_factory.h" + +#include <memory> + +#include "base/json/json_string_value_serializer.h" + +namespace ash { + +namespace { + +// Creates a serialized dictionary string of the geoposition. +std::string CreateResponseBody(const Geoposition& position) { + base::Value::Dict value; + if (position.accuracy) + value.Set("accuracy", position.accuracy); + + if (position.latitude && position.longitude) { + base::Value location(base::Value::Type::DICTIONARY); + location.SetDoubleKey("lat", position.latitude); + location.SetDoubleKey("lng", position.longitude); + value.Set("location", std::move(location)); + } + + if (position.error_code) { + base::Value error(base::Value::Type::DICTIONARY); + error.SetIntKey("error_code", position.error_code); + value.Set("error", std::move(error)); + } + + std::string serialized_response; + JSONStringValueSerializer serializer(&serialized_response); + serializer.Serialize(value); + return serialized_response; +} + +} // namespace + +TestGeolocationUrlLoaderFactory::TestGeolocationUrlLoaderFactory() = default; + +void TestGeolocationUrlLoaderFactory::CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& url_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { + test_url_loader_factory_.CreateLoaderAndStart( + std::move(receiver), request_id, options, url_request, std::move(client), + traffic_annotation); + test_url_loader_factory_.AddResponse(url_request.url.spec(), + CreateResponseBody(position_)); +} + +void TestGeolocationUrlLoaderFactory::Clone( + mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) { + NOTREACHED(); +} + +std::unique_ptr<network::PendingSharedURLLoaderFactory> +TestGeolocationUrlLoaderFactory::Clone() { + NOTREACHED(); + return nullptr; +} + +TestGeolocationUrlLoaderFactory::~TestGeolocationUrlLoaderFactory() = default; + +} // namespace ash
diff --git a/ash/system/geolocation/test_geolocation_url_loader_factory.h b/ash/system/geolocation/test_geolocation_url_loader_factory.h new file mode 100644 index 0000000..0653ee5 --- /dev/null +++ b/ash/system/geolocation/test_geolocation_url_loader_factory.h
@@ -0,0 +1,56 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_SYSTEM_GEOLOCATION_TEST_GEOLOCATION_URL_LOADER_FACTORY_H_ +#define ASH_SYSTEM_GEOLOCATION_TEST_GEOLOCATION_URL_LOADER_FACTORY_H_ + +#include "ash/components/geolocation/geoposition.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" + +namespace ash { + +// A fake SharedURLLoaderFactory that always responds with 200 OK status with a +// `geoposition_` chosen by the test client. +class TestGeolocationUrlLoaderFactory : public network::SharedURLLoaderFactory { + public: + TestGeolocationUrlLoaderFactory(); + + TestGeolocationUrlLoaderFactory(const TestGeolocationUrlLoaderFactory&) = + delete; + TestGeolocationUrlLoaderFactory& operator=( + const TestGeolocationUrlLoaderFactory&) = delete; + + // network::SharedURLLoaderFactory + void CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& url_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override; + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) + override; + std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override; + + void set_position(Geoposition position) { position_ = position; } + const Geoposition& position() const { return position_; } + + protected: + ~TestGeolocationUrlLoaderFactory() override; + + private: + // Used to control a server response data corresponding to a request url. + network::TestURLLoaderFactory test_url_loader_factory_; + + // The geoposition to be responded from this factory when a client makes a + // request to any url. + Geoposition position_; +}; + +} // namespace ash + +#endif // ASH_SYSTEM_GEOLOCATION_TEST_GEOLOCATION_URL_LOADER_FACTORY_H_
diff --git a/ash/system/phonehub/camera_roll_thumbnail.cc b/ash/system/phonehub/camera_roll_thumbnail.cc index 72a0d88..356b892 100644 --- a/ash/system/phonehub/camera_roll_thumbnail.cc +++ b/ash/system/phonehub/camera_roll_thumbnail.cc
@@ -4,12 +4,12 @@ #include "ash/system/phonehub/camera_roll_thumbnail.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/camera_roll_manager.h" #include "ash/components/phonehub/user_action_recorder.h" #include "ash/resources/vector_icons/vector_icons.h" #include "ash/style/ash_color_provider.h" #include "base/bind.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/views/controls/highlight_path_generator.h"
diff --git a/ash/system/phonehub/continue_browsing_chip.cc b/ash/system/phonehub/continue_browsing_chip.cc index 18e601e8..394cd3f 100644 --- a/ash/system/phonehub/continue_browsing_chip.cc +++ b/ash/system/phonehub/continue_browsing_chip.cc
@@ -4,6 +4,7 @@ #include "ash/system/phonehub/continue_browsing_chip.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/user_action_recorder.h" #include "ash/public/cpp/new_window_delegate.h" #include "ash/resources/vector_icons/vector_icons.h" @@ -17,7 +18,6 @@ #include "base/bind.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/views/controls/focus_ring.h"
diff --git a/ash/system/phonehub/multidevice_feature_opt_in_view.cc b/ash/system/phonehub/multidevice_feature_opt_in_view.cc index ad71cbb..fc9300d 100644 --- a/ash/system/phonehub/multidevice_feature_opt_in_view.cc +++ b/ash/system/phonehub/multidevice_feature_opt_in_view.cc
@@ -7,6 +7,7 @@ #include <memory> #include <string> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/multidevice_feature_access_manager.h" #include "ash/constants/ash_features.h" #include "ash/public/cpp/new_window_delegate.h" @@ -14,7 +15,6 @@ #include "ash/style/ash_color_provider.h" #include "ash/system/phonehub/phone_hub_metrics.h" #include "ash/system/phonehub/phone_hub_view_ids.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "ui/base/metadata/metadata_impl_macros.h" namespace ash {
diff --git a/ash/system/phonehub/phone_hub_notification_controller.cc b/ash/system/phonehub/phone_hub_notification_controller.cc index 02ea9d8..6a57be5d9 100644 --- a/ash/system/phonehub/phone_hub_notification_controller.cc +++ b/ash/system/phonehub/phone_hub_notification_controller.cc
@@ -4,6 +4,7 @@ #include "ash/system/phonehub/phone_hub_notification_controller.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/notification.h" #include "ash/components/phonehub/notification_interaction_handler.h" #include "ash/components/phonehub/phone_hub_manager.h" @@ -27,7 +28,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/timer/timer.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/display/types/display_constants.h"
diff --git a/ash/system/power/adaptive_charging_controller.cc b/ash/system/power/adaptive_charging_controller.cc new file mode 100644 index 0000000..8aa1a08 --- /dev/null +++ b/ash/system/power/adaptive_charging_controller.cc
@@ -0,0 +1,12 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/system/power/adaptive_charging_controller.h" + +namespace ash { + +AdaptiveChargingController::AdaptiveChargingController() = default; +AdaptiveChargingController::~AdaptiveChargingController() = default; + +} // namespace ash
diff --git a/ash/system/power/adaptive_charging_controller.h b/ash/system/power/adaptive_charging_controller.h new file mode 100644 index 0000000..c0af8a8 --- /dev/null +++ b/ash/system/power/adaptive_charging_controller.h
@@ -0,0 +1,25 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_SYSTEM_POWER_ADAPTIVE_CHARGING_CONTROLLER_H_ +#define ASH_SYSTEM_POWER_ADAPTIVE_CHARGING_CONTROLLER_H_ + +namespace ash { + +// The controller responsible for the adaptive charging toast and notifications, +// and communication with the power daemon. +// +// Is currently a stub. TODO(b:216035280): add in real logic. +class AdaptiveChargingController { + public: + AdaptiveChargingController(); + AdaptiveChargingController(const AdaptiveChargingController&) = delete; + AdaptiveChargingController& operator=(const AdaptiveChargingController&) = + delete; + ~AdaptiveChargingController(); +}; + +} // namespace ash + +#endif // ASH_SYSTEM_POWER_ADAPTIVE_CHARGING_CONTROLLER_H_
diff --git a/ash/system/unified/hps_notify_controller.cc b/ash/system/unified/hps_notify_controller.cc index 4f58533..8d42a55b 100644 --- a/ash/system/unified/hps_notify_controller.cc +++ b/ash/system/unified/hps_notify_controller.cc
@@ -84,7 +84,7 @@ user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); registry->RegisterBooleanPref( prefs::kSnoopingProtectionNotificationSuppressionEnabled, - /*default_value=*/false, + /*default_value=*/true, user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); }
diff --git a/ash/test_shell_delegate.cc b/ash/test_shell_delegate.cc index f4a2ed6..bbcda14 100644 --- a/ash/test_shell_delegate.cc +++ b/ash/test_shell_delegate.cc
@@ -10,6 +10,7 @@ #include "ash/capture_mode/test_capture_mode_delegate.h" #include "ash/public/cpp/test/test_desks_templates_delegate.h" #include "ash/public/cpp/test/test_nearby_share_delegate.h" +#include "ash/system/geolocation/test_geolocation_url_loader_factory.h" #include "ash/system/tray/system_tray_notifier.h" #include "ash/wm/gestures/back_gesture/test_back_gesture_contextual_nudge_delegate.h" #include "ui/gfx/image/image.h" @@ -51,6 +52,12 @@ return std::make_unique<TestDesksTemplatesDelegate>(); } +scoped_refptr<network::SharedURLLoaderFactory> +TestShellDelegate::GetGeolocationSharedURLLoaderFactory() const { + return static_cast<scoped_refptr<network::SharedURLLoaderFactory>>( + base::MakeRefCounted<TestGeolocationUrlLoaderFactory>()); +} + bool TestShellDelegate::CanGoBack(gfx::NativeWindow window) const { return can_go_back_; }
diff --git a/ash/test_shell_delegate.h b/ash/test_shell_delegate.h index 7f8ef21..10afec5 100644 --- a/ash/test_shell_delegate.h +++ b/ash/test_shell_delegate.h
@@ -44,6 +44,8 @@ NearbyShareController* controller) const override; std::unique_ptr<DesksTemplatesDelegate> CreateDesksTemplatesDelegate() const override; + scoped_refptr<network::SharedURLLoaderFactory> + GetGeolocationSharedURLLoaderFactory() const override; bool CanGoBack(gfx::NativeWindow window) const override; void SetTabScrubberChromeOSEnabled(bool enabled) override; bool ShouldWaitForTouchPressAck(gfx::NativeWindow window) override;
diff --git a/ash/webui/eche_app_ui/BUILD.gn b/ash/webui/eche_app_ui/BUILD.gn index a1b621c..dcb6a24 100644 --- a/ash/webui/eche_app_ui/BUILD.gn +++ b/ash/webui/eche_app_ui/BUILD.gn
@@ -76,6 +76,8 @@ deps = [ ":eche_app_ui_pref", "//ash", + "//ash/components/multidevice:multidevice", + "//ash/components/multidevice/logging", "//ash/components/phonehub", "//ash/constants", "//ash/public/cpp", @@ -88,8 +90,6 @@ "//ash/webui/eche_app_ui/proto", "//ash/webui/resources:eche_app_resources", "//ash/webui/resources:eche_bundle_resources", - "//chromeos/components/multidevice:multidevice", - "//chromeos/components/multidevice/logging", "//components/prefs", "//content/public/browser", "//mojo/public/js:resources", @@ -145,6 +145,8 @@ ":eche_app_ui_pref", ":test_support", "//ash:test_support", + "//ash/components/multidevice", + "//ash/components/multidevice:test_support", "//ash/components/phonehub", "//ash/components/phonehub:debug", "//ash/public/cpp", @@ -159,8 +161,6 @@ "//ash/webui/eche_app_ui/proto", "//base", "//base/test:test_support", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:test_support", "//components/prefs:test_support", "//components/sync_preferences:test_support", "//dbus",
diff --git a/ash/webui/eche_app_ui/apps_access_manager.cc b/ash/webui/eche_app_ui/apps_access_manager.cc index b434d88..fd41607 100644 --- a/ash/webui/eche_app_ui/apps_access_manager.cc +++ b/ash/webui/eche_app_ui/apps_access_manager.cc
@@ -4,10 +4,10 @@ #include "ash/webui/eche_app_ui/apps_access_manager.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/multidevice_feature_access_manager.h" #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h" #include "base/memory/ptr_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace eche_app {
diff --git a/ash/webui/eche_app_ui/apps_access_manager_impl.cc b/ash/webui/eche_app_ui/apps_access_manager_impl.cc index 016d80a..aa1860d 100644 --- a/ash/webui/eche_app_ui/apps_access_manager_impl.cc +++ b/ash/webui/eche_app_ui/apps_access_manager_impl.cc
@@ -4,12 +4,12 @@ #include "ash/webui/eche_app_ui/apps_access_manager_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/multidevice_feature_access_manager.h" #include "ash/constants/ash_features.h" #include "ash/services/multidevice_setup/public/cpp/prefs.h" #include "ash/webui/eche_app_ui/pref_names.h" #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/ash/webui/eche_app_ui/eche_app_manager_unittest.cc b/ash/webui/eche_app_ui/eche_app_manager_unittest.cc index 7525f8c..c981523 100644 --- a/ash/webui/eche_app_ui/eche_app_manager_unittest.cc +++ b/ash/webui/eche_app_ui/eche_app_manager_unittest.cc
@@ -6,6 +6,8 @@ #include <vector> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/phonehub/fake_phone_hub_manager.h" #include "ash/components/phonehub/phone_hub_manager.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" @@ -20,8 +22,6 @@ #include "base/callback_helpers.h" #include "base/run_loop.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/prefs/testing_pref_service.h" #include "device/bluetooth/dbus/bluez_dbus_manager.h" #include "device/bluetooth/dbus/fake_bluetooth_debug_manager_client.h"
diff --git a/ash/webui/eche_app_ui/eche_connector_impl.cc b/ash/webui/eche_app_ui/eche_connector_impl.cc index dead723f..917b18ff 100644 --- a/ash/webui/eche_app_ui/eche_connector_impl.cc +++ b/ash/webui/eche_app_ui/eche_connector_impl.cc
@@ -4,13 +4,13 @@ #include "ash/webui/eche_app_ui/eche_connector_impl.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/components/phonehub/phone_hub_manager.h" #include "ash/services/secure_channel/public/cpp/client/connection_manager.h" #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" namespace ash { namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_display_stream_handler.cc b/ash/webui/eche_app_ui/eche_display_stream_handler.cc index 7d2f1ee..0618a38 100644 --- a/ash/webui/eche_app_ui/eche_display_stream_handler.cc +++ b/ash/webui/eche_app_ui/eche_display_stream_handler.cc
@@ -4,8 +4,8 @@ #include "ash/webui/eche_app_ui/eche_display_stream_handler.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/webui/eche_app_ui/launch_app_helper.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_feature_status_provider.cc b/ash/webui/eche_app_ui/eche_feature_status_provider.cc index 780bf76..27217aa7 100644 --- a/ash/webui/eche_app_ui/eche_feature_status_provider.cc +++ b/ash/webui/eche_app_ui/eche_feature_status_provider.cc
@@ -4,13 +4,13 @@ #include "ash/webui/eche_app_ui/eche_feature_status_provider.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/software_feature.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/components/phonehub/feature_status.h" #include "ash/components/phonehub/phone_hub_manager.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/software_feature.h" -#include "chromeos/components/multidevice/software_feature_state.h" namespace ash { namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc b/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc index abbe884..799dae75 100644 --- a/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc +++ b/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc
@@ -7,13 +7,13 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/phonehub/fake_phone_hub_manager.h" #include "ash/components/phonehub/phone_hub_manager.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" #include "ash/services/secure_channel/public/cpp/client/fake_connection_manager.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/webui/eche_app_ui/eche_notification_click_handler.cc b/ash/webui/eche_app_ui/eche_notification_click_handler.cc index 6b58bbe8..ffd190e 100644 --- a/ash/webui/eche_app_ui/eche_notification_click_handler.cc +++ b/ash/webui/eche_app_ui/eche_notification_click_handler.cc
@@ -4,13 +4,13 @@ #include "ash/webui/eche_app_ui/eche_notification_click_handler.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/phone_hub_manager.h" #include "ash/constants/ash_features.h" #include "ash/root_window_controller.h" #include "ash/shell.h" #include "ash/system/eche/eche_tray.h" #include "ash/webui/eche_app_ui/launch_app_helper.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_notification_generator.cc b/ash/webui/eche_app_ui/eche_notification_generator.cc index 7b184a5..b395d46 100644 --- a/ash/webui/eche_app_ui/eche_notification_generator.cc +++ b/ash/webui/eche_app_ui/eche_notification_generator.cc
@@ -4,8 +4,8 @@ #include "ash/webui/eche_app_ui/eche_notification_generator.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/webui/eche_app_ui/launch_app_helper.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_presence_manager.cc b/ash/webui/eche_app_ui/eche_presence_manager.cc index 79b75a9..8ed50fd 100644 --- a/ash/webui/eche_app_ui/eche_presence_manager.cc +++ b/ash/webui/eche_app_ui/eche_presence_manager.cc
@@ -4,12 +4,12 @@ #include "ash/webui/eche_app_ui/eche_presence_manager.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/secure_channel/public/cpp/client/presence_monitor_client.h" #include "ash/webui/eche_app_ui/eche_connector.h" #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device_ref.h" namespace ash { namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc b/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc index 0c12c82..e54ba588 100644 --- a/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc +++ b/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc
@@ -4,6 +4,7 @@ #include "ash/webui/eche_app_ui/eche_presence_manager.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/constants/ash_features.h" #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" @@ -14,7 +15,6 @@ #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/prefs/testing_pref_service.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc b/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc index ae5fe2c..9198674 100644 --- a/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc +++ b/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc
@@ -4,12 +4,12 @@ #include "ash/webui/eche_app_ui/eche_recent_app_click_handler.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/phone_hub_manager.h" #include "ash/root_window_controller.h" #include "ash/shell.h" #include "ash/system/eche/eche_tray.h" #include "ash/webui/eche_app_ui/launch_app_helper.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_signaler.cc b/ash/webui/eche_app_ui/eche_signaler.cc index 9e4bdfc..6f0680a0 100644 --- a/ash/webui/eche_app_ui/eche_signaler.cc +++ b/ash/webui/eche_app_ui/eche_signaler.cc
@@ -4,8 +4,8 @@ #include "ash/webui/eche_app_ui/eche_signaler.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_signaler_unittest.cc b/ash/webui/eche_app_ui/eche_signaler_unittest.cc index e4581534..4fa274fe 100644 --- a/ash/webui/eche_app_ui/eche_signaler_unittest.cc +++ b/ash/webui/eche_app_ui/eche_signaler_unittest.cc
@@ -7,11 +7,11 @@ #include <memory> #include <vector> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/public/cpp/client/fake_connection_manager.h" #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h" #include "base/task/single_thread_task_runner.h" #include "base/test/task_environment.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h"
diff --git a/ash/webui/eche_app_ui/eche_uid_provider.cc b/ash/webui/eche_app_ui/eche_uid_provider.cc index 5fec7cd..af83df8a 100644 --- a/ash/webui/eche_app_ui/eche_uid_provider.cc +++ b/ash/webui/eche_app_ui/eche_uid_provider.cc
@@ -8,8 +8,8 @@ #include <openssl/base64.h> #include <cstring> +#include "ash/components/multidevice/logging/logging.h" #include "base/check.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_service.h" #include "crypto/random.h"
diff --git a/ash/webui/eche_app_ui/system_info_provider.cc b/ash/webui/eche_app_ui/system_info_provider.cc index 46fe1c7..9878cc20 100644 --- a/ash/webui/eche_app_ui/system_info_provider.cc +++ b/ash/webui/eche_app_ui/system_info_provider.cc
@@ -4,6 +4,7 @@ #include "ash/webui/eche_app_ui/system_info_provider.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/constants/ash_features.h" #include "ash/public/cpp/tablet_mode.h" #include "ash/public/cpp/tablet_mode_observer.h" @@ -12,7 +13,6 @@ #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h" namespace ash {
diff --git a/ash/webui/multidevice_debug/BUILD.gn b/ash/webui/multidevice_debug/BUILD.gn index 1ffd71f..e6dc35a6 100644 --- a/ash/webui/multidevice_debug/BUILD.gn +++ b/ash/webui/multidevice_debug/BUILD.gn
@@ -17,6 +17,7 @@ ] deps = [ + "//ash/components/multidevice/logging", "//ash/constants", "//ash/services/device_sync/proto", "//ash/services/device_sync/proto:util", @@ -27,7 +28,6 @@ "//ash/webui/resources:multidevice_debug_resources", "//base", "//base:i18n", - "//chromeos/components/multidevice/logging", "//components/prefs", "//components/resources", "//content/public/browser",
diff --git a/ash/webui/multidevice_debug/OWNERS b/ash/webui/multidevice_debug/OWNERS index 7027ab73..18377141 100644 --- a/ash/webui/multidevice_debug/OWNERS +++ b/ash/webui/multidevice_debug/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc b/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc index 9b50a8cf..0f9bf7f4 100644 --- a/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc +++ b/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc
@@ -9,6 +9,8 @@ #include <sstream> #include <utility> +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/services/device_sync/proto/enum_util.h" #include "base/base64url.h" #include "base/bind.h" @@ -17,8 +19,6 @@ #include "base/time/default_clock.h" #include "base/time/default_tick_clock.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "components/prefs/pref_service.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_ui.h"
diff --git a/ash/webui/multidevice_debug/proximity_auth_webui_handler.h b/ash/webui/multidevice_debug/proximity_auth_webui_handler.h index ce651772..eb12f15 100644 --- a/ash/webui/multidevice_debug/proximity_auth_webui_handler.h +++ b/ash/webui/multidevice_debug/proximity_auth_webui_handler.h
@@ -5,11 +5,11 @@ #ifndef ASH_WEBUI_MULTIDEVICE_DEBUG_PROXIMITY_AUTH_WEBUI_HANDLER_H_ #define ASH_WEBUI_MULTIDEVICE_DEBUG_PROXIMITY_AUTH_WEBUI_HANDLER_H_ +#include "ash/components/multidevice/logging/log_buffer.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "base/memory/weak_ptr.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/log_buffer.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "content/public/browser/web_ui_message_handler.h" #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/webui/multidevice_debug/resources/proximity_auth.html b/ash/webui/multidevice_debug/resources/proximity_auth.html index 1a5beb2..19340f37 100644 --- a/ash/webui/multidevice_debug/resources/proximity_auth.html +++ b/ash/webui/multidevice_debug/resources/proximity_auth.html
@@ -15,7 +15,7 @@ </script> <script src="chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js"> </script> - <script src="chrome://resources/mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js"> + <script src="chrome://resources/mojo/ash/components/multidevice/mojom/multidevice_types.mojom-lite.js"> </script> <script src="chrome://resources/mojo/ash/services/device_sync/public/mojom/device_sync.mojom-lite.js"> </script>
diff --git a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts index d637089..5b8b207b 100644 --- a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts +++ b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
@@ -369,7 +369,7 @@ } getImageUrlForEmptyTile_(tile: ImageTile): string { - return `//personalization/common/${ + return `chrome://personalization/common/${ (this.isGooglePhotosTile_(tile) ? 'google_photos.svg' : 'no_images.svg')}`; }
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc index 9b6d841..3b50e99 100644 --- a/ash/wm/desks/desks_controller.cc +++ b/ash/wm/desks/desks_controller.cc
@@ -49,7 +49,6 @@ #include "base/containers/contains.h" #include "base/containers/unique_ptr_adapters.h" #include "base/cxx17_backports.h" -#include "base/guid.h" #include "base/i18n/number_formatting.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -266,12 +265,6 @@ int count_ = 0; }; -DesksController::Call::Call() - : data(std::make_unique<app_restore::RestoreData>()) {} -DesksController::Call::Call(DesksController::Call&&) = default; -DesksController::Call& DesksController::Call::operator=(Call&&) = default; -DesksController::Call::~Call() = default; - DesksController::DesksController() : metrics_helper_(std::make_unique<DeskTraversalsMetricsHelper>(this)) { Shell::Get()->activation_client()->AddObserver(this); @@ -914,54 +907,9 @@ aura::Window* root_window_to_show) const { DCHECK(current_account_id_.is_valid()); - // Construct RestoreData for |desk_template|. - const auto current_serial = serial_++; - auto emplace_result = calls_.emplace(current_serial, Call{}); - DCHECK(emplace_result.second); - Call& call = emplace_result.first->second; - - auto* shell = Shell::Get(); - auto mru_windows = - shell->mru_window_tracker()->BuildMruWindowList(kActiveDesk); - auto* delegate = shell->desks_templates_delegate(); - for (auto* window : mru_windows) { - if (!delegate->IsWindowSupportedForDeskTemplate(window) && - !wm::GetTransientParent(window)) { - call.unsupported_apps.push_back(window); - continue; - } - - // Exclude window that does not asscociate with a full restore app id, - // silently omitting them. - const std::string app_id = full_restore::GetAppId(window); - if (app_id.empty()) - continue; - - // We need to copy |app_launch_info->app_id| to |app_id| as the below - // function AddAppLaunchInfo() will destroy |app_launch_info|. - const int32_t window_id = window->GetProperty(app_restore::kWindowIdKey); - std::unique_ptr<app_restore::WindowInfo> window_info = BuildWindowInfo( - window, /*activation_index=*/absl::nullopt, mru_windows); - // Clear WindowInfo's `desk_id` in the template. It will later be set to the - // id of a newly created desk when launching. - window_info->desk_id.reset(); - - // The delegate may call back OnAppLaunchDataReceived() synchronously, which - // will decrement the counter, that is why we should increment it before - // placing the call. - ++call.pending_request_count; - delegate->GetAppLaunchDataForDeskTemplate( - window, base::BindOnce(&DesksController::OnAppLaunchDataReceived, - base::Unretained(this), current_serial, app_id, - window_id, std::move(window_info))); - } - - call.callback = std::move(callback); - - // If all requests in the loop above returned data synchronously, then we have - // no pending requests and send the data right away. - if (call.pending_request_count == 0) - SendRestoreData(current_serial, root_window_to_show); + restore_data_collector_.CaptureActiveDeskAsTemplate( + std::move(callback), base::UTF16ToUTF8(active_desk_->name()), + root_window_to_show); } void DesksController::CreateAndActivateNewDeskForTemplate( @@ -1633,63 +1581,4 @@ &DesksController::RecordAndResetNumberOfWeeklyActiveDesks); } -void DesksController::OnAppLaunchDataReceived( - uint32_t serial, - const std::string app_id, - const int32_t window_id, - std::unique_ptr<app_restore::WindowInfo> window_info, - std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info) const { - auto call_it = calls_.find(serial); - DCHECK(call_it != calls_.end()); - Call& call = call_it->second; - - DCHECK(call.data); - DCHECK_GT(call.pending_request_count, 0u); - - --call.pending_request_count; - - // nullptr means that this app does not have data to save. - if (app_launch_info) { - call.data->AddAppLaunchInfo(std::move(app_launch_info)); - call.data->ModifyWindowInfo(app_id, window_id, *window_info); - } - - // Null callback here means that the loop in CaptureActiveDeskAsTemplate() has - // not yet finished polling the windows. Non-zero pending request count means - // that some of preceding requests were asynchronous. - if (call.pending_request_count > 0 || call.callback.is_null()) - return; - - // TODO(crbug.com/1268741): get a better root window. Originally it is taken - // from the desk template UI that is active when the user clicks the button, - // but here we are in the asynchronous handler, and that window may have been - // destroyed already. - SendRestoreData(serial, Shell::Get()->GetPrimaryRootWindow()); -} - -void DesksController::SendRestoreData(uint32_t serial, - aura::Window* root_window_to_show) const { - auto call_it = calls_.find(serial); - DCHECK(call_it != calls_.end()); - Call& call = call_it->second; - - auto desk_template = std::make_unique<DeskTemplate>( - base::GUID::GenerateRandomV4().AsLowercaseString(), - DeskTemplateSource::kUser, base::UTF16ToUTF8(active_desk_->name()), - base::Time::Now()); - desk_template->set_desk_restore_data(std::move(call.data)); - - if (!call.unsupported_apps.empty() && - Shell::Get()->overview_controller()->InOverviewSession()) { - // There were some unsupported apps in the active desk so open up a dialog - // to let the user know. - DesksTemplatesDialogController::Get()->ShowUnsupportedAppsDialog( - root_window_to_show, call.unsupported_apps, std::move(call.callback), - std::move(desk_template)); - } else { - std::move(call.callback).Run(std::move(desk_template)); - } - calls_.erase(serial); -} - } // namespace ash
diff --git a/ash/wm/desks/desks_controller.h b/ash/wm/desks/desks_controller.h index f21214b..d74a686 100644 --- a/ash/wm/desks/desks_controller.h +++ b/ash/wm/desks/desks_controller.h
@@ -14,6 +14,7 @@ #include "ash/public/cpp/session/session_observer.h" #include "ash/wm/desks/desks_histogram_enums.h" #include "ash/wm/desks/root_window_desk_switch_animator.h" +#include "ash/wm/desks/templates/restore_data_collector.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/observer_list.h" @@ -309,19 +310,6 @@ friend class DeskRemovalAnimation; friend class DesksTemplatesTest; - // Keeps the state for the asynchronous call for AppLaunchData to the clients. - struct Call { - Call(); - Call(Call&&); - Call& operator=(Call&&); - ~Call(); - - std::vector<aura::Window*> unsupported_apps; - std::unique_ptr<app_restore::RestoreData> data; - uint32_t pending_request_count = 0; - GetDeskTemplateCallback callback; - }; - void set_disable_app_id_check_for_desk_templates( bool disable_app_id_check_for_desk_templates) { disable_app_id_check_for_desk_templates_ = @@ -379,23 +367,6 @@ // |interacted_with_this_week_| field for each inactive desk in |desks_|. void RecordAndResetNumberOfWeeklyActiveDesks(); - // Receives the AppLaunchInfo from the single client and puts it into the - // RestoreData record where data from all clients is accumulated. If all data - // is collected, invokes SendAppLaunchData(). - // TODO(crbug.com/1268741): extract this, together with the `calls_` and the - // relevant methods, into a separate class. - void OnAppLaunchDataReceived( - uint32_t serial, - const std::string app_id, - const int32_t window_id, - std::unique_ptr<app_restore::WindowInfo> window_info, - std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info) const; - - // Sends the RestoreData to the consumer after all clients deliver their - // AppLaunchInfo. - void SendRestoreData(uint32_t serial, - aura::Window* root_window_to_show) const; - std::vector<std::unique_ptr<Desk>> desks_; Desk* active_desk_ = nullptr; @@ -441,12 +412,8 @@ // Scheduler for reporting the weekly active desks metric. base::OneShotTimer weekly_active_desks_scheduler_; - // Data to put into desk template. Mutable because it is not part of the - // state but it can live between asynchronous calls. - // Because gathering the data is asynchronous, we maintain a map of requests - // identified by serial number of the request that comes from the UI. - mutable uint32_t serial_ = 0; - mutable base::flat_map<uint32_t, Call> calls_; + // Does the job for the `CaptureActiveDeskAsTemplate()` method. + mutable RestoreDataCollector restore_data_collector_; }; } // namespace ash
diff --git a/ash/wm/desks/templates/desks_templates_dialog_controller.cc b/ash/wm/desks/templates/desks_templates_dialog_controller.cc index bac4b5de..c65e1ed1 100644 --- a/ash/wm/desks/templates/desks_templates_dialog_controller.cc +++ b/ash/wm/desks/templates/desks_templates_dialog_controller.cc
@@ -4,6 +4,7 @@ #include "ash/wm/desks/templates/desks_templates_dialog_controller.h" +#include "ash/constants/app_types.h" #include "ash/public/cpp/desks_templates_delegate.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" @@ -15,6 +16,7 @@ #include "ash/wm/overview/overview_controller.h" #include "ash/wm/overview/overview_grid.h" #include "base/bind.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/base/l10n/l10n_util.h" @@ -151,10 +153,17 @@ unsupported_apps_template_ = std::move(desk_template); size_t incognito_window_count = 0; + bool contains_lacros_window = false; auto* delegate = Shell::Get()->desks_templates_delegate(); // TODO(shidi): The caller of ShowUnsupportedAppsDialog should provide us // with the incognito window count to avoid double looping. for (auto* window : unsupported_apps) { + if (static_cast<AppType>(window->GetProperty(aura::client::kAppType)) == + AppType::LACROS) { + contains_lacros_window = true; + break; + } + if (delegate->IsIncognitoWindow(window)) ++incognito_window_count; } @@ -163,7 +172,10 @@ // are linux apps. std::u16string app_description; int app_description_id; - if (incognito_window_count == 0) { + if (contains_lacros_window) { + app_description_id = + IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION; + } else if (incognito_window_count == 0) { app_description_id = IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LINUX_APPS_DIALOG_DESCRIPTION; } else if (incognito_window_count != unsupported_apps.size()) {
diff --git a/ash/wm/desks/templates/restore_data_collector.cc b/ash/wm/desks/templates/restore_data_collector.cc new file mode 100644 index 0000000..a38957b --- /dev/null +++ b/ash/wm/desks/templates/restore_data_collector.cc
@@ -0,0 +1,146 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wm/desks/templates/restore_data_collector.h" + +#include "ash/public/cpp/desks_templates_delegate.h" +#include "ash/shell.h" +#include "ash/wm/desks/templates/desks_templates_dialog_controller.h" +#include "ash/wm/mru_window_tracker.h" +#include "ash/wm/overview/overview_controller.h" +#include "ash/wm/window_restore/window_restore_util.h" +#include "ash/wm/window_util.h" +#include "base/guid.h" +#include "components/app_restore/app_launch_info.h" +#include "components/app_restore/full_restore_utils.h" +#include "components/app_restore/restore_data.h" +#include "components/app_restore/window_info.h" +#include "components/app_restore/window_properties.h" + +namespace ash { + +RestoreDataCollector::Call::Call() + : data(std::make_unique<app_restore::RestoreData>()) {} +RestoreDataCollector::Call::Call(RestoreDataCollector::Call&&) = default; +RestoreDataCollector::Call& RestoreDataCollector::Call::operator=(Call&&) = + default; +RestoreDataCollector::Call::~Call() = default; + +RestoreDataCollector::RestoreDataCollector() = default; +RestoreDataCollector::~RestoreDataCollector() = default; + +void RestoreDataCollector::CaptureActiveDeskAsTemplate( + GetDeskTemplateCallback callback, + const std::string& template_name, + aura::Window* root_window_to_show) { + const auto current_serial = serial_++; + auto emplace_result = calls_.emplace(current_serial, Call{}); + DCHECK(emplace_result.second); + Call& call = emplace_result.first->second; + + if (root_window_to_show) + window_tracker_.Add(root_window_to_show); + call.root_window_to_show = root_window_to_show; + call.template_name = template_name; + + auto* const shell = Shell::Get(); + auto mru_windows = + shell->mru_window_tracker()->BuildMruWindowList(kActiveDesk); + auto* delegate = shell->desks_templates_delegate(); + for (auto* window : mru_windows) { + if (!delegate->IsWindowSupportedForDeskTemplate(window) && + !wm::GetTransientParent(window)) { + call.unsupported_apps.push_back(window); + continue; + } + + // Skip windows that do not associate with a full restore app id. + const std::string app_id = full_restore::GetAppId(window); + if (app_id.empty()) + continue; + + const int32_t window_id = window->GetProperty(app_restore::kWindowIdKey); + std::unique_ptr<app_restore::WindowInfo> window_info = BuildWindowInfo( + window, /*activation_index=*/absl::nullopt, mru_windows); + // Clear the desk ID in the WindowInfo that is to be stored in the template. + // It will be set to the ID of a newly created desk when launching. + window_info->desk_id.reset(); + + ++call.pending_request_count; + delegate->GetAppLaunchDataForDeskTemplate( + window, base::BindOnce(&RestoreDataCollector::OnAppLaunchDataReceived, + base::Unretained(this), current_serial, app_id, + window_id, std::move(window_info))); + } + + call.callback = std::move(callback); + + // If all requests in the loop above returned data synchronously, then we have + // no pending requests and send the data right away. Otherwise it will be + // sent after the last pending request is handled. + if (call.pending_request_count == 0) + SendDeskTemplate(current_serial); +} + +void RestoreDataCollector::OnAppLaunchDataReceived( + uint32_t serial, + const std::string app_id, + const int32_t window_id, + std::unique_ptr<app_restore::WindowInfo> window_info, + std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info) { + auto call_it = calls_.find(serial); + DCHECK(call_it != calls_.end()); + Call& call = call_it->second; + + DCHECK(call.data); + DCHECK_GT(call.pending_request_count, 0u); + + --call.pending_request_count; + + // nullptr means that this app does not have any data to save. + if (app_launch_info) { + call.data->AddAppLaunchInfo(std::move(app_launch_info)); + call.data->ModifyWindowInfo(app_id, window_id, *window_info); + } + + // Null callback here means that the loop in `CaptureActiveDeskAsTemplate()` + // has not yet finished polling the windows. Non-zero pending request count + // means that some of preceding requests were asynchronous. + if (call.pending_request_count == 0 && !call.callback.is_null()) + SendDeskTemplate(serial); +} + +void RestoreDataCollector::SendDeskTemplate(uint32_t serial) { + auto call_it = calls_.find(serial); + DCHECK(call_it != calls_.end()); + Call& call = call_it->second; + + auto desk_template = std::make_unique<DeskTemplate>( + base::GUID::GenerateRandomV4().AsLowercaseString(), + DeskTemplateSource::kUser, call.template_name, base::Time::Now()); + desk_template->set_desk_restore_data(std::move(call.data)); + + if (!call.unsupported_apps.empty() && + Shell::Get()->overview_controller()->InOverviewSession()) { + // The ideal root window may have gone by now. In that case fall back to + // the primary root one. + auto* root_window_to_show = call.root_window_to_show; + if (root_window_to_show && window_tracker_.Contains(root_window_to_show)) + window_tracker_.Remove(root_window_to_show); + else + root_window_to_show = Shell::Get()->GetPrimaryRootWindow(); + + // There were some unsupported apps in the active desk so open up a dialog + // to let the user know. + DesksTemplatesDialogController::Get()->ShowUnsupportedAppsDialog( + root_window_to_show, std::move(call.unsupported_apps), + std::move(call.callback), std::move(desk_template)); + } else { + std::move(call.callback).Run(std::move(desk_template)); + } + + calls_.erase(call_it); +} + +} // namespace ash
diff --git a/ash/wm/desks/templates/restore_data_collector.h b/ash/wm/desks/templates/restore_data_collector.h new file mode 100644 index 0000000..35740a0 --- /dev/null +++ b/ash/wm/desks/templates/restore_data_collector.h
@@ -0,0 +1,88 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WM_DESKS_TEMPLATES_RESTORE_DATA_COLLECTOR_H_ +#define ASH_WM_DESKS_TEMPLATES_RESTORE_DATA_COLLECTOR_H_ + +#include <memory> +#include <vector> + +#include "base/callback.h" +#include "base/containers/flat_map.h" +#include "base/memory/weak_ptr.h" +#include "ui/aura/window_tracker.h" + +namespace app_restore { +class RestoreData; +struct AppLaunchInfo; +struct WindowInfo; +} // namespace app_restore + +namespace aura { +class Window; +} + +namespace ash { + +class DeskTemplate; + +// Collects `AppLaunchData` from all applications that are currently active, and +// returns it in form of `DeskTemplate` record. +class RestoreDataCollector { + public: + using GetDeskTemplateCallback = + base::OnceCallback<void(std::unique_ptr<DeskTemplate>)>; + + RestoreDataCollector(); + RestoreDataCollector(const RestoreDataCollector&) = delete; + RestoreDataCollector& operator=(const RestoreDataCollector&) = delete; + ~RestoreDataCollector(); + + // Captures the active desk and returns it as a `DeskTemplate` object via the + // `callback`. + void CaptureActiveDeskAsTemplate(GetDeskTemplateCallback callback, + const std::string& template_name, + aura::Window* root_window_to_show); + + private: + // Keeps the state for the asynchronous call for `AppLaunchData` to the apps. + struct Call { + Call(); + Call(Call&&); + Call& operator=(Call&&); + ~Call(); + + std::string template_name; + aura::Window* root_window_to_show; + std::vector<aura::Window*> unsupported_apps; + std::unique_ptr<app_restore::RestoreData> data; + uint32_t pending_request_count = 0; + GetDeskTemplateCallback callback; + }; + + // Receives the `AppLaunchInfo` for the single app and puts it into the + // RestoreData record where data from all clients is accumulated. If all data + // is collected, invokes the `SendDeskTemplate()` method. + void OnAppLaunchDataReceived( + uint32_t serial, + const std::string app_id, + const int32_t window_id, + std::unique_ptr<app_restore::WindowInfo> window_info, + std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info); + + // Creates a `DeskTemplate` object and sends it to the consumer after all apps + // have delivered their `AppLaunchInfo`. + void SendDeskTemplate(uint32_t serial); + + // Auxiliary data for maintaining the asynchronous polling of the windows. + uint32_t serial_ = 0; + base::flat_map<uint32_t, Call> calls_; + aura::WindowTracker window_tracker_; + + base::WeakPtrFactory<RestoreDataCollector> weak_factory_{this}; +}; + +} // namespace ash + +#endif // #define ASH_WM_DESKS_TEMPLATES_RESTORE_DATA_COLLECTOR_H_
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc index 96c74ad..c9ae1fda 100644 --- a/ash/wm/overview/overview_session.cc +++ b/ash/wm/overview/overview_session.cc
@@ -1037,6 +1037,12 @@ UpdateNoWindowsWidgetOnEachGrid(); UpdateAccessibilityFocus(); + + // TODO(crbug.com/1307467): This doesn't need to be reset if it's an ancestor + // of the desks bar view. Also, add testing for this. Note that this isn't + // needed when hiding, because we either move the focus to the new desk, or + // delete all the grid templates items which would reset their highlights. + highlight_controller_->ResetHighlightedView(); } void OverviewSession::HideDesksTemplatesGrids() {
diff --git a/base/BUILD.gn b/base/BUILD.gn index bec4315b..29f74fb8 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -4160,7 +4160,6 @@ "android/java/src/org/chromium/base/annotations/CalledByNative.java", "android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java", "android/java/src/org/chromium/base/annotations/CheckDiscard.java", - "android/java/src/org/chromium/base/annotations/DoNotClassMerge.java", "android/java/src/org/chromium/base/annotations/DoNotInline.java", "android/java/src/org/chromium/base/annotations/IdentifierNameString.java", "android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java", @@ -4393,6 +4392,7 @@ "test/android/javatests/src/org/chromium/base/test/util/DisableIf.java", "test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java", "test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java", + "test/android/javatests/src/org/chromium/base/test/util/DoNotRevive.java", "test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java", "test/android/javatests/src/org/chromium/base/test/util/Feature.java", "test/android/javatests/src/org/chromium/base/test/util/FlakyTest.java",
diff --git a/base/allocator/partition_allocator/partition_page.h b/base/allocator/partition_allocator/partition_page.h index 5ee5e2c..9419fa4c 100644 --- a/base/allocator/partition_allocator/partition_page.h +++ b/base/allocator/partition_allocator/partition_page.h
@@ -63,8 +63,8 @@ // CAUTION! |extent| must point to the extent of the first super page in the // range of consecutive super pages. template <bool thread_safe> -ALWAYS_INLINE uintptr_t -SuperPagesBeginFromExtent(PartitionSuperPageExtentEntry<thread_safe>* extent) { +ALWAYS_INLINE uintptr_t SuperPagesBeginFromExtent( + const PartitionSuperPageExtentEntry<thread_safe>* extent) { PA_DCHECK(0 < extent->number_of_consecutive_super_pages); uintptr_t extent_as_uintptr = reinterpret_cast<uintptr_t>(extent); PA_DCHECK(IsManagedByNormalBuckets(extent_as_uintptr)); @@ -77,8 +77,8 @@ // CAUTION! |extent| must point to the extent of the first super page in the // range of consecutive super pages. template <bool thread_safe> -ALWAYS_INLINE uintptr_t -SuperPagesEndFromExtent(PartitionSuperPageExtentEntry<thread_safe>* extent) { +ALWAYS_INLINE uintptr_t SuperPagesEndFromExtent( + const PartitionSuperPageExtentEntry<thread_safe>* extent) { return SuperPagesBeginFromExtent(extent) + (extent->number_of_consecutive_super_pages * kSuperPageSize); }
diff --git a/base/allocator/partition_allocator/thread_cache.h b/base/allocator/partition_allocator/thread_cache.h index d7c2974..0d77f4f 100644 --- a/base/allocator/partition_allocator/thread_cache.h +++ b/base/allocator/partition_allocator/thread_cache.h
@@ -56,6 +56,7 @@ constexpr size_t kThreadCacheNeedleArraySize = 4; extern uintptr_t kThreadCacheNeedleArray[kThreadCacheNeedleArraySize]; +class HeapDumper; class ThreadCacheInspector; } // namespace partition_alloc::internal::tools @@ -148,6 +149,7 @@ private: friend class partition_alloc::internal::tools::ThreadCacheInspector; + friend class partition_alloc::internal::tools::HeapDumper; friend class NoDestructor<ThreadCacheRegistry>; // Not using base::Lock as the object's constructor must be constexpr. PartitionLock lock_; @@ -319,6 +321,7 @@ ThreadCacheLimits::kLargeSizeThreshold; private: + friend class partition_alloc::internal::tools::HeapDumper; friend class partition_alloc::internal::tools::ThreadCacheInspector; struct Bucket {
diff --git a/base/android/java/src/org/chromium/base/annotations/DoNotClassMerge.java b/base/android/java/src/org/chromium/base/annotations/DoNotClassMerge.java deleted file mode 100644 index 73c0516..0000000 --- a/base/android/java/src/org/chromium/base/annotations/DoNotClassMerge.java +++ /dev/null
@@ -1,20 +0,0 @@ -// Copyright 2022 The Chromium 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 org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The annotated class should never be horizontally or vertically merged. - * - * The annotated classes are guaranteed not to be horizontally or vertically - * merged by Proguard. Other optimizations may still apply. - */ -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.CLASS) -public @interface DoNotClassMerge {}
diff --git a/base/android/proguard/chromium_code.flags b/base/android/proguard/chromium_code.flags index b3273e2..81d446c 100644 --- a/base/android/proguard/chromium_code.flags +++ b/base/android/proguard/chromium_code.flags
@@ -64,9 +64,9 @@ static boolean isDebug() return false; } -# Never inline classes, methods, or fields with this annotation, but allow +# Never inline classes, methods, or fields with this annotation, but allow # shrinking and obfuscation. -# Relevant to fields when they are needed to store strong references to objects +# Relevant to fields when they are needed to store strong refrences to objects # that are held as weak references by native code. -if @org.chromium.base.annotations.DoNotInline class * { *** *(...); @@ -81,11 +81,6 @@ @org.chromium.base.annotations.DoNotInline <fields>; } -# Never merge classes horizontally or vertically with this annotation. -# Relevant to classes being used as a key in maps or sets. --nohorizontalclassmerging @org.chromium.base.annotations.DoNotClassMerge class * --noverticalclassmerging @org.chromium.base.annotations.DoNotClassMerge class * - # Keep all CREATOR fields within Parcelable that are kept. -keepclassmembers class org.chromium.** implements android.os.Parcelable { public static *** CREATOR;
diff --git a/base/memory/shared_memory_mapping.h b/base/memory/shared_memory_mapping.h index 4d9f10a..254a28d1 100644 --- a/base/memory/shared_memory_mapping.h +++ b/base/memory/shared_memory_mapping.h
@@ -8,7 +8,6 @@ #include <cstddef> #include <type_traits> -#include "base/containers/buffer_iterator.h" #include "base/containers/span.h" #include "base/unguessable_token.h" @@ -151,12 +150,6 @@ return span<const T>(static_cast<const T*>(raw_memory_ptr()), count); } - // Returns a BufferIterator of const T. - template <typename T> - BufferIterator<const T> GetMemoryAsBufferIterator() const { - return BufferIterator<const T>(GetMemoryAsSpan<T>()); - } - private: friend class ReadOnlySharedMemoryRegion; ReadOnlySharedMemoryMapping(void* address, @@ -231,12 +224,6 @@ return span<T>(static_cast<T*>(raw_memory_ptr()), count); } - // Returns a BufferIterator of T. - template <typename T> - BufferIterator<T> GetMemoryAsBufferIterator() { - return BufferIterator<T>(GetMemoryAsSpan<T>()); - } - private: friend WritableSharedMemoryMapping MapAtForTesting( subtle::PlatformSharedMemoryRegion* region,
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/DoNotRevive.java b/base/test/android/javatests/src/org/chromium/base/test/util/DoNotRevive.java new file mode 100644 index 0000000..512f4c80 --- /dev/null +++ b/base/test/android/javatests/src/org/chromium/base/test/util/DoNotRevive.java
@@ -0,0 +1,22 @@ +// Copyright 2022 The Chromium 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 org.chromium.base.test.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is for disabled tests that should not be run in Test Reviver. Tests that cause + * other tests to fail or caue problems to the testing the infrastructure should have this + * annotation. <p> This should be used in conjunction with @DisabledTest or @DisableIf to prevent a + * test from running on normal bots. + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DoNotRevive { + String reason(); +}
diff --git a/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java b/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java index 3ccf9b6..e98c427 100644 --- a/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java +++ b/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java
@@ -15,12 +15,14 @@ public class ShadowBuildInfo { private static boolean sIsAtLeastS; private static boolean sIsAtLeastT; + private static boolean sTargetsAtLeastT; /** Rests the changes made to static state. */ @Resetter public static void reset() { sIsAtLeastS = false; sIsAtLeastT = false; + sTargetsAtLeastT = false; } /** Whether the current build is considered to be at least S. */ @@ -35,6 +37,12 @@ return sIsAtLeastT; } + /** Whether the current build is targeting at least T. */ + @Implementation + public static boolean targetsAtLeastT() { + return sTargetsAtLeastT; + } + /** Sets whether current Android version is at least S. */ public static void setIsAtLeastS(boolean isAtLeastS) { sIsAtLeastS = isAtLeastS; @@ -44,4 +52,9 @@ public static void setIsAtLeastT(boolean isAtLeastT) { sIsAtLeastT = isAtLeastT; } + + /** Sets whether the current build is targeting at least T. */ + public static void setTargetsAtLeastT(boolean targetsAtLeastT) { + sTargetsAtLeastT = targetsAtLeastT; + } }
diff --git a/build/android/gyp/create_unwind_table.py b/build/android/gyp/create_unwind_table.py index 8c9c347f..f4a54f3 100755 --- a/build/android/gyp/create_unwind_table.py +++ b/build/android/gyp/create_unwind_table.py
@@ -8,8 +8,8 @@ import argparse import collections import enum +import json import logging -import os import re import struct import subprocess @@ -614,22 +614,21 @@ complete_instruction_sequence=bytes([0b10000000, 0b00000000])), ) -def EncodeFunctionUnwinds(function_unwinds: Iterable[FunctionUnwind] +def EncodeFunctionUnwinds(function_unwinds: Iterable[FunctionUnwind], + text_section_start_address: int ) -> Iterable[EncodedFunctionUnwind]: """Encodes the unwind state for all functions defined in the binary. This function - sorts the collection of `FunctionUnwind`s by address. - fills in gaps between functions with trivial unwind. - - fills the space in the space in last page after last function with refuse + - fills the space in the last page after last function with refuse to unwind. + - fills the space in the first page before the first function with refuse to unwind. - Note: - This function assumes that min function start address is the text section - start address. - Args: function_unwinds: An iterable of function unwind states. + text_section_start_address: The address of .text section in ELF file. Returns: The encoded function unwind states with no gaps between functions, ordered @@ -656,7 +655,11 @@ sorted_function_unwinds: List[FunctionUnwind] = sorted( function_unwinds, key=lambda function_unwind: function_unwind.address) - text_section_start_address: int = sorted_function_unwinds[0].address + if sorted_function_unwinds[0].address > text_section_start_address: + yield EncodedFunctionUnwind(page_number=0, + page_offset=0, + address_unwinds=REFUSE_TO_UNWIND) + prev_func_end_address: int = sorted_function_unwinds[0].address gaps = 0 @@ -1009,6 +1012,29 @@ unwind_instruction_table) +def ReadTextSectionStartAddress(readobj_path: str, libchrome_path: str) -> int: + """Reads the .text section start address of libchrome ELF. + + Arguments: + readobj_path: Path to llvm-obj binary. + libchrome_path: Path to libchrome binary. + + Returns: + The text section start address as a number. + """ + proc = subprocess.Popen( + [readobj_path, '--sections', '--elf-output-style=JSON', libchrome_path], + stdout=subprocess.PIPE, + encoding='ascii') + + elfs = json.loads(proc.stdout.read())[0] + assert len(elfs) == 1 + sections = list(elfs.values())[0]['Sections'] + + return next(s['Section']['Address'] for s in sections + if s['Section']['Name']['Value'] == '.text') + + def main(): build_utils.InitLogging('CREATE_UNWIND_TABLE_DEBUG') parser = argparse.ArgumentParser(description=__doc__) @@ -1024,6 +1050,10 @@ required=True, help='The path of the dump_syms binary.', metavar='FILE') + parser.add_argument('--readobj_path', + required=True, + help='The path of the llvm-readobj binary.', + metavar='FILE') args = parser.parse_args() proc = subprocess.Popen(['./' + args.dump_syms_path, args.input_path, '-v'], @@ -1032,7 +1062,9 @@ function_cfis = ReadFunctionCfi(proc.stdout) function_unwinds = GenerateUnwinds(function_cfis, parsers=ALL_PARSERS) - encoded_function_unwinds = EncodeFunctionUnwinds(function_unwinds) + encoded_function_unwinds = EncodeFunctionUnwinds( + function_unwinds, + ReadTextSectionStartAddress(args.readobj_path, args.input_path)) (page_table, function_table, function_offset_table, unwind_instruction_table) = GenerateUnwindTables(encoded_function_unwinds) unwind_info: bytes = EncodeUnwindInfo(page_table, function_table,
diff --git a/build/android/gyp/create_unwind_table_tests.py b/build/android/gyp/create_unwind_table_tests.py index 81e9b7d7..c46557f 100755 --- a/build/android/gyp/create_unwind_table_tests.py +++ b/build/android/gyp/create_unwind_table_tests.py
@@ -311,10 +311,10 @@ FunctionUnwind(address=100, size=PAGE_SIZE - 100, address_unwinds=()), - FunctionUnwind(address=0, - size=100, - address_unwinds=()), - ]))) + FunctionUnwind( + address=0, size=100, address_unwinds=()), + ], + text_section_start_address=0))) @unittest.mock.patch('create_unwind_table.EncodeAddressUnwinds') def testFillingGaps(self, MockEncodeAddressUnwinds): @@ -332,38 +332,65 @@ ], list( EncodeFunctionUnwinds([ - FunctionUnwind(address=0, - size=50, - address_unwinds=()), + FunctionUnwind( + address=0, size=50, address_unwinds=()), FunctionUnwind(address=100, size=PAGE_SIZE - 100, address_unwinds=()), - ]))) + ], + text_section_start_address=0))) @unittest.mock.patch('create_unwind_table.EncodeAddressUnwinds') def testFillingLastPage(self, MockEncodeAddressUnwinds): MockEncodeAddressUnwinds.return_value = EncodedAddressUnwind(0, b'\x00') - self.assertEqual([ - EncodedFunctionUnwind(page_number=0, - page_offset=0, - address_unwinds=EncodedAddressUnwind(0, b'\x00')), - EncodedFunctionUnwind(page_number=0, - page_offset=100 >> 1, - address_unwinds=EncodedAddressUnwind(0, b'\x00')), - EncodedFunctionUnwind(page_number=0, - page_offset=200 >> 1, - address_unwinds=REFUSE_TO_UNWIND), - ], - list( - EncodeFunctionUnwinds([ - FunctionUnwind(address=1100, - size=100, - address_unwinds=()), - FunctionUnwind(address=1200, - size=100, - address_unwinds=()), - ]))) + self.assertEqual( + [ + EncodedFunctionUnwind(page_number=0, + page_offset=0, + address_unwinds=EncodedAddressUnwind( + 0, b'\x00')), + EncodedFunctionUnwind(page_number=0, + page_offset=100 >> 1, + address_unwinds=EncodedAddressUnwind( + 0, b'\x00')), + EncodedFunctionUnwind(page_number=0, + page_offset=200 >> 1, + address_unwinds=REFUSE_TO_UNWIND), + ], + list( + EncodeFunctionUnwinds([ + FunctionUnwind(address=1100, size=100, address_unwinds=()), + FunctionUnwind(address=1200, size=100, address_unwinds=()), + ], + text_section_start_address=1100))) + + @unittest.mock.patch('create_unwind_table.EncodeAddressUnwinds') + def testFillingFirstPage(self, MockEncodeAddressUnwinds): + MockEncodeAddressUnwinds.return_value = EncodedAddressUnwind(0, b'\x00') + + self.assertEqual( + [ + EncodedFunctionUnwind( + page_number=0, page_offset=0, address_unwinds=REFUSE_TO_UNWIND), + EncodedFunctionUnwind(page_number=0, + page_offset=100 >> 1, + address_unwinds=EncodedAddressUnwind( + 0, b'\x00')), + EncodedFunctionUnwind(page_number=0, + page_offset=200 >> 1, + address_unwinds=EncodedAddressUnwind( + 0, b'\x00')), + EncodedFunctionUnwind(page_number=0, + page_offset=300 >> 1, + address_unwinds=REFUSE_TO_UNWIND), + ], + list( + EncodeFunctionUnwinds([ + FunctionUnwind(address=1100, size=100, address_unwinds=()), + FunctionUnwind(address=1200, size=100, address_unwinds=()), + ], + text_section_start_address=1000))) @unittest.mock.patch('create_unwind_table.EncodeAddressUnwinds') def testOverlappedFunctions(self, _): @@ -374,7 +401,8 @@ EncodeFunctionUnwinds([ FunctionUnwind(address=0, size=100, address_unwinds=()), FunctionUnwind(address=50, size=100, address_unwinds=()), - ]))) + ], + text_section_start_address=0))) class _TestNullParser(unittest.TestCase):
diff --git a/build/android/pylib/constants/__init__.py b/build/android/pylib/constants/__init__.py index 050b99e..615307a 100644 --- a/build/android/pylib/constants/__init__.py +++ b/build/android/pylib/constants/__init__.py
@@ -72,10 +72,6 @@ chrome.PackageInfo('com.google.android.webview', 'com.android.cts.webkit.WebViewStartupCtsActivity', 'webview-command-line', None), - 'android_system_webview_shell': - chrome.PackageInfo('org.chromium.webview_shell', - 'org.chromium.webview_shell.WebViewBrowserActivity', - 'webview-command-line', None), 'android_webview_ui_test': chrome.PackageInfo('org.chromium.webview_ui_test', 'org.chromium.webview_ui_test.WebViewUiTestActivity',
diff --git a/build/chromeos/gen_skylab_runner.py b/build/chromeos/gen_skylab_runner.py deleted file mode 100755 index 3a5e0c0..0000000 --- a/build/chromeos/gen_skylab_runner.py +++ /dev/null
@@ -1,108 +0,0 @@ -#!/usr/bin/env vpython3 -# -# Copyright 2021 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import argparse -import os -import sys - - -class SkylabClientTestTest: - - # The basic shell script for client test run in Skylab. The arguments listed - # here will be fed by autotest at the run time. - # - # * test-launcher-summary-output: the path for the json result. It will be - # assigned by autotest, who will upload it to GCS upon test completion. - # * test-launcher-shard-index: the index for this test run. - # * test-launcher-total-shards: the total test shards. - # * test_args: arbitrary runtime arguments configured in test_suites.pyl, - # attached after '--'. - BASIC_SHELL_SCRIPT = """ -#!/bin/sh - -while [[ $# -gt 0 ]]; do - case "$1" in - --test-launcher-summary-output) - summary_output=$2 - shift 2 - ;; - - --test-launcher-shard-index) - shard_index=$2 - shift 2 - ;; - - --test-launcher-total-shards) - total_shards=$2 - shift 2 - ;; - - --) - test_args=$2 - break - ;; - - *) - break - ;; - esac -done - -if [ ! -d $(dirname $summary_output) ] ; then - mkdir -p $(dirname $summary_output) -fi - -cd `dirname $0` && cd .. - """ - - def __init__(self, args): - self.test_exe = args.test_exe - self.output = args.output - - @property - def suite_name(self): - return self.test_exe - - def build_test_script(self): - # Build the shell script that will be used on the device to invoke the test. - # Stored here as a list of lines. - device_test_script_contents = self.BASIC_SHELL_SCRIPT.split('\n') - - test_invocation = ('LD_LIBRARY_PATH=./ ./%s ' - ' --test-launcher-summary-output=$summary_output' - ' --test-launcher-shard-index=$shard_index' - ' --test-launcher-total-shards=$total_shards' - ' $test_args' % self.test_exe) - - device_test_script_contents.append(test_invocation) - with open(self.output, 'w') as w: - w.write('\n'.join(device_test_script_contents) + '\n') - os.chmod(self.output, 0o755) - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument( - '--test-exe', - type=str, - required=True, - help='Path to test executable to run inside the device.') - parser.add_argument('--verbose', '-v', action='store_true') - parser.add_argument( - '--output', - required=True, - type=str, - help='Path to create the runner script.') - - args = parser.parse_args() - - test = SkylabClientTestTest(args) - test.build_test_script() - return 0 - - -if __name__ == '__main__': - sys.exit(main())
diff --git a/build/chromeos/generate_skylab_deps.py b/build/chromeos/generate_skylab_deps.py new file mode 100755 index 0000000..908956e --- /dev/null +++ b/build/chromeos/generate_skylab_deps.py
@@ -0,0 +1,137 @@ +#!/usr/bin/env vpython3 +# +# Copyright 2022 The Chromium 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 argparse +import os +import sys + +# The basic shell script for client test run in Skylab. The arguments listed +# here will be fed by autotest at the run time. +# +# * test-launcher-summary-output: the path for the json result. It will be +# assigned by autotest, who will upload it to GCS upon test completion. +# * test-launcher-shard-index: the index for this test run. +# * test-launcher-total-shards: the total test shards. +# * test_args: arbitrary runtime arguments configured in test_suites.pyl, +# attached after '--'. +BASIC_SHELL_SCRIPT = """ +#!/bin/sh + +while [[ $# -gt 0 ]]; do + case "$1" in + --test-launcher-summary-output) + summary_output=$2 + shift 2 + ;; + + --test-launcher-shard-index) + shard_index=$2 + shift 2 + ;; + + --test-launcher-total-shards) + total_shards=$2 + shift 2 + ;; + + --) + test_args=$2 + break + ;; + + *) + break + ;; + esac +done + +if [ ! -d $(dirname $summary_output) ] ; then + mkdir -p $(dirname $summary_output) +fi + +cd `dirname $0` && cd .. + """ + + +def build_test_script(args): + # Build the shell script that will be used on the device to invoke the test. + # Stored here as a list of lines. + device_test_script_contents = BASIC_SHELL_SCRIPT.split('\n') + + test_invocation = ('LD_LIBRARY_PATH=./ ./%s ' + ' --test-launcher-summary-output=$summary_output' + ' --test-launcher-shard-index=$shard_index' + ' --test-launcher-total-shards=$total_shards' + ' $test_args' % args.test_exe) + + device_test_script_contents.append(test_invocation) + with open(args.output, 'w') as w: + w.write('\n'.join(device_test_script_contents) + '\n') + os.chmod(args.output, 0o755) + + +def build_filter_file(args): + with open(args.output, 'w') as w: + if args.disabled_tests is not None: + w.write('\n'.join('-{0}'.format(test) for test in args.disabled_tests) + + '\n') + if args.tests is not None: + w.write('\n'.join(args.tests) + '\n') + os.chmod(args.output, 0o755) + + +def main(): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest='command') + + script_gen_parser = subparsers.add_parser('generate-runner') + script_gen_parser.add_argument( + '--test-exe', + type=str, + required=True, + help='Path to test executable to run inside the device.') + script_gen_parser.add_argument('--verbose', '-v', action='store_true') + script_gen_parser.add_argument( + '--output', + required=True, + type=str, + help='Path to create the runner script.') + script_gen_parser.set_defaults(func=build_test_script) + + filter_gen_parser = subparsers.add_parser('generate-filter') + filter_gen_parser.add_argument( + '--disabled-tests', + type=str, + required=False, + action='append', + help='Space separated test names to prevent running. This generates the \ + negative filter') + filter_gen_parser.add_argument( + '--tests', + type=str, + required=False, + action='append', + help='Space separated test names to be run. This generates a positive \ + filter.') + filter_gen_parser.add_argument( + '--output', + required=True, + type=str, + help='Path to create the plain text filter file.') + filter_gen_parser.set_defaults(func=build_filter_file) + + args = parser.parse_args() + + if (args.command == "generate-filter" and args.disabled_tests is None and + args.tests is None): + parser.error('disabled_tests or tests must be provided to generate-filter') + args.func(args) + + return 0 + + +if __name__ == '__main__': + sys.exit(main())
diff --git a/build/config/android/create_unwind_table.gni b/build/config/android/create_unwind_table.gni index 15f8fd9..0e553ea 100644 --- a/build/config/android/create_unwind_table.gni +++ b/build/config/android/create_unwind_table.gni
@@ -30,6 +30,8 @@ rebase_path(_asset_path, root_build_dir), "--dump_syms_path", rebase_path("$root_out_dir/dump_syms", root_build_dir), + "--readobj_path", + rebase_path("$clang_base_path/bin/llvm-readobj", root_build_dir), ] deps = invoker.deps deps += [ "//third_party/breakpad:dump_syms" ]
diff --git a/build/config/chromeos/rules.gni b/build/config/chromeos/rules.gni index 60a21ab..c4d7e7cc 100644 --- a/build/config/chromeos/rules.gni +++ b/build/config/chromeos/rules.gni
@@ -102,6 +102,93 @@ } } +# Creates dependencies required by skylab testing. If passed the +# generated_script and test_exe this will generate the skylab runner script. +# If passed tast_tests or tast_disabled_tests this will generate a filter file +# to enable or disable the appropriate tests in skylab. +# Args: +# tast_disabled_tests: Names of tests to disable in tast. All other tests that +# match the tast expression will still run. +# tast_tests: Names of tests to enable in tast. All other tests will be +# disabled that are not listed. +# generated_script: Name of the generated runner script created for test_exe +# test_exe: Name of the executable to run with the generated script. This +# argument +template("generate_skylab_deps") { + forward_variables_from(invoker, + [ + "tast_disabled_tests", + "tast_tests", + "generated_script", + "test_exe", + ]) + if (defined(test_exe) || defined(generated_script)) { + assert(defined(test_exe) && defined(generated_script), + "The test_exe and generated_script must both be defined when " + + "generating the skylab runner script") + action(target_name) { + script = "//build/chromeos/generate_skylab_deps.py" + outputs = [ generated_script ] + args = [ + "generate-runner", + "--test-exe", + test_exe, + "--output", + rebase_path(generated_script, root_build_dir), + ] + + deps = [ "//testing/buildbot/filters:chromeos_filters" ] + if (defined(invoker.deps)) { + deps += invoker.deps + } + + data = [ generated_script ] + if (defined(invoker.data)) { + data += invoker.data + } + + data_deps = [ "//testing:test_scripts_shared" ] + if (defined(invoker.data_deps)) { + data_deps += invoker.data_deps + } + } + } + if (defined(tast_tests) || defined(tast_disabled_tests)) { + _generated_filter = "$root_build_dir/bin/${target_name}.filter" + _skylab_args = [ + "generate-filter", + "--output", + rebase_path(_generated_filter), + ] + if (defined(tast_disabled_tests)) { + foreach(_test, tast_disabled_tests) { + _skylab_args += [ + "--disabled-tests", + _test, + ] + } + } + if (defined(tast_tests)) { + foreach(_test, tast_tests) { + _skylab_args += [ + "--tests", + _test, + ] + } + } + action(target_name) { + script = "//build/chromeos/generate_skylab_deps.py" + outputs = [ _generated_filter ] + args = _skylab_args + data_deps = invoker.data_deps + data = [ _generated_filter ] + if (defined(invoker.data)) { + data += invoker.data + } + } + } +} + # Creates a script at $generated_script that can be used to launch a cros VM # and optionally run a test within it. # Args: @@ -432,6 +519,24 @@ assert(defined(tast_attr_expr) != defined(tast_tests), "Specify one of tast_tests or tast_attr_expr.") + _lacros_data_deps = [ + "//chrome", # Builds the browser. + + # Tools used to symbolize Chrome crash dumps. + # TODO(crbug.com/1156772): Remove these if/when all tests pick them up by + # default. + "//third_party/breakpad:dump_syms", + "//third_party/breakpad:minidump_dump", + "//third_party/breakpad:minidump_stackwalk", + ] + + _lacros_data = [ + "//components/crash/content/tools/generate_breakpad_symbols.py", + + # A script needed to launch Lacros in Lacros Tast tests. + "//build/lacros/mojo_connection_lacros_launcher.py", + ] + # Append any disabled tests to the expression. if (defined(tast_disabled_tests)) { assert(defined(tast_attr_expr), @@ -444,74 +549,33 @@ tast_attr_expr = "( " + tast_attr_expr + " )" } - generate_runner_script(target_name) { - testonly = true - deploy_lacros = true - generated_script = "$root_build_dir/bin/run_${target_name}" - runtime_deps_file = "$root_out_dir/${target_name}.runtime_deps" - - # At build time, Lacros tests don't know whether they'll run on VM or HW, - # and instead, these flags are specified at runtime when invoking the - # generated runner script. - skip_generating_board_args = true - - # By default, tast tests download a lacros-chrome from a gcs location and - # use it for testing. To support running lacros tast tests from Chromium CI, - # a Var is added to support pointing the tast tests to use a specified - # pre-deployed lacros-chrome. The location is decided by: - # https://source.chromium.org/chromium/chromium/src/+/main:third_party/chromite/scripts/deploy_chrome.py;l=80;drc=86f1234a4be8e9574442e076cdc835897f7bea61 - tast_vars = [ "lacrosDeployedBinary=/usr/local/lacros-chrome" ] - - data_deps = [ - "//chrome", # Builds the browser. - - # Tools used to symbolize Chrome crash dumps. - # TODO(crbug.com/1156772): Remove these if/when all tests pick them up by - # default. - "//third_party/breakpad:dump_syms", - "//third_party/breakpad:minidump_dump", - "//third_party/breakpad:minidump_stackwalk", - ] - - data = [ - "//components/crash/content/tools/generate_breakpad_symbols.py", - - # A script needed to launch Lacros in Lacros Tast tests. - "//build/lacros/mojo_connection_lacros_launcher.py", - ] - } -} - -template("generate_skylab_runner_script") { - forward_variables_from(invoker, - [ - "generated_script", - "test_exe", - ]) - - action(target_name) { - script = "//build/chromeos/gen_skylab_runner.py" - outputs = [ generated_script ] - args = [ - "--test-exe", - test_exe, - "--output", - rebase_path(generated_script, root_build_dir), - ] - - deps = [ "//testing/buildbot/filters:chromeos_filters" ] - if (defined(invoker.deps)) { - deps += invoker.deps + if (is_skylab) { + generate_skylab_deps(target_name) { + data = _lacros_data + data_deps = _lacros_data_deps } + } else { + generate_runner_script(target_name) { + testonly = true + deploy_lacros = true + generated_script = "$root_build_dir/bin/run_${target_name}" + runtime_deps_file = "$root_out_dir/${target_name}.runtime_deps" - data = [ generated_script ] - if (defined(invoker.data)) { - data += invoker.data - } + # At build time, Lacros tests don't know whether they'll run on VM or HW, + # and instead, these flags are specified at runtime when invoking the + # generated runner script. + skip_generating_board_args = true - data_deps = [ "//testing:test_scripts_shared" ] - if (defined(invoker.data_deps)) { - data_deps += invoker.data_deps + # By default, tast tests download a lacros-chrome from a gcs location and + # use it for testing. To support running lacros tast tests from Chromium CI, + # a Var is added to support pointing the tast tests to use a specified + # pre-deployed lacros-chrome. The location is decided by: + # https://source.chromium.org/chromium/chromium/src/+/main:third_party/chromite/scripts/deploy_chrome.py;l=80;drc=86f1234a4be8e9574442e076cdc835897f7bea61 + tast_vars = [ "lacrosDeployedBinary=/usr/local/lacros-chrome" ] + + data_deps = _lacros_data_deps + + data = _lacros_data } } }
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 48aa424..60165f1 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -820,13 +820,15 @@ "-Coverflow-checks=on", # To make Rust .d files compatible with ninja - "-Z", - "dep-info-omit-d-target", + "-Zdep-info-omit-d-target", # If a macro panics during compilation, show which macro and where it is # defined. - "-Z", - "macro-backtrace", + "-Zmacro-backtrace", + + # For deterministic builds, keep the local machine's current working + # directory from appearing in build outputs. + "-Zremap-cwd-prefix=.", ] if (rust_abi_target != "") { rustflags += [ "--target=$rust_abi_target" ]
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index dfbe3ef..03e9699 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -7.20220317.1.1 +7.20220318.0.1
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1 index dfbe3ef..ae4ad6b 100644 --- a/build/fuchsia/linux_internal.sdk.sha1 +++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@ -7.20220317.1.1 +7.20220318.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index dfbe3ef..03e9699 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -7.20220317.1.1 +7.20220318.0.1
diff --git a/build/rust/BUILD.gn b/build/rust/BUILD.gn index 09da28ee..4477db4 100644 --- a/build/rust/BUILD.gn +++ b/build/rust/BUILD.gn
@@ -22,10 +22,13 @@ "//third_party/rust/cxx/v1/crate/src/cxx.cc", ] - defines = [ - "RUST_CXX_NO_EXCEPTIONS", - "CXX_RS_EXPORT=__attribute__((visibility(\"default\")))", - ] + defines = [ "RUST_CXX_NO_EXCEPTIONS" ] + + if (is_win) { + defines += [ "CXX_RS_EXPORT=__declspec(dllexport)" ] + } else { + defines += [ "CXX_RS_EXPORT=__attribute__((visibility(\"default\")))" ] + } # Depending on the C++ bindings side of cxx then requires also depending # on the Rust bindings, since one calls the other. And the Rust bindings
diff --git a/build/rust/tests/BUILD.gn b/build/rust/tests/BUILD.gn index 418f322..ffeb9d2f 100644 --- a/build/rust/tests/BUILD.gn +++ b/build/rust/tests/BUILD.gn
@@ -76,7 +76,7 @@ "test_rust_exe", "test_rust_multiple_dep_versions_exe", "test_simple_rust_exe", - "//third_party/rust/autocxx_gen/v0_16:autocxx_gen", + "//third_party/rust/autocxx_gen/v0_17:autocxx_gen", "//third_party/rust/bindgen/v0_59:bindgen", ] }
diff --git a/buildtools/mac/clang-format.arm64.sha1 b/buildtools/mac/clang-format.arm64.sha1 new file mode 100644 index 0000000..6b3f7a91 --- /dev/null +++ b/buildtools/mac/clang-format.arm64.sha1
@@ -0,0 +1 @@ +5ba974b3b37f9f4e3b44fdde11d7ef2ab71619ab
diff --git a/buildtools/mac/clang-format.x64.sha1 b/buildtools/mac/clang-format.x64.sha1 new file mode 100644 index 0000000..6b3f7a91 --- /dev/null +++ b/buildtools/mac/clang-format.x64.sha1
@@ -0,0 +1 @@ +5ba974b3b37f9f4e3b44fdde11d7ef2ab71619ab
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 50b5b29..a7636e45 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -4911,8 +4911,8 @@ element_id)); const ScrollNode* scroll_node = property_trees->scroll_tree().FindNodeFromElementId(element_id); - // TODO(flackr): We should aim to prevent this condition from happening - // and either remove this check or make it fatal. + // TODO(crbug.com/1307498): We should aim to prevent this condition from + // happening and either remove this check or make it fatal. DCHECK(scroll_node); if (!scroll_node) return;
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index 2e9caef1..ca71933 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc
@@ -61,17 +61,6 @@ namespace cc { namespace { - -template <class T> -bool CheckPropertyNodeExists(const T& property_tree, ElementId element_id) { - size_t nodes_with_element_id = - property_tree.element_id_to_node_index().count(element_id); - DCHECK_EQ(1u, nodes_with_element_id); - // TODO(flackr): We should aim to prevent this condition from happening - // and either remove this check or make it fatal. - return nodes_with_element_id > 0; -} - // Small helper class that saves the current viewport location as the user sees // it and resets to the same location. class ViewportAnchor { @@ -962,9 +951,9 @@ void LayerTreeImpl::SetTransformMutated(ElementId element_id, const gfx::Transform& transform) { - if (!CheckPropertyNodeExists(property_trees()->transform_tree(), element_id)) - return; - + DCHECK_EQ(1u, + property_trees()->transform_tree().element_id_to_node_index().count( + element_id)); if (IsSyncTree() || IsRecycleTree()) element_id_to_transform_animations_[element_id] = transform; if (property_trees()->transform_tree_mutable().OnTransformAnimated(element_id, @@ -973,9 +962,9 @@ } void LayerTreeImpl::SetOpacityMutated(ElementId element_id, float opacity) { - if (!CheckPropertyNodeExists(property_trees()->effect_tree(), element_id)) - return; - + DCHECK_EQ(1u, + property_trees()->effect_tree().element_id_to_node_index().count( + element_id)); if (IsSyncTree() || IsRecycleTree()) element_id_to_opacity_animations_[element_id] = opacity; if (property_trees()->effect_tree_mutable().OnOpacityAnimated(element_id, @@ -985,9 +974,9 @@ void LayerTreeImpl::SetFilterMutated(ElementId element_id, const FilterOperations& filters) { - if (!CheckPropertyNodeExists(property_trees()->effect_tree(), element_id)) - return; - + DCHECK_EQ(1u, + property_trees()->effect_tree().element_id_to_node_index().count( + element_id)); if (IsSyncTree() || IsRecycleTree()) element_id_to_filter_animations_[element_id] = filters; if (property_trees()->effect_tree_mutable().OnFilterAnimated(element_id, @@ -998,9 +987,9 @@ void LayerTreeImpl::SetBackdropFilterMutated( ElementId element_id, const FilterOperations& backdrop_filters) { - if (!CheckPropertyNodeExists(property_trees()->effect_tree(), element_id)) - return; - + DCHECK_EQ(1u, + property_trees()->effect_tree().element_id_to_node_index().count( + element_id)); if (IsSyncTree() || IsRecycleTree()) element_id_to_backdrop_filter_animations_[element_id] = backdrop_filters; if (property_trees()->effect_tree_mutable().OnBackdropFilterAnimated(
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc index feb6874a..36fdc34 100644 --- a/cc/trees/property_tree.cc +++ b/cc/trees/property_tree.cc
@@ -152,6 +152,10 @@ const gfx::Transform& transform) { TransformNode* node = FindNodeFromElementId(element_id); DCHECK(node); + // TODO(crbug.com/1307498): Remove this when we no longer animate + // non-existent nodes. + if (!node) + return false; if (node->local == transform) return false; node->local = transform; @@ -472,8 +476,8 @@ fixed_position_adjustment += gfx::ScaleVector2d(overscroll_offset, 1.f / page_scale_factor()); - ClipNode* clip_node = property_trees()->clip_tree_mutable().Node( - property_trees()->clip_tree_mutable().overscroll_node_id()); + ClipTree& clip_tree = property_trees()->clip_tree_mutable(); + ClipNode* clip_node = clip_tree.Node(clip_tree.overscroll_node_id()); if (clip_node) { // Inflate the clip rect based on the overscroll direction. @@ -486,7 +490,7 @@ : outsets.set_bottom(fixed_position_adjustment.y()); clip_node->clip.Outset(outsets); - property_trees()->clip_tree_mutable().set_needs_update(true); + clip_tree.set_needs_update(true); } } @@ -887,6 +891,10 @@ bool EffectTree::OnOpacityAnimated(ElementId id, float opacity) { EffectNode* node = FindNodeFromElementId(id); DCHECK(node); + // TODO(crbug.com/1307498): Remove this when we no longer animate + // non-existent nodes. + if (!node) + return false; if (node->opacity == opacity) return false; node->opacity = opacity; @@ -900,6 +908,10 @@ const FilterOperations& filters) { EffectNode* node = FindNodeFromElementId(id); DCHECK(node); + // TODO(crbug.com/1307498): Remove this when we no longer animate + // non-existent nodes. + if (!node) + return false; if (node->filters == filters) return false; node->filters = filters; @@ -914,6 +926,10 @@ const FilterOperations& backdrop_filters) { EffectNode* node = FindNodeFromElementId(id); DCHECK(node); + // TODO(crbug.com/1307498): Remove this when we no longer animate + // non-existent nodes. + if (!node) + return false; if (node->backdrop_filters == backdrop_filters) return false; node->backdrop_filters = backdrop_filters;
diff --git a/cc/trees/property_tree_unittest.cc b/cc/trees/property_tree_unittest.cc index e2fef3af..e31cbd7c 100644 --- a/cc/trees/property_tree_unittest.cc +++ b/cc/trees/property_tree_unittest.cc
@@ -196,36 +196,52 @@ } // Tests that the transform for fixed elements is translated based on the -// overscroll nodes scroll_offset. +// overscroll nodes scroll_offset and that the clip node has an outset based on +// the overscroll distance. TEST(PropertyTreeTest, FixedElementInverseTranslation) { FakeProtectedSequenceSynchronizer synchronizer; PropertyTrees property_trees(synchronizer); - TransformTree& tree = property_trees.transform_tree_mutable(); + + ClipTree& clip_tree = property_trees.clip_tree_mutable(); + const gfx::RectF clip_rect(0, 0, 100, 100); + ClipNode clip_node; + clip_node.id = 1; + clip_node.parent_id = 0; + clip_node.clip = clip_rect; + clip_tree.Insert(clip_node, 0); + clip_tree.set_overscroll_node_id(clip_node.id); + + TransformTree& transform_tree = property_trees.transform_tree_mutable(); TransformNode contents_root; contents_root.local.Translate(2, 2); - contents_root.id = tree.Insert(contents_root, 0); - tree.UpdateTransforms(1); + contents_root.id = transform_tree.Insert(contents_root, 0); + transform_tree.UpdateTransforms(1); const gfx::PointF overscroll_offset(0, 10); TransformNode overscroll_node; overscroll_node.scroll_offset = overscroll_offset; - overscroll_node.id = tree.Insert(overscroll_node, 1); + overscroll_node.id = transform_tree.Insert(overscroll_node, 1); - tree.set_overscroll_node_id(overscroll_node.id); - tree.set_fixed_elements_dont_overscroll(true); + transform_tree.set_overscroll_node_id(overscroll_node.id); + transform_tree.set_fixed_elements_dont_overscroll(true); TransformNode fixed_node; fixed_node.is_fixed_position = true; - fixed_node.id = tree.Insert(fixed_node, 2); + fixed_node.id = transform_tree.Insert(fixed_node, 2); - EXPECT_TRUE(tree.ShouldUndoOverscroll(&fixed_node)); + EXPECT_TRUE(transform_tree.ShouldUndoOverscroll(&fixed_node)); - tree.UpdateTransforms(2); // overscroll_node - tree.UpdateTransforms(3); // fixed_node + transform_tree.UpdateTransforms(2); // overscroll_node + transform_tree.UpdateTransforms(3); // fixed_node gfx::Transform expected; expected.Translate(overscroll_offset.OffsetFromOrigin()); - EXPECT_TRANSFORM_EQ(expected, tree.Node(fixed_node.id)->to_parent); + EXPECT_TRANSFORM_EQ(expected, transform_tree.Node(fixed_node.id)->to_parent); + + gfx::RectF expected_clip_rect(clip_rect); + expected_clip_rect.set_height(clip_rect.height() + overscroll_offset.y()); + EXPECT_EQ(clip_tree.Node(clip_tree.overscroll_node_id())->clip, + expected_clip_rect); } TEST(PropertyTreeTest, TransformsWithFlattening) {
diff --git a/chrome/MAJOR_BRANCH_DATE b/chrome/MAJOR_BRANCH_DATE index bf2682dc..6fddc50 100644 --- a/chrome/MAJOR_BRANCH_DATE +++ b/chrome/MAJOR_BRANCH_DATE
@@ -1 +1 @@ -MAJOR_BRANCH_DATE=2022-02-18 +MAJOR_BRANCH_DATE=2022-03-18
diff --git a/chrome/VERSION b/chrome/VERSION index 8609131..e1efff5 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ -MAJOR=101 +MAJOR=102 MINOR=0 -BUILD=4951 +BUILD=4952 PATCH=0
diff --git a/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected b/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected index 8d4070d..35a58e1 100644 --- a/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected +++ b/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected
@@ -207,9 +207,9 @@ static boolean isDebug() return false; } -# Never inline classes, methods, or fields with this annotation, but allow +# Never inline classes, methods, or fields with this annotation, but allow # shrinking and obfuscation. -# Relevant to fields when they are needed to store strong references to objects +# Relevant to fields when they are needed to store strong refrences to objects # that are held as weak references by native code. -if @org.chromium.base.annotations.DoNotInline class * { *** *(...); @@ -224,11 +224,6 @@ @org.chromium.base.annotations.DoNotInline <fields>; } -# Never merge classes horizontally or vertically with this annotation. -# Relevant to classes being used as a key in maps or sets. --nohorizontalclassmerging @org.chromium.base.annotations.DoNotClassMerge class * --noverticalclassmerging @org.chromium.base.annotations.DoNotClassMerge class * - # Keep all CREATOR fields within Parcelable that are kept. -keepclassmembers class org.chromium.** implements android.os.Parcelable { public static *** CREATOR;
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/helper/FaviconHelper.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/helper/FaviconHelper.java index 3b37c0d7..85c693000 100644 --- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/helper/FaviconHelper.java +++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/helper/FaviconHelper.java
@@ -22,7 +22,7 @@ * Provides default favicons and helps to fetch and set favicons. */ public class FaviconHelper { - private final Resources mResources; + private final Context mContext; private final RoundedIconGenerator mIconGenerator; private final int mDesiredSize; @@ -52,15 +52,17 @@ * @param context The {@link Context} used to fetch resources and create Drawables. */ protected FaviconHelper(Context context) { - mResources = context.getResources(); + mContext = context; + final Resources resources = mContext.getResources(); mDesiredSize = - mResources.getDimensionPixelSize(R.dimen.keyboard_accessory_suggestion_icon_size); - mIconGenerator = FaviconUtils.createCircularIconGenerator(mResources); + resources.getDimensionPixelSize(R.dimen.keyboard_accessory_suggestion_icon_size); + mIconGenerator = FaviconUtils.createCircularIconGenerator(mContext.getResources()); } public Drawable getDefaultIcon(String origin) { return FaviconUtils.getIconDrawableWithoutFilter(null, origin, - R.color.default_favicon_background_color, mIconGenerator, mResources, mDesiredSize); + mContext.getColor(R.color.default_favicon_background_color), mIconGenerator, + mContext.getResources(), mDesiredSize); } /** @@ -76,7 +78,7 @@ mIconBridge.getLargeIconForUrl(gurlOrigin, mDesiredSize, (icon, fallbackColor, isFallbackColorDefault, iconType) -> { Drawable drawable = FaviconUtils.getIconDrawableWithoutFilter(icon, gurlOrigin, - fallbackColor, mIconGenerator, mResources, mDesiredSize); + fallbackColor, mIconGenerator, mContext.getResources(), mDesiredSize); setIconCallback.onResult(drawable); }); }
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java index ddf898ea..d615863 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java
@@ -99,7 +99,7 @@ @Rule public final ChromeRenderTestRule mRenderTestRule = - ChromeRenderTestRule.Builder.withPublicCorpus().build(); + ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build(); public AccessorySheetRenderTest(boolean nightModeEnabled, boolean useRtlLayout) { FeatureList.setTestFeatures(
diff --git a/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java b/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java index 453f91d..0b1fb5cd 100644 --- a/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java +++ b/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java
@@ -273,12 +273,26 @@ * created from the new Tab menu or "+" button, and it hasn't navigate to any URL yet. */ public static boolean shouldHandleAsNtp(Tab tab) { + // TODO(https://crbug.com/1305397): Rule out a null url here and assert that it's non-null. if (tab == null || tab.getUrl() == null) return false; return tab.getUrl().isEmpty() && StartSurfaceUserData.getCreatedAsNtp(tab); } /** + * @return Whether the given tab with the given url should be treated as chrome://newTab. This + * function returns true only when {@link OMNIBOX_FOCUSED_ON_NEW_TAB} is enabled, the + * tab is newly created from the new Tab menu or "+" button, and it hasn't navigate to + * any URL yet. + */ + // TODO(https://crbug.com/1305374): migrate to GURL. + public static boolean shouldHandleAsNtp(Tab tab, String url) { + if (tab == null || url == null) return false; + + return url.isEmpty() && StartSurfaceUserData.getCreatedAsNtp(tab); + } + + /** * Sets the UserData if the Tab is a newly created empty Tab when * {@link OMNIBOX_FOCUSED_ON_NEW_TAB} is enabled. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java index df82b97..085f85b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
@@ -27,7 +27,6 @@ import org.chromium.chrome.browser.notifications.NotificationUmaTracker; import org.chromium.chrome.browser.notifications.NotificationWrapperBuilderFactory; import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions; -import org.chromium.components.browser_ui.notifications.ForegroundServiceUtils; import org.chromium.components.browser_ui.notifications.NotificationMetadata; import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder; @@ -369,7 +368,6 @@ */ private boolean canStartForeground() { if (!BuildInfo.isAtLeastS()) return true; - if (!ForegroundServiceUtils.canStartForegroundServiceExcludingMedia()) return true; // If foreground service is started, startForeground() must be called. return ApplicationStatus.hasVisibleActivities() || (mIsServiceBound && !mStartForegroundCalled);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java index 1870587..88afb798 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java
@@ -30,10 +30,10 @@ import org.chromium.base.test.util.JniMocker; import org.chromium.chrome.R; import org.chromium.chrome.browser.flags.ChromeFeatureList; -import org.chromium.chrome.browser.omnibox.LocationBarLayout; import org.chromium.chrome.browser.omnibox.NewTabPageDelegate; import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; +import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.TabImpl; import org.chromium.chrome.test.util.ToolbarTestUtils; import org.chromium.chrome.test.util.browser.Features; @@ -42,13 +42,15 @@ import org.chromium.components.security_state.SecurityStateModel; import org.chromium.components.security_state.SecurityStateModelJni; import org.chromium.content_public.browser.test.NativeLibraryTestUtils; +import org.chromium.content_public.browser.test.util.TestThreadUtils; /** - * Unit tests for {@link LocationBarLayout} class. + * Instrumentation tests for the toolbar security icon. */ @RunWith(BaseJUnit4ClassRunner.class) @Batch(Batch.UNIT_TESTS) -@Features.DisableFeatures(ChromeFeatureList.OMNIBOX_UPDATED_CONNECTION_SECURITY_INDICATORS) +@Features.DisableFeatures({ChromeFeatureList.OMNIBOX_UPDATED_CONNECTION_SECURITY_INDICATORS, + ChromeFeatureList.LOCATION_BAR_MODEL_OPTIMIZATIONS}) public final class ToolbarSecurityIconTest { private static final boolean IS_SMALL_DEVICE = true; private static final boolean IS_OFFLINE_PAGE = true; @@ -76,6 +78,8 @@ @Mock private PrefService mMockPrefService; + @Mock + private Profile mMockProfile; /** * Set up the lock icon policy for Mock PrefService. @@ -106,7 +110,8 @@ (url) -> url.getSpec(), (window) -> null, ToolbarTestUtils.OFFLINE_STATUS, mSearchEngineLogoUtils)); // clang-format on - mLocationBarModel.initializeWithNative(); + Profile.setLastUsedProfileForTesting(mMockProfile); + TestThreadUtils.runOnUiThreadBlocking(() -> mLocationBarModel.initializeWithNative()); doReturn(mMockPrefService).when(mLocationBarModel).getPrefService(); setupLockIconPolicyForTests(false);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/LocationBarModelUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/LocationBarModelUnitTest.java index be77670f..9ac17197 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/LocationBarModelUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/LocationBarModelUnitTest.java
@@ -4,41 +4,82 @@ package org.chromium.chrome.browser.toolbar; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.text.SpannableStringBuilder; +import android.util.LruCache; import android.view.ContextThemeWrapper; +import androidx.annotation.Nullable; import androidx.test.filters.MediumTest; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; import org.chromium.base.ContextUtils; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.JniMocker; import org.chromium.chrome.R; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.incognito.IncognitoCctProfileManager; import org.chromium.chrome.browser.incognito.IncognitoUtils; +import org.chromium.chrome.browser.omnibox.ChromeAutocompleteSchemeClassifier; +import org.chromium.chrome.browser.omnibox.ChromeAutocompleteSchemeClassifierJni; import org.chromium.chrome.browser.omnibox.LocationBarDataProvider; import org.chromium.chrome.browser.omnibox.NewTabPageDelegate; import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TrustedCdn; +import org.chromium.chrome.browser.toolbar.LocationBarModelUnitTest.ShadowTrustedCdn; +import org.chromium.chrome.test.util.browser.Features; +import org.chromium.chrome.test.util.browser.Features.DisableFeatures; +import org.chromium.chrome.test.util.browser.Features.EnableFeatures; +import org.chromium.components.dom_distiller.core.DomDistillerUrlUtilsJni; +import org.chromium.components.omnibox.OmniboxUrlEmphasizerJni; +import org.chromium.components.url_formatter.UrlFormatter; +import org.chromium.components.url_formatter.UrlFormatterJni; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.WindowAndroid; +import org.chromium.url.GURL; +import org.chromium.url.ShadowGURL; /** * Unit tests for the LocationBarModel. */ @RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE, shadows = {ShadowGURL.class, ShadowTrustedCdn.class}) +@DisableFeatures({ChromeFeatureList.LOCATION_BAR_MODEL_OPTIMIZATIONS}) public class LocationBarModelUnitTest { + @Implements(TrustedCdn.class) + static class ShadowTrustedCdn { + @Implementation + public static String getPublisherUrl(@Nullable Tab tab) { + return null; + } + } + + @Rule + public TestRule mProcessor = new Features.JUnitProcessor(); + @Rule + public JniMocker mJniMocker = new JniMocker(); + @Mock private Tab mIncognitoTabMock; @@ -63,11 +104,27 @@ private LocationBarDataProvider.Observer mLocationBarDataObserver; @Mock private SearchEngineLogoUtils mSearchEngineLogoUtils; + @Mock + private LocationBarModel.Natives mLocationBarModelJni; + @Mock + private ChromeAutocompleteSchemeClassifier.Natives mChromeAutocompleteSchemeClassifierJni; + @Mock + private UrlFormatter.Natives mUrlFormatterJniMock; + @Mock + private DomDistillerUrlUtilsJni mDomDistillerUrlUtilsJni; + @Mock + private OmniboxUrlEmphasizerJni mOmniboxUrlEmphasizerJni; + @Mock + private GURL mMockGurl; @Before public void setUp() { MockitoAnnotations.initMocks(this); Profile.setLastUsedProfileForTesting(mRegularProfileMock); + mJniMocker.mock(LocationBarModelJni.TEST_HOOKS, mLocationBarModelJni); + mJniMocker.mock(UrlFormatterJni.TEST_HOOKS, mUrlFormatterJniMock); + mJniMocker.mock(DomDistillerUrlUtilsJni.TEST_HOOKS, mDomDistillerUrlUtilsJni); + mJniMocker.mock(OmniboxUrlEmphasizerJni.TEST_HOOKS, mOmniboxUrlEmphasizerJni); IncognitoCctProfileManager.setIncognitoCctProfileManagerForTesting( mIncognitoCctProfileManagerMock); when(mIncognitoCctProfileManagerMock.getProfile()).thenReturn(mNonPrimaryOTRProfileMock); @@ -88,12 +145,12 @@ new LocationBarModel.OfflineStatus() { @Override public boolean isShowingTrustedOfflinePage(WebContents webContents) { - return true; + return false; } @Override public boolean isOfflinePage(Tab tab) { - return true; + return false; } }; @@ -127,6 +184,7 @@ when(mIncognitoCctProfileManagerMock.getProfile()).thenReturn(null); LocationBarModel incognitoLocationBarModel = new TestIncognitoLocationBarModel(mIncognitoTabMock, mSearchEngineLogoUtils); + incognitoLocationBarModel.initializeWithNative(); Profile otrProfile = incognitoLocationBarModel.getProfile(); Assert.assertEquals(mPrimaryOTRProfileMock, otrProfile); } @@ -136,6 +194,7 @@ public void getProfile_IncognitoCCT_ReturnsNonPrimaryOTRProfile() { LocationBarModel incognitoLocationBarModel = new TestIncognitoLocationBarModel(mIncognitoTabMock, mSearchEngineLogoUtils); + incognitoLocationBarModel.initializeWithNative(); Profile otrProfile = incognitoLocationBarModel.getProfile(); Assert.assertEquals(mNonPrimaryOTRProfileMock, otrProfile); } @@ -145,6 +204,7 @@ public void getProfile_NullTab_ReturnsPrimaryOTRProfile() { LocationBarModel incognitoLocationBarModel = new TestIncognitoLocationBarModel(null, mSearchEngineLogoUtils); + incognitoLocationBarModel.initializeWithNative(); Profile otrProfile = incognitoLocationBarModel.getProfile(); Assert.assertEquals(mPrimaryOTRProfileMock, otrProfile); } @@ -154,6 +214,7 @@ public void getProfile_RegularTab_ReturnsRegularProfile() { LocationBarModel regularLocationBarModel = new TestRegularLocationBarModel(mRegularTabMock, mSearchEngineLogoUtils); + regularLocationBarModel.initializeWithNative(); Profile profile = regularLocationBarModel.getProfile(); Assert.assertEquals(mRegularProfileMock, profile); } @@ -163,6 +224,7 @@ public void getProfile_NullTab_ReturnsRegularProfile() { LocationBarModel regularLocationBarModel = new TestRegularLocationBarModel(null, mSearchEngineLogoUtils); + regularLocationBarModel.initializeWithNative(); Profile profile = regularLocationBarModel.getProfile(); Assert.assertEquals(mRegularProfileMock, profile); } @@ -235,4 +297,38 @@ verify(mLocationBarDataObserver).onPrimaryColorChanged(); verify(mLocationBarDataObserver).onSecurityStateChanged(); } + + @EnableFeatures({ChromeFeatureList.LOCATION_BAR_MODEL_OPTIMIZATIONS}) + @Test + @MediumTest + public void testSpannableCache() { + doReturn(123L).when(mLocationBarModelJni).init(Mockito.any()); + mJniMocker.mock(ChromeAutocompleteSchemeClassifierJni.TEST_HOOKS, + mChromeAutocompleteSchemeClassifierJni); + LocationBarModel regularLocationBarModel = + new TestRegularLocationBarModel(mRegularTabMock, mSearchEngineLogoUtils); + doReturn(true).when(mRegularTabMock).isInitialized(); + regularLocationBarModel.initializeWithNative(); + LruCache<LocationBarModel.SpannableDisplayTextCacheKey, SpannableStringBuilder> cache = + regularLocationBarModel.getCacheForTesting(); + Assert.assertEquals(cache.size(), 0); + + String url = "http://www.example.com/"; + doReturn(url).when(mMockGurl).getSpec(); + doReturn(mMockGurl).when(mRegularTabMock).getUrl(); + doReturn(url) + .when(mLocationBarModelJni) + .getFormattedFullURL(Mockito.anyLong(), Mockito.any()); + doReturn(url).when(mLocationBarModelJni).getURLForDisplay(Mockito.anyLong(), Mockito.any()); + doReturn(mMockGurl).when(mUrlFormatterJniMock).fixupUrl(url); + doReturn(new int[] {0, 7, 7, 15}) + .when(mOmniboxUrlEmphasizerJni) + .parseForEmphasizeComponents(any(), any()); + regularLocationBarModel.getUrlBarData(); + Assert.assertEquals(cache.size(), 1); + regularLocationBarModel.getUrlBarData(); + Assert.assertEquals(cache.hitCount(), 1); + + regularLocationBarModel.destroy(); + } }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt index 46695e1..261c4a3a 100644 --- a/chrome/android/profiles/newest.txt +++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-101.0.4948.0_rc-r1-merged.afdo.bz2 +chromeos-chrome-amd64-101.0.4950.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h index 2ae848c..c5a62ce 100644 --- a/chrome/app/chrome_command_ids.h +++ b/chrome/app/chrome_command_ids.h
@@ -225,7 +225,7 @@ #define IDC_CLOSE_SIGN_IN_PROMO 40258 #define IDC_SHOW_FULL_URLS 40259 #define IDC_CARET_BROWSING_TOGGLE 40260 -#define IDC_TOGGLE_COMMANDER 40261 +#define IDC_TOGGLE_QUICK_COMMANDS 40261 #define IDC_CHROME_TIPS 40263 #define IDC_CHROME_WHATS_NEW 40264
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 42f3041..3491c3b 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -1177,8 +1177,8 @@ <message name="IDS_MOVE_TAB_TO_NEW_WINDOW" desc="The text label of the Move Tab to Window menu item."> Move tab to new window </message> - <message name="IDS_TOGGLE_COMMANDER" desc="The text label of the Toggle Quick Commands menu item"> - Toggle Quick Commands + <message name="IDS_TOGGLE_QUICK_COMMANDS" desc="The text label of the Toggle Quick Commands menu item"> + Quick commands </message> </if> <if expr="use_titlecase"> @@ -1260,8 +1260,8 @@ <message name="IDS_MOVE_TAB_TO_NEW_WINDOW" desc="In Title Case: The text label of the Move Tab to New Window menu item."> Move Tab to New Window </message> - <message name="IDS_TOGGLE_COMMANDER" desc="In Title Case:The text label of the Toggle Quick Commands menu item"> - Toggle Quick Commands + <message name="IDS_TOGGLE_QUICK_COMMANDS" desc="In Title Case:The text label of the Toggle Quick Commands menu item"> + Quick Commands </message> </if> </if> @@ -12454,9 +12454,17 @@ desc="Title of the prompt to choose a new name for a window."> Name this window </message> - <message name="IDS_COMMANDER_LABEL" - desc="Label to identify the Commander feature in Task Manager"> - Commander + <message name="IDS_QUICK_COMMANDS_LABEL" + desc="Label to identify the Quick Commands feature in Task Manager"> + Quick Commands + </message> + <message name="IDS_QUICK_COMMANDS_PLACEHOLDER" + desc="Placeholder text for the Quick Commands text input"> + Enter a keyword like "tabs" or "windows" to find an action + </message> + <message name="IDS_QUICK_COMMANDS_NO_RESULTS" + desc="Shown when there are no results in Quick Commands"> + No commands found </message> <!-- ChromeLabs bubble -->
diff --git a/chrome/app/generated_resources_grd/IDS_COMMANDER_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_COMMANDER_LABEL.png.sha1 deleted file mode 100644 index 7909895..0000000 --- a/chrome/app/generated_resources_grd/IDS_COMMANDER_LABEL.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -bb35663c6800e28b389ec3ed50789794131bff82 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_LABEL.png.sha1 new file mode 100644 index 0000000..6bfa2fc --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_LABEL.png.sha1
@@ -0,0 +1 @@ +e305501bbd68e9722c49b0da2128b9abc4c56f30 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_NO_RESULTS.png.sha1 b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_NO_RESULTS.png.sha1 new file mode 100644 index 0000000..fd544a5 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_NO_RESULTS.png.sha1
@@ -0,0 +1 @@ +185849eea17493d7cad20948a2f24afb927bf31e \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_PLACEHOLDER.png.sha1 b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_PLACEHOLDER.png.sha1 new file mode 100644 index 0000000..527942ba --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_PLACEHOLDER.png.sha1
@@ -0,0 +1 @@ +d35ab5350649d7d682cd092c813acc9ab361163a \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_TOGGLE_COMMANDER.png.sha1 b/chrome/app/generated_resources_grd/IDS_TOGGLE_COMMANDER.png.sha1 deleted file mode 100644 index 24ceb35..0000000 --- a/chrome/app/generated_resources_grd/IDS_TOGGLE_COMMANDER.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -2d630d3c9160d9dc531a91ff4f01b0042ae9d952 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_TOGGLE_QUICK_COMMANDS.png.sha1 b/chrome/app/generated_resources_grd/IDS_TOGGLE_QUICK_COMMANDS.png.sha1 new file mode 100644 index 0000000..e2edf8a --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_TOGGLE_QUICK_COMMANDS.png.sha1
@@ -0,0 +1 @@ +fad73dc15316d3dfad90ebc691577fb7cb76944e \ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn index 951af578..e5730271 100644 --- a/chrome/app/vector_icons/BUILD.gn +++ b/chrome/app/vector_icons/BUILD.gn
@@ -208,6 +208,10 @@ sources += [ "autocorrect_undo.icon", "full_restore_notification.icon", + "mouse_left_click_edit.icon", + "mouse_left_click_view.icon", + "mouse_right_click_edit.icon", + "mouse_right_click_view.icon", "notification_battery.icon", "notification_captive_portal.icon", "notification_cellular_alert.icon",
diff --git a/chrome/app/vector_icons/mouse_left_click_edit.icon b/chrome/app/vector_icons/mouse_left_click_edit.icon new file mode 100644 index 0000000..3e9764b5 --- /dev/null +++ b/chrome/app/vector_icons/mouse_left_click_edit.icon
@@ -0,0 +1,40 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +PATH_COLOR_ARGB, 0xFF, 0x20, 0x21, 0x24, +MOVE_TO, 10, 2, +CUBIC_TO, 6.68f, 2, 4, 4.5f, 4, 7.6f, +R_V_LINE_TO, 4.8f, +R_CUBIC_TO, 0, 3.1f, 2.68f, 5.6f, 6, 5.6f, +R_CUBIC_TO, 3.32f, 0, 6, -2.5f, 6, -5.6f, +V_LINE_TO, 7.6f, +CUBIC_TO, 16, 4.5f, 13.32f, 2, 10, 2, +CLOSE, +R_MOVE_TO, 4, 5.5f, +V_LINE_TO, 8, +R_H_LINE_TO, -3, +V_LINE_TO, 4, +R_CUBIC_TO, 2, 0, 3, 2, 3, 3.5f, +CLOSE, +MOVE_TO, 9, 4, +R_V_LINE_TO, 4, +H_LINE_TO, 6, +R_V_LINE_TO, -0.4f, +CUBIC_TO, 6, 6, 7, 4, 9, 4, +CLOSE, +R_MOVE_TO, 1, 12, +R_CUBIC_TO, -2.37f, 0, -4, -1.39f, -4, -3.6f, +V_LINE_TO, 10, +R_H_LINE_TO, 8, +R_V_LINE_TO, 2.4f, +R_CUBIC_TO, 0, 2.21f, -1.63f, 3.6f, -4, 3.6f, +CLOSE, +NEW_PATH, +PATH_COLOR_ARGB, 0xFF, 0x20, 0x21, 0x24, +MOVE_TO, 6, 4, +R_H_LINE_TO, 3, +R_V_LINE_TO, 4, +H_LINE_TO, 6, +CLOSE
diff --git a/chrome/app/vector_icons/mouse_left_click_view.icon b/chrome/app/vector_icons/mouse_left_click_view.icon new file mode 100644 index 0000000..13fbbbc --- /dev/null +++ b/chrome/app/vector_icons/mouse_left_click_view.icon
@@ -0,0 +1,40 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +PATH_COLOR_ARGB, 0xFF, 0xFF, 0xFF, 0xFF, +MOVE_TO, 10, 2, +CUBIC_TO, 6.68f, 2, 4, 4.5f, 4, 7.6f, +R_V_LINE_TO, 4.8f, +R_CUBIC_TO, 0, 3.1f, 2.68f, 5.6f, 6, 5.6f, +R_CUBIC_TO, 3.32f, 0, 6, -2.5f, 6, -5.6f, +V_LINE_TO, 7.6f, +CUBIC_TO, 16, 4.5f, 13.32f, 2, 10, 2, +CLOSE, +R_MOVE_TO, 4, 5.5f, +V_LINE_TO, 8, +R_H_LINE_TO, -3, +V_LINE_TO, 4, +R_CUBIC_TO, 2, 0, 3, 2, 3, 3.5f, +CLOSE, +MOVE_TO, 9, 4, +R_V_LINE_TO, 4, +H_LINE_TO, 6, +R_V_LINE_TO, -0.4f, +CUBIC_TO, 6, 6, 7, 4, 9, 4, +CLOSE, +R_MOVE_TO, 1, 12, +R_CUBIC_TO, -2.37f, 0, -4, -1.39f, -4, -3.6f, +V_LINE_TO, 10, +R_H_LINE_TO, 8, +R_V_LINE_TO, 2.4f, +R_CUBIC_TO, 0, 2.21f, -1.63f, 3.6f, -4, 3.6f, +CLOSE, +NEW_PATH, +PATH_COLOR_ARGB, 0xFF, 0xFF, 0xFF, 0xFF, +MOVE_TO, 6, 4, +R_H_LINE_TO, 3, +R_V_LINE_TO, 4, +H_LINE_TO, 6, +CLOSE
diff --git a/chrome/app/vector_icons/mouse_right_click_edit.icon b/chrome/app/vector_icons/mouse_right_click_edit.icon new file mode 100644 index 0000000..5ac40cc7 --- /dev/null +++ b/chrome/app/vector_icons/mouse_right_click_edit.icon
@@ -0,0 +1,40 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +PATH_COLOR_ARGB, 0xFF, 0x20, 0x21, 0x24, +MOVE_TO, 10, 2, +CUBIC_TO, 6.68f, 2, 4, 4.5f, 4, 7.6f, +R_V_LINE_TO, 4.8f, +R_CUBIC_TO, 0, 3.1f, 2.68f, 5.6f, 6, 5.6f, +R_CUBIC_TO, 3.32f, 0, 6, -2.5f, 6, -5.6f, +V_LINE_TO, 7.6f, +CUBIC_TO, 16, 4.5f, 13.32f, 2, 10, 2, +CLOSE, +R_MOVE_TO, 4, 5.5f, +V_LINE_TO, 8, +R_H_LINE_TO, -3, +V_LINE_TO, 4, +R_CUBIC_TO, 2, 0, 3, 2, 3, 3.5f, +CLOSE, +MOVE_TO, 9, 4, +R_V_LINE_TO, 4, +H_LINE_TO, 6, +R_V_LINE_TO, -0.4f, +CUBIC_TO, 6, 6, 7, 4, 9, 4, +CLOSE, +R_MOVE_TO, 1, 12, +R_CUBIC_TO, -2.37f, 0, -4, -1.39f, -4, -3.6f, +V_LINE_TO, 10, +R_H_LINE_TO, 8, +R_V_LINE_TO, 2.4f, +R_CUBIC_TO, 0, 2.21f, -1.63f, 3.6f, -4, 3.6f, +CLOSE, +NEW_PATH, +PATH_COLOR_ARGB, 0xFF, 0x20, 0x21, 0x24, +MOVE_TO, 11, 4, +R_H_LINE_TO, 3, +R_V_LINE_TO, 4, +R_H_LINE_TO, -3, +CLOSE
diff --git a/chrome/app/vector_icons/mouse_right_click_view.icon b/chrome/app/vector_icons/mouse_right_click_view.icon new file mode 100644 index 0000000..fb042292 --- /dev/null +++ b/chrome/app/vector_icons/mouse_right_click_view.icon
@@ -0,0 +1,40 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +PATH_COLOR_ARGB, 0xFF, 0xFF, 0xFF, 0xFF, +MOVE_TO, 10, 2, +CUBIC_TO, 6.68f, 2, 4, 4.5f, 4, 7.6f, +R_V_LINE_TO, 4.8f, +R_CUBIC_TO, 0, 3.1f, 2.68f, 5.6f, 6, 5.6f, +R_CUBIC_TO, 3.32f, 0, 6, -2.5f, 6, -5.6f, +V_LINE_TO, 7.6f, +CUBIC_TO, 16, 4.5f, 13.32f, 2, 10, 2, +CLOSE, +R_MOVE_TO, 4, 5.5f, +V_LINE_TO, 8, +R_H_LINE_TO, -3, +V_LINE_TO, 4, +R_CUBIC_TO, 2, 0, 3, 2, 3, 3.5f, +CLOSE, +MOVE_TO, 9, 4, +R_V_LINE_TO, 4, +H_LINE_TO, 6, +R_V_LINE_TO, -0.4f, +CUBIC_TO, 6, 6, 7, 4, 9, 4, +CLOSE, +R_MOVE_TO, 1, 12, +R_CUBIC_TO, -2.37f, 0, -4, -1.39f, -4, -3.6f, +V_LINE_TO, 10, +R_H_LINE_TO, 8, +R_V_LINE_TO, 2.4f, +R_CUBIC_TO, 0, 2.21f, -1.63f, 3.6f, -4, 3.6f, +CLOSE, +NEW_PATH, +PATH_COLOR_ARGB, 0xFF, 0xFF, 0xFF, 0xFF, +MOVE_TO, 11, 4, +R_H_LINE_TO, 3, +R_V_LINE_TO, 4, +R_H_LINE_TO, -3, +CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index a6b6811..1e48a3b6 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -396,11 +396,6 @@ "domain_reliability/service_factory.h", "download/background_download_service_factory.cc", "download/background_download_service_factory.h", - "download/bubble/download_display.cc", - "download/bubble/download_display.h", - "download/bubble/download_display_controller.cc", - "download/bubble/download_display_controller.h", - "download/bubble/download_icon_state.h", "download/chrome_download_manager_delegate.cc", "download/chrome_download_manager_delegate.h", "download/deferred_client_wrapper.cc", @@ -1496,6 +1491,8 @@ "security_events/security_event_sync_bridge_impl.h", "segmentation_platform/chrome_browser_main_extra_parts_segmentation_platform.cc", "segmentation_platform/chrome_browser_main_extra_parts_segmentation_platform.h", + "segmentation_platform/model_provider_factory_impl.cc", + "segmentation_platform/model_provider_factory_impl.h", "segmentation_platform/segmentation_platform_config.cc", "segmentation_platform/segmentation_platform_config.h", "segmentation_platform/segmentation_platform_profile_observer.cc", @@ -3594,6 +3591,8 @@ "apps/intent_helper/intent_picker_auto_display_service_factory.cc", "apps/intent_helper/intent_picker_auto_display_service_factory.h", "apps/intent_helper/intent_picker_constants.h", + "apps/intent_helper/intent_picker_features.cc", + "apps/intent_helper/intent_picker_features.h", "apps/intent_helper/intent_picker_helpers.cc", "apps/intent_helper/intent_picker_helpers.h", "apps/intent_helper/intent_picker_internal.cc", @@ -3620,8 +3619,6 @@ "cart/cart_discount_link_fetcher.h", "cart/cart_discount_metric_collector.cc", "cart/cart_discount_metric_collector.h", - "cart/cart_features.cc", - "cart/cart_features.h", "cart/cart_handler.cc", "cart/cart_handler.h", "cart/cart_metrics_tracker.cc", @@ -3648,6 +3645,8 @@ "commerce/coupons/coupon_service_observer.h", "component_updater/commerce_heuristics_component_installer.cc", "component_updater/commerce_heuristics_component_installer.h", + "component_updater/desktop_screenshot_editor_component_installer.cc", + "component_updater/desktop_screenshot_editor_component_installer.h", "component_updater/desktop_sharing_hub_component_installer.cc", "component_updater/desktop_sharing_hub_component_installer.h", "component_updater/intervention_policy_database_component_installer.cc", @@ -3695,6 +3694,15 @@ "diagnostics/recon_diagnostics.h", "diagnostics/sqlite_diagnostics.cc", "diagnostics/sqlite_diagnostics.h", + "download/bubble/download_bubble_controller.cc", + "download/bubble/download_bubble_controller.h", + "download/bubble/download_bubble_prefs.cc", + "download/bubble/download_bubble_prefs.h", + "download/bubble/download_display.cc", + "download/bubble/download_display.h", + "download/bubble/download_display_controller.cc", + "download/bubble/download_display_controller.h", + "download/bubble/download_icon_state.h", "download/default_download_dir_policy_handler.cc", "download/default_download_dir_policy_handler.h", "download/download_commands.cc", @@ -5208,6 +5216,8 @@ "lacros/lacros_url_handling.h", "lacros/launcher_search/search_controller_lacros.cc", "lacros/launcher_search/search_controller_lacros.h", + "lacros/launcher_search/search_util.cc", + "lacros/launcher_search/search_util.h", "lacros/metrics_reporting_observer.cc", "lacros/metrics_reporting_observer.h", "lacros/net/lacros_extension_proxy_tracker.cc", @@ -7311,6 +7321,7 @@ "permissions/prediction_model_handler_factory.cc", "permissions/prediction_model_handler_factory.h", ] + deps += [ "//components/segmentation_platform/internal:optimization_guide_segmentation_handler" ] } }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index bdb3367..80c69ea7 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -210,6 +210,7 @@ #endif #if BUILDFLAG(IS_CHROMEOS) +#include "chrome/browser/apps/intent_helper/intent_picker_features.h" #include "chromeos/constants/chromeos_features.h" #endif @@ -1001,6 +1002,15 @@ std::size(kPageContentAnnotationsTitleParams), nullptr}, }; const FeatureEntry::FeatureParam + kJourneysSortClustersWithinBatchForQueryParams[] = { + {"JourneysSortClustersWithinBatchForQuery", "true"}, +}; +const FeatureEntry::FeatureVariation kJourneysVariations[] = { + {"Sort Clusters Within Batch for Query", + kJourneysSortClustersWithinBatchForQueryParams, + std::size(kJourneysSortClustersWithinBatchForQueryParams), nullptr}, +}; +const FeatureEntry::FeatureParam kJourneysOnDeviceClusteringLabelingNoContentClusteringParams[] = { {"should_label_clusters", "true"}, {"content_clustering_enabled", "false"}, @@ -1508,11 +1518,11 @@ // The following are consent v2 variations in the Chrome Cart module. const flags_ui::FeatureEntry::FeatureParam kDiscountConsentNtpStringChange[] = { - {ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "1"}}; + {commerce::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "1"}}; const flags_ui::FeatureEntry::FeatureParam kDiscountConsentNtpInline[] = { - {ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "2"}}; + {commerce::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "2"}}; const flags_ui::FeatureEntry::FeatureParam kDiscountConsentNtpDialog[] = { - {ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "3"}}; + {commerce::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "3"}}; const FeatureEntry::FeatureVariation kDiscountConsentV2Variations[] = { {"Changing string", kDiscountConsentNtpStringChange, std::size(kDiscountConsentNtpStringChange), nullptr}, @@ -2935,6 +2945,13 @@ {"100", &kUnthrottledNestedTimeout_NestingLevel, 1, nullptr}, }; +constexpr FeatureEntry::FeatureParam kLensStandaloneWithSidePanel[] = { + {"enable-side-panel", "true"}}; +constexpr FeatureEntry::FeatureVariation kLensStandaloneVariations[] = { + {"With Side Panel", kLensStandaloneWithSidePanel, + std::size(kLensStandaloneWithSidePanel), nullptr}, +}; + // RECORDING USER METRICS FOR FLAGS: // ----------------------------------------------------------------------------- // The first line of the entry is the internal name. @@ -4098,6 +4115,18 @@ flag_descriptions::kCastStreamingVp9Description, kOsDesktop, FEATURE_VALUE_TYPE(mirroring::features::kCastStreamingVp9)}, + {"enable-cast-remoting-query-blocklist", + flag_descriptions::kCastUseBlocklistForRemotingQueryName, + flag_descriptions::kCastUseBlocklistForRemotingQueryDescription, + kOsDesktop, + FEATURE_VALUE_TYPE( + mirroring::features::kCastUseBlocklistForRemotingQuery)}, + + {"force-enable-cast-remoting-query", + flag_descriptions::kCastForceEnableRemotingQueryName, + flag_descriptions::kCastForceEnableRemotingQueryDescription, kOsDesktop, + FEATURE_VALUE_TYPE(mirroring::features::kCastForceEnableRemotingQuery)}, + #endif // !BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_ANDROID) @@ -5073,7 +5102,9 @@ {"history-journeys", flag_descriptions::kJourneysName, flag_descriptions::kJourneysDescription, kOsDesktop | kOsAndroid, - FEATURE_VALUE_TYPE(history_clusters::internal::kJourneys)}, + FEATURE_WITH_PARAMS_VALUE_TYPE(history_clusters::internal::kJourneys, + kJourneysVariations, + "HistoryJourneys")}, {"history-journeys-omnibox-action", flag_descriptions::kJourneysOmniboxActionName, @@ -5770,28 +5801,29 @@ "CCTResizableThirdPartiesDefaultPolicy")}, #endif -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) {"allow-dsp-based-aec", flag_descriptions::kCrOSDspBasedAecAllowedName, - flag_descriptions::kCrOSDspBasedAecAllowedDescription, kOsCrOS, + flag_descriptions::kCrOSDspBasedAecAllowedDescription, kOsCrOS | kOsLinux, FEATURE_VALUE_TYPE(features::kCrOSDspBasedAecAllowed)}, {"allow-dsp-based-ns", flag_descriptions::kCrOSDspBasedNsAllowedName, - flag_descriptions::kCrOSDspBasedNsAllowedDescription, kOsCrOS, + flag_descriptions::kCrOSDspBasedNsAllowedDescription, kOsCrOS | kOsLinux, FEATURE_VALUE_TYPE(features::kCrOSDspBasedNsAllowed)}, {"allow-dsp-based-agc", flag_descriptions::kCrOSDspBasedAgcAllowedName, - flag_descriptions::kCrOSDspBasedAgcAllowedDescription, kOsCrOS, + flag_descriptions::kCrOSDspBasedAgcAllowedDescription, kOsCrOS | kOsLinux, FEATURE_VALUE_TYPE(features::kCrOSDspBasedAgcAllowed)}, {"enforce-system-aec", flag_descriptions::kCrOSEnforceSystemAecName, - flag_descriptions::kCrOSEnforceSystemAecDescription, kOsCrOS, + flag_descriptions::kCrOSEnforceSystemAecDescription, kOsCrOS | kOsLinux, FEATURE_VALUE_TYPE(features::kCrOSEnforceSystemAec)}, {"enforce-system-aec-agc", flag_descriptions::kCrOSEnforceSystemAecAgcName, - flag_descriptions::kCrOSEnforceSystemAecAgcDescription, kOsCrOS, + flag_descriptions::kCrOSEnforceSystemAecAgcDescription, kOsCrOS | kOsLinux, FEATURE_VALUE_TYPE(features::kCrOSEnforceSystemAecAgc)}, {"enforce-system-aec-ns-agc", flag_descriptions::kCrOSEnforceSystemAecNsAgcName, - flag_descriptions::kCrOSEnforceSystemAecNsAgcDescription, kOsCrOS, + flag_descriptions::kCrOSEnforceSystemAecNsAgcDescription, + kOsCrOS | kOsLinux, FEATURE_VALUE_TYPE(features::kCrOSEnforceSystemAecNsAgc)}, {"enforce-system-aec-ns", flag_descriptions::kCrOSEnforceSystemAecNsName, - flag_descriptions::kCrOSEnforceSystemAecNsDescription, kOsCrOS, + flag_descriptions::kCrOSEnforceSystemAecNsDescription, kOsCrOS | kOsLinux, FEATURE_VALUE_TYPE(features::kCrOSEnforceSystemAecNs)}, #endif @@ -6486,6 +6518,11 @@ FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kLensCameraAssistedSearch, kLensCameraAssistedSearchVariations, "LensCameraAssistedSearch")}, + + {"location-bar-model-optimizations", + flag_descriptions::kLocationBarModelOptimizationsName, + flag_descriptions::kLocationBarModelOptimizationsDescription, kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kLocationBarModelOptimizations)}, #endif // BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_CHROMEOS_ASH) @@ -7637,10 +7674,12 @@ FEATURE_VALUE_TYPE( autofill::features::kAutofillSuggestVirtualCardsOnIncompleteForm)}, - {flag_descriptions::kEnableLensRegionSearchFlagId, - flag_descriptions::kEnableLensRegionSearchName, - flag_descriptions::kEnableLensRegionSearchDescription, kOsDesktop, - FEATURE_VALUE_TYPE(lens::features::kLensRegionSearch)}, + {flag_descriptions::kEnableLensStandaloneFlagId, + flag_descriptions::kEnableLensStandaloneName, + flag_descriptions::kEnableLensStandaloneDescription, kOsDesktop, + FEATURE_WITH_PARAMS_VALUE_TYPE(lens::features::kLensStandalone, + kLensStandaloneVariations, + "GoogleLensDesktopContextMenuSearch")}, {"enable-penetrating-image-selection", flag_descriptions::kEnablePenetratingImageSelectionName, @@ -7811,8 +7850,13 @@ FEATURE_VALUE_TYPE(net::features::kSamePartyCookiesConsideredFirstParty)}, {"partitioned-cookies", flag_descriptions::kPartitionedCookiesName, - flag_descriptions::kPartitionedCookiesDescription, kOsDesktop | kOsAndroid, + flag_descriptions::kPartitionedCookiesDescription, kOsAll, FEATURE_VALUE_TYPE(net::features::kPartitionedCookies)}, + // TODO(crbug.com/1296161): Remove this flag when the CHIPS OT ends. + {"partitioned-cookies-bypass-origin-trial", + flag_descriptions::kPartitionedCookiesBypassOriginTrialName, + flag_descriptions::kPartitionedCookiesBypassOriginTrialDescription, kOsAll, + FEATURE_VALUE_TYPE(net::features::kPartitionedCookiesBypassOriginTrial)}, #if BUILDFLAG(IS_CHROMEOS_ASH) {kBorealisBigGlInternalName, flag_descriptions::kBorealisBigGlName, @@ -8158,7 +8202,7 @@ #if BUILDFLAG(IS_CHROMEOS) {"link-capturing-ui-update", flag_descriptions::kLinkCapturingUiUpdateName, flag_descriptions::kLinkCapturingUiUpdateDescription, kOsCrOS, - FEATURE_VALUE_TYPE(features::kLinkCapturingUiUpdate)}, + FEATURE_VALUE_TYPE(apps::features::kLinkCapturingUiUpdate)}, #endif #if BUILDFLAG(IS_ANDROID) @@ -8365,6 +8409,17 @@ flag_descriptions::kOmitCorsClientCertDescription, kOsAll, FEATURE_VALUE_TYPE(network::features::kOmitCorsClientCert)}, +#if BUILDFLAG(IS_CHROMEOS) + {"link-capturing-infobar", flag_descriptions::kLinkCapturingInfoBarName, + flag_descriptions::kLinkCapturingInfoBarDescription, kOsCrOS, + FEATURE_VALUE_TYPE(apps::features::kLinkCapturingInfoBar)}, + + {"intent-chip-skips-intent-picker", + flag_descriptions::kIntentChipSkipsPickerName, + flag_descriptions::kIntentChipSkipsPickerDescription, kOsCrOS, + FEATURE_VALUE_TYPE(apps::features::kIntentChipSkipsPicker)}, +#endif + // NOTE: Adding a new flag requires adding a corresponding entry to enum // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/apps/app_service/app_service_proxy_base.cc b/chrome/browser/apps/app_service/app_service_proxy_base.cc index e6eb6d8..b816ae8 100644 --- a/chrome/browser/apps/app_service/app_service_proxy_base.cc +++ b/chrome/browser/apps/app_service/app_service_proxy_base.cc
@@ -20,7 +20,8 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/web_applications/web_app_id_constants.h" #include "components/services/app_service/app_service_mojom_impl.h" -#include "components/services/app_service/public/cpp/intent_constants.h" +#include "components/services/app_service/public/cpp/intent.h" +#include "components/services/app_service/public/cpp/intent_filter.h" #include "components/services/app_service/public/cpp/intent_filter_util.h" #include "components/services/app_service/public/cpp/intent_util.h" #include "components/services/app_service/public/cpp/types_util.h" @@ -46,9 +47,9 @@ bool is_generic; }; -std::string GetActivityLabel(const apps::mojom::IntentFilterPtr& filter, - const apps::AppUpdate& update) { - if (filter->activity_label && !filter->activity_label->empty()) { +std::string GetActivityLabel(const IntentFilterPtr& filter, + const AppUpdate& update) { + if (filter->activity_label.has_value() && !filter->activity_label->empty()) { return filter->activity_label.value(); } else { return update.Name(); @@ -497,12 +498,17 @@ } std::vector<IntentLaunchInfo> AppServiceProxyBase::GetAppsForIntent( - const apps::mojom::IntentPtr& intent, + const apps::mojom::IntentPtr& mojom_intent, bool exclude_browsers, bool exclude_browser_tab_apps) { std::vector<IntentLaunchInfo> intent_launch_info; - if (apps_util::OnlyShareToDrive(intent) || - !apps_util::IsIntentValid(intent)) { + if (apps_util::OnlyShareToDrive(mojom_intent) || + !apps_util::IsIntentValid(mojom_intent)) { + return intent_launch_info; + } + + auto intent = ConvertMojomIntentToIntent(mojom_intent); + if (!intent) { return intent_launch_info; } @@ -526,14 +532,14 @@ } // |activity_label| -> {index, is_generic} std::map<std::string, IndexAndGeneric> best_handler_map; - bool is_file_handling_intent = - intent->files.has_value() && intent->files->size() > 0; + bool is_file_handling_intent = !intent->files.empty(); size_t index = 0; for (const auto& filter : update.IntentFilters()) { - if (exclude_browsers && apps_util::IsBrowserFilter(filter)) { + DCHECK(filter); + if (exclude_browsers && filter->IsBrowserFilter()) { continue; } - if (apps_util::IntentMatchesFilter(intent, filter)) { + if (intent->MatchFilter(filter)) { // Return the first non-generic match if it exists, otherwise the // first generic match. bool generic = false; @@ -553,16 +559,14 @@ } const auto& filters = update.IntentFilters(); for (const auto& handler_entry : best_handler_map) { - const mojom::IntentFilterPtr& filter = - filters[handler_entry.second.index]; + const IntentFilterPtr& filter = filters[handler_entry.second.index]; IntentLaunchInfo entry; entry.app_id = update.AppId(); entry.activity_label = GetActivityLabel(filter, update); entry.activity_name = filter->activity_name.value_or(""); entry.is_generic_file_handler = apps_util::IsGenericFileHandler(intent, filter); - entry.is_file_extension_match = - apps_util::FilterIsForFileExtensions(filter); + entry.is_file_extension_match = filter->IsFileExtensionsFilter(); intent_launch_info.push_back(entry); } }); @@ -598,7 +602,7 @@ // Treat kUseBrowserForLink like an app with a single supported link, so // that any apps with overlapping supported links will have their preference // removed correctly. - if (app_id == apps::kUseBrowserForLink) { + if (app_id == apps_util::kUseBrowserForLink) { std::vector<apps::mojom::IntentFilterPtr> filters; filters.push_back(std::move(intent_filter)); app_service_->SetSupportedLinksPreference(apps::mojom::AppType::kUnknown, @@ -630,7 +634,7 @@ app_id, [&app_id, &filters](const AppUpdate& app) { for (auto& filter : app.IntentFilters()) { if (apps_util::IsSupportedLinkForApp(app_id, filter)) { - filters.push_back(std::move(filter)); + filters.push_back(ConvertIntentFilterToMojomIntentFilter(filter)); } } }); @@ -701,25 +705,30 @@ } apps::mojom::IntentFilterPtr AppServiceProxyBase::FindBestMatchingFilter( - const apps::mojom::IntentPtr& intent) { + const apps::mojom::IntentPtr& mojom_intent) { apps::mojom::IntentFilterPtr best_matching_intent_filter; - if (!app_service_.is_bound()) { + if (!app_service_.is_bound() || !mojom_intent) { return best_matching_intent_filter; } - int best_match_level = apps_util::IntentFilterMatchLevel::kNone; + auto intent = ConvertMojomIntentToIntent(mojom_intent); + if (!intent) { + return best_matching_intent_filter; + } + int best_match_level = static_cast<int>(IntentFilterMatchLevel::kNone); app_registry_cache_.ForEachApp( [&intent, &best_match_level, &best_matching_intent_filter](const apps::AppUpdate& update) { for (const auto& filter : update.IntentFilters()) { - if (!apps_util::IntentMatchesFilter(intent, filter)) { + if (!intent->MatchFilter(filter)) { continue; } - auto match_level = apps_util::GetFilterMatchLevel(filter); + auto match_level = filter->GetFilterMatchLevel(); if (match_level <= best_match_level) { continue; } - best_matching_intent_filter = filter->Clone(); + best_matching_intent_filter = + ConvertIntentFilterToMojomIntentFilter(filter); best_match_level = match_level; } });
diff --git a/chrome/browser/apps/app_service/app_service_proxy_lacros.cc b/chrome/browser/apps/app_service/app_service_proxy_lacros.cc index c726d84..3a218d1 100644 --- a/chrome/browser/apps/app_service/app_service_proxy_lacros.cc +++ b/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
@@ -462,12 +462,17 @@ } std::vector<IntentLaunchInfo> AppServiceProxyLacros::GetAppsForIntent( - const apps::mojom::IntentPtr& intent, + const apps::mojom::IntentPtr& mojom_intent, bool exclude_browsers, bool exclude_browser_tab_apps) { std::vector<IntentLaunchInfo> intent_launch_info; - if (apps_util::OnlyShareToDrive(intent) || - !apps_util::IsIntentValid(intent)) { + if (apps_util::OnlyShareToDrive(mojom_intent) || + !apps_util::IsIntentValid(mojom_intent)) { + return intent_launch_info; + } + + auto intent = ConvertMojomIntentToIntent(mojom_intent); + if (!intent) { return intent_launch_info; } @@ -485,10 +490,11 @@ } std::set<std::string> existing_activities; for (const auto& filter : update.IntentFilters()) { - if (exclude_browsers && apps_util::IsBrowserFilter(filter)) { + DCHECK(filter); + if (exclude_browsers && filter->IsBrowserFilter()) { continue; } - if (apps_util::IntentMatchesFilter(intent, filter)) { + if (intent->MatchFilter(filter)) { IntentLaunchInfo entry; entry.app_id = update.AppId(); std::string activity_label;
diff --git a/chrome/browser/apps/app_service/app_service_proxy_unittest.cc b/chrome/browser/apps/app_service/app_service_proxy_unittest.cc index e65f0ddd..958316d5 100644 --- a/chrome/browser/apps/app_service/app_service_proxy_unittest.cc +++ b/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
@@ -16,7 +16,6 @@ #include "chrome/test/base/testing_profile.h" #include "components/services/app_service/public/cpp/app_types.h" #include "components/services/app_service/public/cpp/icon_types.h" -#include "components/services/app_service/public/cpp/intent_constants.h" #include "components/services/app_service/public/cpp/intent_filter_util.h" #include "components/services/app_service/public/cpp/intent_test_util.h" #include "components/services/app_service/public/cpp/intent_util.h" @@ -483,24 +482,24 @@ // Setting "use browser" for a URL currently handled by App 1 should unset // both of App 1's links. - proxy()->AddPreferredApp(apps::kUseBrowserForLink, kTestUrl1); + proxy()->AddPreferredApp(apps_util::kUseBrowserForLink, kTestUrl1); proxy()->FlushMojoCallsForTesting(); - ASSERT_EQ(apps::kUseBrowserForLink, + ASSERT_EQ(apps_util::kUseBrowserForLink, proxy()->PreferredApps().FindPreferredAppForUrl(kTestUrl1)); ASSERT_EQ(absl::nullopt, proxy()->PreferredApps().FindPreferredAppForUrl(kTestUrl2)); - proxy()->AddPreferredApp(apps::kUseBrowserForLink, kTestUrl3); + proxy()->AddPreferredApp(apps_util::kUseBrowserForLink, kTestUrl3); proxy()->FlushMojoCallsForTesting(); - ASSERT_EQ(apps::kUseBrowserForLink, + ASSERT_EQ(apps_util::kUseBrowserForLink, proxy()->PreferredApps().FindPreferredAppForUrl(kTestUrl3)); // Changing the setting back from "use browser" to App 1 should only update // that "use-browser" setting, settings for other URLs are unchanged. proxy()->AddPreferredApp(kTestAppId1, kTestUrl1); proxy()->FlushMojoCallsForTesting(); - ASSERT_EQ(apps::kUseBrowserForLink, + ASSERT_EQ(apps_util::kUseBrowserForLink, proxy()->PreferredApps().FindPreferredAppForUrl(kTestUrl3)); }
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc b/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc index 719875db..c66d0376d 100644 --- a/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc +++ b/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
@@ -130,7 +130,7 @@ apps::AppServiceProxyFactory::GetForProfile(profile()) ->AppRegistryCache() .ForApp(app_id, [&target](const apps::AppUpdate& update) { - target = update.GetIntentFilters(); + target = update.IntentFilters(); }); EXPECT_EQ(source.size(), target.size());
diff --git a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc index c32e4a1e..3dfd3889 100644 --- a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc +++ b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
@@ -326,7 +326,7 @@ apps::AppServiceProxyFactory::GetForProfile(profile()) ->AppRegistryCache() .ForApp(app_id, [&target](const apps::AppUpdate& update) { - target = update.GetIntentFilters(); + target = update.IntentFilters(); }); EXPECT_EQ(source.size(), target.size());
diff --git a/chrome/browser/apps/app_service/webapk/webapk_manager.cc b/chrome/browser/apps/app_service/webapk/webapk_manager.cc index 3f326c4..3ef17c89 100644 --- a/chrome/browser/apps/app_service/webapk/webapk_manager.cc +++ b/chrome/browser/apps/app_service/webapk/webapk_manager.cc
@@ -24,6 +24,7 @@ #include "chrome/browser/web_applications/web_app_utils.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h" +#include "components/services/app_service/public/cpp/intent.h" #include "components/services/app_service/public/cpp/intent_util.h" #include "components/services/app_service/public/mojom/types.mojom-shared.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -32,11 +33,10 @@ constexpr char kGeneratedWebApkPackagePrefix[] = "org.chromium.webapk."; bool HasShareIntentFilter(const apps::AppUpdate& app) { - auto intent = apps::mojom::Intent::New(); - intent->action = apps_util::kIntentActionSend; + auto intent = std::make_unique<apps::Intent>(apps_util::kIntentActionSend); for (const auto& filter : app.IntentFilters()) { for (const auto& condition : filter->conditions) { - if (apps_util::IntentMatchesCondition(intent, condition)) { + if (intent->MatchCondition(condition)) { return true; } }
diff --git a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc index 5b40f452..5193dae 100644 --- a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc +++ b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
@@ -230,10 +230,8 @@ // Validate that the scheme is something that could be registered by the PWA // via the manifest. - bool has_custom_scheme_prefix = false; - if (!blink::IsValidCustomHandlerScheme(url.scheme(), - /* allow_ext_plus_prefix */ false, - has_custom_scheme_prefix)) { + if (!blink::IsValidCustomHandlerScheme( + url.scheme(), blink::ProtocolHandlerSecurityLevel::kStrict)) { DLOG(ERROR) << "Protocol is not a valid custom handler scheme."; continue; }
diff --git a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc index 785a8d6..db419252 100644 --- a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc +++ b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
@@ -9,21 +9,20 @@ #include "base/bind.h" #include "base/debug/dump_without_crashing.h" -#include "base/feature_list.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/apps/app_service/launch_utils.h" #include "chrome/browser/apps/intent_helper/apps_navigation_types.h" #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h" +#include "chrome/browser/apps/intent_helper/intent_picker_features.h" #include "chrome/browser/apps/intent_helper/intent_picker_internal.h" #include "chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h" #include "chrome/browser/apps/intent_helper/supported_links_infobar_delegate.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/intent_picker_tab_helper.h" #include "chrome/browser/ui/web_applications/web_app_launch_utils.h" -#include "chrome/common/chrome_features.h" -#include "components/services/app_service/public/cpp/intent_constants.h" +#include "components/services/app_service/public/cpp/intent_util.h" #include "components/services/app_service/public/mojom/types.mojom.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/visibility.h" @@ -51,7 +50,7 @@ const GURL& url = navigation_handle->GetURL(); // Disable Auto-display when the Intent Chip is enabled. - if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) + if (features::LinkCapturingUiUpdateEnabled()) return false; if (apps_for_picker.empty()) @@ -88,7 +87,7 @@ if (proxy) { auto preferred_app_id = proxy->PreferredApps().FindPreferredAppForUrl(url); if (preferred_app_id.has_value() && - preferred_app_id.value() == apps::kUseBrowserForLink) { + preferred_app_id.value() == apps_util::kUseBrowserForLink) { return false; } } @@ -218,7 +217,7 @@ #if BUILDFLAG(IS_CHROMEOS_ASH) // TODO(crbug.com/1293173): Lacros support for the infobar UI. - if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) { + if (features::LinkCapturingInfoBarEnabled()) { SupportedLinksInfoBarDelegate::MaybeShowSupportedLinksInfoBar( web_contents, launch_name); }
diff --git a/chrome/browser/apps/intent_helper/intent_picker_features.cc b/chrome/browser/apps/intent_helper/intent_picker_features.cc new file mode 100644 index 0000000..3803422f --- /dev/null +++ b/chrome/browser/apps/intent_helper/intent_picker_features.cc
@@ -0,0 +1,33 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/apps/intent_helper/intent_picker_features.h" +#include "base/feature_list.h" + +namespace apps::features { + +const base::Feature kLinkCapturingUiUpdate{"LinkCapturingUiUpdate", + base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kLinkCapturingInfoBar{"LinkCapturingInfoBar", + base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kIntentChipSkipsPicker{"IntentChipSkipsPicker", + base::FEATURE_DISABLED_BY_DEFAULT}; + +bool LinkCapturingUiUpdateEnabled() { + return base::FeatureList::IsEnabled(kLinkCapturingUiUpdate); +} + +bool LinkCapturingInfoBarEnabled() { + return LinkCapturingUiUpdateEnabled() && + base::FeatureList::IsEnabled(kLinkCapturingInfoBar); +} + +bool ShouldIntentChipSkipIntentPicker() { + return LinkCapturingUiUpdateEnabled() && + base::FeatureList::IsEnabled(kIntentChipSkipsPicker); +} + +} // namespace apps::features
diff --git a/chrome/browser/apps/intent_helper/intent_picker_features.h b/chrome/browser/apps/intent_helper/intent_picker_features.h new file mode 100644 index 0000000..4407f76 --- /dev/null +++ b/chrome/browser/apps/intent_helper/intent_picker_features.h
@@ -0,0 +1,31 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_APPS_INTENT_HELPER_INTENT_PICKER_FEATURES_H_ +#define CHROME_BROWSER_APPS_INTENT_HELPER_INTENT_PICKER_FEATURES_H_ + +#include "base/feature_list.h" + +namespace apps::features { + +extern const base::Feature kLinkCapturingUiUpdate; +extern const base::Feature kLinkCapturingInfoBar; +extern const base::Feature kIntentChipSkipsPicker; + +// Returns true if the overall link capturing UI update feature is enabled. +bool LinkCapturingUiUpdateEnabled(); + +// Returns true if clicking the Intent Chip should skip the Intent Picker when +// there is only one relevant app. Only returns true if +// LinkCapturingUiUpdateEnabled() returns true. +bool ShouldIntentChipSkipIntentPicker(); + +// Returns true if the Link Capturing Info Bar should be shown when launching a +// web app through the Intent Picker. Only returns true if +// LinkCapturingUiUpdateEnabled() returns true. +bool LinkCapturingInfoBarEnabled(); + +} // namespace apps::features + +#endif // CHROME_BROWSER_APPS_INTENT_HELPER_INTENT_PICKER_FEATURES_H_
diff --git a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc index e1f8af2..750c39e1 100644 --- a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc +++ b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
@@ -7,17 +7,16 @@ #include <string> #include <utility> -#include "base/feature_list.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h" +#include "chrome/browser/apps/intent_helper/intent_picker_features.h" #include "chrome/browser/apps/intent_helper/intent_picker_internal.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/intent_picker_tab_helper.h" #include "chrome/browser/ui/web_applications/web_app_launch_utils.h" -#include "chrome/common/chrome_features.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" @@ -183,8 +182,7 @@ apps::IntentHandlingMetrics::IntentPickerIconEvent::kIconClicked); #endif - if (apps.size() == 1 && - base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) { + if (apps.size() == 1 && apps::features::ShouldIntentChipSkipIntentPicker()) { LaunchAppFromIntentPicker(web_contents, url, apps[0].launch_name, apps[0].type); return;
diff --git a/chrome/browser/apps/platform_apps/api/BUILD.gn b/chrome/browser/apps/platform_apps/api/BUILD.gn index d0f483cd..4faa6e2 100644 --- a/chrome/browser/apps/platform_apps/api/BUILD.gn +++ b/chrome/browser/apps/platform_apps/api/BUILD.gn
@@ -77,9 +77,9 @@ ] deps += [ + "//ash/components/multidevice/logging", "//ash/components/proximity_auth", "//chrome:strings", - "//chromeos/components/multidevice/logging", "//components/account_id", "//components/strings:components_strings_grit", "//components/user_manager",
diff --git a/chrome/browser/ash/android_sms/android_sms_app_manager_impl.cc b/chrome/browser/ash/android_sms/android_sms_app_manager_impl.cc index 7aa87e76..7fa4fb1 100644 --- a/chrome/browser/ash/android_sms/android_sms_app_manager_impl.cc +++ b/chrome/browser/ash/android_sms/android_sms_app_manager_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/callback.h" #include "base/callback_helpers.h" @@ -17,7 +18,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/app_list/app_list_syncable_service.h" #include "chrome/browser/web_applications/web_app_provider.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc b/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc index c44fd4b..478aa49 100644 --- a/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc +++ b/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc
@@ -7,6 +7,7 @@ #include <string> #include <vector> +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/callback.h" #include "base/containers/flat_map.h" @@ -18,7 +19,6 @@ #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/browser/web_applications/web_app_install_finalizer.h" #include "chrome/browser/web_applications/web_app_provider.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/webapps/browser/install_result_code.h" #include "components/webapps/browser/installable/installable_metrics.h"
diff --git a/chrome/browser/ash/android_sms/android_sms_pairing_state_tracker_impl.cc b/chrome/browser/ash/android_sms/android_sms_pairing_state_tracker_impl.cc index 43049071..3f93d078 100644 --- a/chrome/browser/ash/android_sms/android_sms_pairing_state_tracker_impl.cc +++ b/chrome/browser/ash/android_sms/android_sms_pairing_state_tracker_impl.cc
@@ -7,12 +7,12 @@ #include <utility> #include <vector> +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/logging.h" #include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/ash/android_sms/android_sms_urls.h" #include "chrome/browser/profiles/profile.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/storage_partition.h" #include "net/cookies/canonical_cookie.h"
diff --git a/chrome/browser/ash/android_sms/connection_manager.cc b/chrome/browser/ash/android_sms/connection_manager.cc index ccb57306..d7df82d7 100644 --- a/chrome/browser/ash/android_sms/connection_manager.cc +++ b/chrome/browser/ash/android_sms/connection_manager.cc
@@ -6,10 +6,10 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "chrome/browser/ash/android_sms/android_sms_urls.h" #include "chrome/browser/ash/android_sms/connection_establisher.h" #include "chrome/browser/profiles/profile.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/session_manager/core/session_manager.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/service_worker_context.h"
diff --git a/chrome/browser/ash/android_sms/fcm_connection_establisher.cc b/chrome/browser/ash/android_sms/fcm_connection_establisher.cc index 1534f2e..3c29133 100644 --- a/chrome/browser/ash/android_sms/fcm_connection_establisher.cc +++ b/chrome/browser/ash/android_sms/fcm_connection_establisher.cc
@@ -6,9 +6,9 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "base/metrics/histogram_macros.h" #include "base/strings/utf_string_conversions.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "third_party/blink/public/common/messaging/string_message_codec.h"
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action_move.cc b/chrome/browser/ash/arc/input_overlay/actions/action_move.cc index 629438ef..b71d3d4c 100644 --- a/chrome/browser/ash/arc/input_overlay/actions/action_move.cc +++ b/chrome/browser/ash/arc/input_overlay/actions/action_move.cc
@@ -7,6 +7,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ash/arc/input_overlay/actions/action.h" #include "chrome/browser/ash/arc/input_overlay/touch_id_manager.h" +#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/gfx/geometry/point.h" @@ -20,6 +21,8 @@ constexpr char kTargetArea[] = "target_area"; constexpr char kTopLeft[] = "top_left"; constexpr char kBottomRight[] = "bottom_right"; +// TODO(cuicuiruan): remove this and replace it with image asset. +constexpr char kMouseCursorLock[] = "mouse cursor lock (esc)"; constexpr int kAxisSize = 2; constexpr int kDirection[kActionMoveKeysSize][kAxisSize] = {{0, -1}, @@ -28,7 +31,7 @@ {1, 0}}; // UI specs. // Offset by label center. -constexpr int kLabelOffset = 49; +constexpr int kTagOffset = 49; std::unique_ptr<Position> ParseApplyAreaPosition(const base::Value& value, base::StringPiece key) { @@ -53,14 +56,13 @@ DisplayOverlayController* display_overlay_controller, const gfx::RectF& content_bounds) : ActionView(action, display_overlay_controller) { - auto label = std::make_unique<ActionLabel>(u"mouse cursor lock (esc)"); - label->set_editable(false); - auto label_size = label->GetPreferredSize(); - label->SetSize(label_size); - SetSize(label_size); - center_.set_x(label_size.width() / 2); - center_.set_y(label_size.height() / 2); - labels_.emplace_back(AddChildView(std::move(label))); + auto tag = ActionTag::CreateTextActionTag(kMouseCursorLock); + auto tag_size = tag->GetPreferredSize(); + tag->SetSize(tag_size); + SetSize(tag_size); + center_.set_x(tag_size.width() / 2); + center_.set_y(tag_size.height() / 2); + tags_.emplace_back(AddChildView(std::move(tag))); } ActionMoveMouseView(const ActionMoveMouseView&) = delete; @@ -87,17 +89,16 @@ auto keys = action->current_binding()->keys(); for (int i = 0; i < keys.size(); i++) { auto text = GetDisplayText(keys[i]); - auto label = std::make_unique<ActionLabel>(base::UTF8ToUTF16(text)); - label->set_editable(true); - auto label_size = label->GetPreferredSize(); - label->SetSize(label_size); + auto tag = ActionTag::CreateTextActionTag(text); + auto tag_size = tag->GetPreferredSize(); + tag->SetSize(tag_size); int x = kDirection[i][0]; int y = kDirection[i][1]; auto pos = gfx::Point( - radius + x * (radius - kLabelOffset) - label_size.width() / 2, - radius + y * (radius - kLabelOffset) - label_size.height() / 2); - label->SetPosition(pos); - labels_.emplace_back(AddChildView(std::move(label))); + radius + x * (radius - kTagOffset) - tag_size.width() / 2, + radius + y * (radius - kTagOffset) - tag_size.height() / 2); + tag->SetPosition(pos); + tags_.emplace_back(AddChildView(std::move(tag))); } }
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc b/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc index b4e4a56..3123872 100644 --- a/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc +++ b/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc
@@ -5,8 +5,9 @@ #include "chrome/browser/ash/arc/input_overlay/actions/action_tap.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/ash/arc/input_overlay/actions/input_element.h" #include "chrome/browser/ash/arc/input_overlay/touch_id_manager.h" -#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h" +#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h" #include "ui/aura/window.h" #include "ui/events/base_event_utils.h" #include "ui/events/keycodes/dom/dom_code.h" @@ -18,8 +19,8 @@ namespace input_overlay { namespace { // UI specs. -constexpr int kLabelPositionToSide = 36; -constexpr int kLabelMargin = 2; +constexpr int kTagPositionToSide = 36; +constexpr int kTagMargin = 2; } // namespace class ActionTap::ActionTapView : public ActionView { @@ -30,39 +31,38 @@ : ActionView(action, display_overlay_controller) { int radius = action->GetUIRadius(content_bounds); auto circle = std::make_unique<ActionCircle>(radius); - std::string text; + std::unique_ptr<ActionTag> tag; if (action->IsKeyboardBound()) { - text = GetDisplayText(action->current_binding()->keys()[0]); + tag = ActionTag::CreateTextActionTag( + GetDisplayText(action->current_binding()->keys()[0])); } else if (action->IsMouseBound()) { - text = action->current_binding()->mouse_action(); + tag = ActionTag::CreateImageActionTag( + action->current_binding()->mouse_action()); } else { - text = "?"; + tag = ActionTag::CreateTextActionTag("?"); } - auto label = std::make_unique<ActionLabel>(base::UTF8ToUTF16(text)); - label->set_editable(true); - auto label_size = label->GetPreferredSize(); - label->SetSize(label_size); - int width = std::max( - radius * 2, radius * 2 - kLabelPositionToSide + label_size.width()); + auto tag_size = tag->GetPreferredSize(); + tag->SetSize(tag_size); + int width = std::max(radius * 2, + radius * 2 - kTagPositionToSide + tag_size.width()); SetSize(gfx::Size(width, radius * 2)); if (action->on_left_or_middle_side()) { circle->SetPosition(gfx::Point()); - label->SetPosition( - gfx::Point(label_size.width() > kLabelPositionToSide - ? width - label_size.width() - : width - kLabelPositionToSide, - radius * 2 - label_size.height() - kLabelMargin)); + tag->SetPosition(gfx::Point(tag_size.width() > kTagPositionToSide + ? width - tag_size.width() + : width - kTagPositionToSide, + radius * 2 - tag_size.height() - kTagMargin)); center_.set_x(radius); center_.set_y(radius); } else { circle->SetPosition(gfx::Point(width - radius * 2, 0)); - label->SetPosition( - gfx::Point(0, radius * 2 - label_size.height() - kLabelMargin)); + tag->SetPosition( + gfx::Point(0, radius * 2 - tag_size.height() - kTagMargin)); center_.set_x(width - radius); center_.set_y(radius); } circle_ = AddChildView(std::move(circle)); - labels_.emplace_back(AddChildView(std::move(label))); + tags_.emplace_back(AddChildView(std::move(tag))); } ActionTapView(const ActionTapView&) = delete;
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc index 4d0ad3e..e8aad2b 100644 --- a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc +++ b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
@@ -6,7 +6,7 @@ #include <set> -#include "chrome/browser/ash/arc/input_overlay/ui/action_view.h" +#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/gfx/color_palette.h" @@ -17,8 +17,8 @@ namespace { // UI specs. constexpr int kWidthPadding = 10; -constexpr gfx::Size kMinimumLabelSize(32, 32); -constexpr int kCornerRadius = 6; +constexpr gfx::Size kMinimumViewLabelSize(32, 32); +constexpr int kCornerRadiusView = 6; constexpr SkColor kViewModeBgColor = SkColorSetA(SK_ColorGRAY, 0x99); constexpr SkColor kEditModeBgColor = SK_ColorWHITE; @@ -53,39 +53,15 @@ ActionLabel::ActionLabel() : views::Label() {} ActionLabel::ActionLabel(const std::u16string& text) : views::Label(text) { - SetDisplayMode(DisplayMode::kView); + SetToViewMode(); } ActionLabel::~ActionLabel() = default; -void ActionLabel::SetDisplayMode(DisplayMode mode) { - switch (mode) { - case DisplayMode::kMenu: - case DisplayMode::kView: - SetToView(); - break; - case DisplayMode::kEdit: - SetToEditDefault(); - break; - default: - NOTREACHED(); - break; - } -} - -void ActionLabel::SetPositionFromCenterPosition(gfx::PointF& center_position) { - auto size = GetPreferredSize(); - SetSize(size); - int left = std::max(0, (int)(center_position.x() - size.width() / 2)); - int top = std::max(0, (int)(center_position.y() - size.height() / 2)); - // SetPosition function needs the top-left position. - SetPosition(gfx::Point(left, top)); -} - gfx::Size ActionLabel::CalculatePreferredSize() const { auto size = Label::CalculatePreferredSize(); size.set_width(size.width() + kWidthPadding); - size.SetToMax(kMinimumLabelSize); + size.SetToMax(kMinimumViewLabelSize); return size; } @@ -97,34 +73,36 @@ SetToEditFocus(); SelectAll(); Label::OnFocus(); - static_cast<ActionView*>(parent())->RemoveEditMenu(); + static_cast<ActionTag*>(parent())->OnActionLabelFocused(); } void ActionLabel::OnBlur() { - SetToEditDefault(); + SetToEditMode(); ClearSelection(); Label::OnBlur(); } -void ActionLabel::SetToView() { +void ActionLabel::SetToViewMode() { SetFontList(gfx::FontList({kFontSytle}, gfx::Font::NORMAL, kViewFontSize, gfx::Font::Weight::BOLD)); SetFocusBehavior(FocusBehavior::NEVER); SetBackground( - views::CreateRoundedRectBackground(kViewModeBgColor, kCornerRadius)); + views::CreateRoundedRectBackground(kViewModeBgColor, kCornerRadiusView)); SetAutoColorReadabilityEnabled(false); SetEnabledColor(kViewTextColor); + SetSize(GetPreferredSize()); SetSelectable(false); SetEnabled(false); } -void ActionLabel::SetToEditDefault() { +void ActionLabel::SetToEditMode() { SetFontList(gfx::FontList({kFontSytle}, gfx::Font::NORMAL, kUnFocusFontSize, gfx::Font::Weight::BOLD)); SetFocusBehavior(FocusBehavior::ALWAYS); SetBackground( - views::CreateRoundedRectBackground(kEditModeBgColor, kCornerRadius)); + views::CreateRoundedRectBackground(kEditModeBgColor, kCornerRadiusView)); SetEnabledColor(kEditTextColor); + SetSize(GetPreferredSize()); SetSelectable(true); SetEnabled(true); }
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_label.h b/chrome/browser/ash/arc/input_overlay/ui/action_label.h index adc96b1..568e7d7 100644 --- a/chrome/browser/ash/arc/input_overlay/ui/action_label.h +++ b/chrome/browser/ash/arc/input_overlay/ui/action_label.h
@@ -17,8 +17,7 @@ // on different keyboard layout. std::string GetDisplayText(const ui::DomCode code); -// ActionLabel is the basic UI label for the action. It can set default view -// mode and edit mode. +// ActionLabel shows text mapping hint for each action. class ActionLabel : public views::Label { public: ActionLabel(); @@ -28,11 +27,8 @@ ActionLabel& operator=(const ActionLabel&) = delete; ~ActionLabel() override; - void set_editable(bool editable) { editable_ = editable; } - - void SetDisplayMode(const DisplayMode mode); - // Set position from its center position. - void SetPositionFromCenterPosition(gfx::PointF& center_position); + void SetToViewMode(); + void SetToEditMode(); // views::View: gfx::Size CalculatePreferredSize() const override; @@ -41,12 +37,7 @@ void OnBlur() override; private: - void SetToView(); - void SetToEditDefault(); void SetToEditFocus(); - - private: - bool editable_ = false; }; } // namespace input_overlay } // namespace arc
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_tag.cc b/chrome/browser/ash/arc/input_overlay/ui/action_tag.cc new file mode 100644 index 0000000..19553f9 --- /dev/null +++ b/chrome/browser/ash/arc/input_overlay/ui/action_tag.cc
@@ -0,0 +1,144 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h" + +#include "chrome/app/vector_icons/vector_icons.h" +#include "chrome/browser/ash/arc/input_overlay/actions/input_element.h" +#include "chrome/browser/ash/arc/input_overlay/ui/action_view.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/views/background.h" +#include "ui/views/controls/button/image_button.h" + +namespace arc { +namespace input_overlay { +namespace { +constexpr int kIconSize = 20; +constexpr int kCornerRadius = 6; +constexpr SkColor kEditModeBgColor = SK_ColorWHITE; +constexpr SkColor kViewModeBgColor = SkColorSetA(SK_ColorGRAY, 0x99); +constexpr gfx::Size kImageButtonSize(32, 32); +} // namespace + +class ActionTag::ActionImage : public views::ImageButton { + public: + explicit ActionImage(std::string mouse_action) + : views::ImageButton(), mouse_action_(mouse_action) { + SetToViewMode(); + } + ~ActionImage() override = default; + + void SetDisplayMode(DisplayMode mode) { + switch (mode) { + case DisplayMode::kMenu: + case DisplayMode::kView: + SetToViewMode(); + break; + case DisplayMode::kEdit: + SetToEditMode(); + break; + default: + NOTREACHED(); + break; + } + } + + void SetToViewMode() { + if (mouse_action_ == kPrimaryClick) { + auto left_click_icon = gfx::CreateVectorIcon( + gfx::IconDescription(kMouseLeftClickViewIcon, kIconSize)); + SetImage(views::Button::STATE_NORMAL, left_click_icon); + } else { + auto right_click_icon = gfx::CreateVectorIcon( + gfx::IconDescription(kMouseRightClickViewIcon, kIconSize)); + SetImage(views::Button::STATE_NORMAL, right_click_icon); + } + SetBackground( + views::CreateRoundedRectBackground(kViewModeBgColor, kCornerRadius)); + } + + void SetToEditMode() { + if (mouse_action_ == kPrimaryClick) { + auto left_click_icon = gfx::CreateVectorIcon( + gfx::IconDescription(kMouseLeftClickEditIcon, kIconSize)); + SetImage(views::Button::STATE_NORMAL, left_click_icon); + } else { + auto right_click_icon = gfx::CreateVectorIcon( + gfx::IconDescription(kMouseRightClickEditIcon, kIconSize)); + SetImage(views::Button::STATE_NORMAL, right_click_icon); + } + SetBackground( + views::CreateRoundedRectBackground(kEditModeBgColor, kCornerRadius)); + } + + private: + std::string mouse_action_; +}; + +ActionTag::ActionTag() : views::View() {} +ActionTag::~ActionTag() = default; + +// static +std::unique_ptr<ActionTag> ActionTag::CreateTextActionTag(std::string text) { + auto tag = std::make_unique<ActionTag>(); + auto label = std::make_unique<ActionLabel>(base::UTF8ToUTF16(text)); + label->SetPosition(gfx::Point()); + tag->label_ = tag->AddChildView(std::move(label)); + + return tag; +} + +// static +std::unique_ptr<ActionTag> ActionTag::CreateImageActionTag( + std::string mouse_action) { + DCHECK(mouse_action == kPrimaryClick || mouse_action == kSecondaryClick); + if (mouse_action != kPrimaryClick && mouse_action != kSecondaryClick) + return nullptr; + auto tag = std::make_unique<ActionTag>(); + auto image = std::make_unique<ActionImage>(mouse_action); + image->SetAccessibleName(base::UTF8ToUTF16(image->GetClassName())); + image->SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER); + image->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE); + image->SetToViewMode(); + image->SetPosition(gfx::Point()); + image->SetSize(kImageButtonSize); + tag->image_ = tag->AddChildView(std::move(image)); + + return tag; +} + +void ActionTag::SetDisplayMode(DisplayMode mode) { + switch (mode) { + case DisplayMode::kMenu: + case DisplayMode::kView: + if (label_) + label_->SetToViewMode(); + if (image_) + image_->SetToViewMode(); + break; + case DisplayMode::kEdit: + if (label_) + label_->SetToEditMode(); + if (image_) + image_->SetToEditMode(); + break; + default: + NOTREACHED(); + break; + } +} + +void ActionTag::OnActionLabelFocused() { + static_cast<ActionView*>(parent())->RemoveEditMenu(); +} + +gfx::Size ActionTag::CalculatePreferredSize() const { + DCHECK((label_ && !image_) || (!label_ && image_)); + if (image_) + return image_->size(); + return label_ ? label_->GetPreferredSize() : gfx::Size(); +} + +} // namespace input_overlay +} // namespace arc
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_tag.h b/chrome/browser/ash/arc/input_overlay/ui/action_tag.h new file mode 100644 index 0000000..88fa2382 --- /dev/null +++ b/chrome/browser/ash/arc/input_overlay/ui/action_tag.h
@@ -0,0 +1,45 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_UI_ACTION_TAG_H_ +#define CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_UI_ACTION_TAG_H_ + +#include "chrome/browser/ash/arc/input_overlay/constants.h" +#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h" +#include "ui/views/view.h" + +namespace arc { +namespace input_overlay { + +// ActionTag is used to showing the mapping hint for each action. It can show +// text hint or image hint. +class ActionTag : public views::View { + public: + ActionTag(); + ~ActionTag() override; + + static std::unique_ptr<ActionTag> CreateTextActionTag(std::string text); + static std::unique_ptr<ActionTag> CreateImageActionTag( + std::string mouse_action); + + void SetDisplayMode(DisplayMode mode); + void OnActionLabelFocused(); + + // views::View: + gfx::Size CalculatePreferredSize() const override; + + private: + class ActionImage; + + void InitTextTag(); + void InitImageTag(); + + ActionImage* image_ = nullptr; + ActionLabel* label_ = nullptr; +}; + +} // namespace input_overlay +} // namespace arc + +#endif // CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_UI_ACTION_TAG_H_
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.cc b/chrome/browser/ash/arc/input_overlay/ui/action_view.cc index df488e5d..9f48b2f 100644 --- a/chrome/browser/ash/arc/input_overlay/ui/action_view.cc +++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
@@ -5,7 +5,6 @@ #include "chrome/browser/ash/arc/input_overlay/ui/action_view.h" #include "base/bind.h" -#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h" namespace arc { namespace input_overlay { @@ -34,8 +33,8 @@ AddEditButton(); if (circle_) circle_->SetDisplayMode(mode); - for (auto* label : labels_) - label->SetDisplayMode(mode); + for (auto* tag : tags_) + tag->SetDisplayMode(mode); } }
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.h b/chrome/browser/ash/arc/input_overlay/ui/action_view.h index fc51ce7a..894f9b9 100644 --- a/chrome/browser/ash/arc/input_overlay/ui/action_view.h +++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.h
@@ -11,7 +11,7 @@ #include "chrome/browser/ash/arc/input_overlay/display_overlay_controller.h" #include "chrome/browser/ash/arc/input_overlay/ui/action_circle.h" #include "chrome/browser/ash/arc/input_overlay/ui/action_edit_button.h" -#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h" +#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/point_f.h" #include "ui/views/view.h" @@ -56,7 +56,7 @@ // The circle view shows up for editing the action. ActionCircle* circle_ = nullptr; // Labels for mapping hints. - std::vector<ActionLabel*> labels_; + std::vector<ActionTag*> tags_; // Current display mode. DisplayMode current_display_mode_ = DisplayMode::kNone; // Center position of the circle view.
diff --git a/chrome/browser/ash/arc/optin/arc_optin_preference_handler.cc b/chrome/browser/ash/arc/optin/arc_optin_preference_handler.cc index 6b9878e..9490985 100644 --- a/chrome/browser/ash/arc/optin/arc_optin_preference_handler.cc +++ b/chrome/browser/ash/arc/optin/arc_optin_preference_handler.cc
@@ -27,12 +27,17 @@ // Metrics mode should be propagated to owner if current user is not the owner // OR ownership has not been taken. - const bool should_use_owner = + const bool is_owner = user_manager::UserManager::Get()->IsCurrentUserOwner() || ash::DeviceSettingsService::Get()->GetOwnershipStatus() == ash::DeviceSettingsService::OWNERSHIP_NONE; - return !should_use_owner; + if (is_owner) + return false; + + // Per user metrics should be disabled if the device metrics was disabled by + // the owner. + return ash::StatsReportingController::Get()->IsEnabled(); } } // namespace
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc index 4b1104e..2fb79574 100644 --- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc +++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -45,7 +45,6 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/path_service.h" -#include "base/rand_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/system/sys_info.h" @@ -169,6 +168,7 @@ #include "chrome/browser/component_updater/cros_component_installer_chromeos.h" #include "chrome/browser/defaults.h" #include "chrome/browser/device_identity/device_oauth2_token_service_factory.h" +#include "chrome/browser/first_run/first_run.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/metrics/chrome_feature_list_creator.h" #include "chrome/browser/metrics/structured/chrome_structured_metrics_recorder.h" @@ -1233,7 +1233,8 @@ chrome::GetChannel(), g_browser_process->local_state(), g_browser_process->system_network_context_manager() ->GetSharedURLLoaderFactory(), - base::Minutes(base::RandInt(0, 29))); + device_activity::DeviceActivityController::DetermineStartUpDelay( + first_run::GetFirstRunSentinelCreationTime())); } #endif
diff --git a/chrome/browser/ash/crostini/crostini_terminal.cc b/chrome/browser/ash/crostini/crostini_terminal.cc index e597b31c..e10d8e3 100644 --- a/chrome/browser/ash/crostini/crostini_terminal.cc +++ b/chrome/browser/ash/crostini/crostini_terminal.cc
@@ -52,7 +52,6 @@ #include "ui/color/color_provider_manager.h" #include "ui/gfx/geometry/point.h" #include "ui/native_theme/native_theme.h" -#include "ui/views/image_model_utils.h" namespace crostini { @@ -463,10 +462,9 @@ ui::ColorProviderManager::Get().GetColorProviderFor( ui::NativeTheme::GetInstanceForWeb()->GetColorProviderKey(nullptr)); auto icon = [color_provider](const gfx::VectorIcon& icon) { - return views::GetImageSkiaFromImageModel( - ui::ImageModel::FromVectorIcon(icon, ui::kColorMenuIcon, - apps::kAppShortcutIconSizeDip), - color_provider); + return ui::ImageModel::FromVectorIcon(icon, ui::kColorMenuIcon, + apps::kAppShortcutIconSizeDip) + .Rasterize(color_provider); }; gfx::ImageSkia terminal_ssh_icon = icon(kTerminalSshIcon); gfx::ImageSkia crostini_mascot_icon = icon(kCrostiniMascotIcon);
diff --git a/chrome/browser/ash/cryptauth/OWNERS b/chrome/browser/ash/cryptauth/OWNERS index 7027ab73..18377141 100644 --- a/chrome/browser/ash/cryptauth/OWNERS +++ b/chrome/browser/ash/cryptauth/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc b/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc index 2e0867144..7df8d86 100644 --- a/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc +++ b/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc
@@ -6,6 +6,7 @@ #include <string> +#include "ash/components/multidevice/logging/logging.h" #include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" #include "ash/services/device_sync/proto/cryptauth_better_together_feature_metadata.pb.h" @@ -23,7 +24,6 @@ #include "chrome/browser/ash/cryptauth/cryptauth_device_id_provider_impl.h" #include "chrome/browser/chrome_content_browser_client.h" #include "chrome/common/pref_names.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/network_state_handler.h" #include "chromeos/network/network_type_pattern.h" #include "components/gcm_driver/instance_id/instance_id_driver.h"
diff --git a/chrome/browser/ash/device_sync/OWNERS b/chrome/browser/ash/device_sync/OWNERS index 7027ab73..18377141 100644 --- a/chrome/browser/ash/device_sync/OWNERS +++ b/chrome/browser/ash/device_sync/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ash/device_sync/device_sync_client_factory.cc b/chrome/browser/ash/device_sync/device_sync_client_factory.cc index fb339fa..adad199 100644 --- a/chrome/browser/ash/device_sync/device_sync_client_factory.cc +++ b/chrome/browser/ash/device_sync/device_sync_client_factory.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/device_sync/device_sync_client_factory.h" +#include "ash/components/multidevice/stub_multidevice_util.h" #include "ash/services/device_sync/device_sync_impl.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/device_sync/public/cpp/device_sync_client_impl.h" @@ -16,7 +17,6 @@ #include "chrome/browser/gcm/gcm_profile_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" -#include "chromeos/components/multidevice/stub_multidevice_util.h" #include "components/gcm_driver/gcm_profile_service.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/core/keyed_service.h"
diff --git a/chrome/browser/ash/eche_app/eche_app_notification_controller.cc b/chrome/browser/ash/eche_app/eche_app_notification_controller.cc index 66242d09..e944485c 100644 --- a/chrome/browser/ash/eche_app/eche_app_notification_controller.cc +++ b/chrome/browser/ash/eche_app/eche_app_notification_controller.cc
@@ -4,12 +4,12 @@ #include "chrome/browser/ash/eche_app/eche_app_notification_controller.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/public/cpp/new_window_delegate.h" #include "chrome/browser/notifications/notification_display_service.h" #include "chrome/browser/ui/settings_window_manager_chromeos.h" #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" #include "chrome/grit/generated_resources.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/message_center/message_center.h"
diff --git a/chrome/browser/ash/file_manager/external_filesystem_apitest.cc b/chrome/browser/ash/file_manager/external_filesystem_apitest.cc index 81a5c13..4111f9a 100644 --- a/chrome/browser/ash/file_manager/external_filesystem_apitest.cc +++ b/chrome/browser/ash/file_manager/external_filesystem_apitest.cc
@@ -815,7 +815,7 @@ FLAGS_NONE)) << message_; } -IN_PROC_BROWSER_TEST_F(DriveFileSystemExtensionApiTest, FileWatch) { +IN_PROC_BROWSER_TEST_F(DriveFileSystemExtensionApiTest, DISABLED_FileWatch) { EXPECT_TRUE(RunFileSystemExtensionApiTest( "file_browser/file_watcher_test", FILE_PATH_LITERAL("manifest.json"),
diff --git a/chrome/browser/ash/file_manager/extract_io_task.cc b/chrome/browser/ash/file_manager/extract_io_task.cc index 278621a..30918cc 100644 --- a/chrome/browser/ash/file_manager/extract_io_task.cc +++ b/chrome/browser/ash/file_manager/extract_io_task.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/ash/file_manager/extract_io_task.h" +#include "base/files/file_util.h" +#include "base/strings/strcat.h" #include "chrome/browser/chromeos/fileapi/file_system_backend.h" #include "components/services/unzip/content/unzip_service.h" @@ -43,6 +45,22 @@ } } +base::FilePath ExtractIOTask::CreateDestinationName( + const base::FilePath& parent, + const base::FilePath& source_file) { + const base::FilePath target = source_file.BaseName().RemoveExtension(); + base::FilePath destination_directory = parent.Append(target); + if (base::PathExists(destination_directory)) { + // Create a unique name for the output directory. + for (int i = 1; base::PathExists(destination_directory) && i < kMaxRetries; + ++i) { + destination_directory = parent.Append( + base::StrCat({target.value(), " (", base::NumberToString(i), ")"})); + } + } + return destination_directory; +} + void ExtractIOTask::Execute(IOTask::ProgressCallback progress_callback, IOTask::CompleteCallback complete_callback) { progress_callback_ = std::move(progress_callback); @@ -55,10 +73,15 @@ const base::FilePath source_file = source.url.path(); // TODO(crbug.com/953256) Perform this check only once. if (chromeos::FileSystemBackend::CanHandleURL(parent_folder_)) { - const base::FilePath destination_directory = parent_folder_.path(); - unzip::Unzip(unzip::LaunchUnzipper(), source_file, destination_directory, - base::BindOnce(&ExtractIOTask::ZipExtractCallback, - weak_ptr_factory_.GetWeakPtr())); + const base::FilePath destination_directory = + CreateDestinationName(parent_folder_.path(), source_file); + // Create the directory to extract into. + if (base::CreateDirectory(destination_directory)) { + unzip::Unzip(unzip::LaunchUnzipper(), source_file, + destination_directory, + base::BindOnce(&ExtractIOTask::ZipExtractCallback, + weak_ptr_factory_.GetWeakPtr())); + } // TODO(crbug.com/953256) Report directory creation error. } else { progress_.state = State::kError; // We won't get a callback so reduce the count and maybe finalise.
diff --git a/chrome/browser/ash/file_manager/extract_io_task.h b/chrome/browser/ash/file_manager/extract_io_task.h index 5dc4df5..e2633ca 100644 --- a/chrome/browser/ash/file_manager/extract_io_task.h +++ b/chrome/browser/ash/file_manager/extract_io_task.h
@@ -39,6 +39,11 @@ void ZipExtractCallback(bool success); + // Creates a (non-existing) destination name for creating a directory inside + // 'parent'. + base::FilePath CreateDestinationName(const base::FilePath& parent, + const base::FilePath& source_file); + // URLs of the files that have archives in them for extraction. const std::vector<storage::FileSystemURL> source_urls_; @@ -53,6 +58,9 @@ // Counter of the number of archives needing extraction. size_t extractCount_; + // Maximum directory naming retries when trying to extract. + const int kMaxRetries = 1000; + base::WeakPtrFactory<ExtractIOTask> weak_ptr_factory_{this}; };
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc index fd4f5a9..f2606d2 100644 --- a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc +++ b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
@@ -1664,6 +1664,10 @@ explicit MockGuestOsMountProvider(std::string name) : name_(name) {} std::string DisplayName() override { return name_; } + Profile* profile() override { return nullptr; } + crostini::ContainerId ContainerId() override { + return crostini::ContainerId::GetDefault(); + } private: std::string name_;
diff --git a/chrome/browser/ash/file_manager/path_util.cc b/chrome/browser/ash/file_manager/path_util.cc index 0caa571..5823746 100644 --- a/chrome/browser/ash/file_manager/path_util.cc +++ b/chrome/browser/ash/file_manager/path_util.cc
@@ -30,6 +30,7 @@ #include "chrome/browser/ash/drive/file_system_util.h" #include "chrome/browser/ash/file_manager/app_id.h" #include "chrome/browser/ash/file_manager/fileapi_util.h" +#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/ash/smb_client/smb_service.h" #include "chrome/browser/ash/smb_client/smb_service_factory.h" @@ -343,10 +344,23 @@ "_"); } +std::string GetGuestOsMountPointName(Profile* profile, + crostini::ContainerId id) { + return base::JoinString( + {"guestos", ash::ProfileHelper::GetUserIdHashFromProfile(profile), + net::EscapeAllExceptUnreserved(id.vm_name), + net::EscapeAllExceptUnreserved(id.container_name)}, + "+"); +} + base::FilePath GetCrostiniMountDirectory(Profile* profile) { return base::FilePath("/media/fuse/" + GetCrostiniMountPointName(profile)); } +base::FilePath GetGuestOsMountDirectory(std::string mountPointName) { + return base::FilePath("/media/fuse/" + mountPointName); +} + std::vector<std::string> GetCrostiniMountOptions( const std::string& hostname, const std::string& host_private_key,
diff --git a/chrome/browser/ash/file_manager/path_util.h b/chrome/browser/ash/file_manager/path_util.h index cdb1bec..91e8e77 100644 --- a/chrome/browser/ash/file_manager/path_util.h +++ b/chrome/browser/ash/file_manager/path_util.h
@@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/files/file_path.h" +#include "chrome/browser/ash/crostini/crostini_util.h" #include "storage/browser/file_system/file_system_url.h" class GURL; @@ -100,9 +101,16 @@ // The canonical mount point name for crostini "Linux files" folder. std::string GetCrostiniMountPointName(Profile* profile); +// The canonical mount point name for the Guest OS `id`. +std::string GetGuestOsMountPointName(Profile* profile, + crostini::ContainerId id); + // The actual directory the crostini "Linux files" folder is mounted. base::FilePath GetCrostiniMountDirectory(Profile* profile); +// The actual directory the Guest OS with `mountPointName` is mounted in. +base::FilePath GetGuestOsMountDirectory(std::string mountPointName); + // The sshfs mount options for crostini "Linux files" mount. std::vector<std::string> GetCrostiniMountOptions( const std::string& hostname,
diff --git a/chrome/browser/ash/file_manager/volume_manager.cc b/chrome/browser/ash/file_manager/volume_manager.cc index 5797518a..4adfb567 100644 --- a/chrome/browser/ash/file_manager/volume_manager.cc +++ b/chrome/browser/ash/file_manager/volume_manager.cc
@@ -164,6 +164,8 @@ return "smb"; case VOLUME_TYPE_SYSTEM_INTERNAL: return "system_internal"; + case VOLUME_TYPE_GUEST_OS: + return "guest_os"; case NUM_VOLUME_TYPE: break; } @@ -387,6 +389,25 @@ } // static +std::unique_ptr<Volume> Volume::CreateForSftpGuestOs( + const std::string display_name, + const base::FilePath& sftp_mount_path, + const base::FilePath& remote_mount_path) { + std::unique_ptr<Volume> volume(new Volume()); + volume->type_ = VOLUME_TYPE_GUEST_OS; + volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN; + // Keep source_path empty. + volume->source_ = SOURCE_SYSTEM; + volume->mount_path_ = sftp_mount_path; + volume->remote_mount_path_ = remote_mount_path; + volume->mount_condition_ = ash::disks::MOUNT_CONDITION_NONE; + volume->volume_id_ = GenerateVolumeId(*volume); + volume->volume_label_ = display_name; + volume->watchable_ = false; + return volume; +} + +// static std::unique_ptr<Volume> Volume::CreateForAndroidFiles( const base::FilePath& mount_path) { std::unique_ptr<Volume> volume(new Volume()); @@ -727,6 +748,19 @@ }))); } +void VolumeManager::AddSftpGuestOsVolume( + const std::string display_name, + const base::FilePath& sftp_mount_path, + const base::FilePath& remote_mount_path) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + std::unique_ptr<Volume> volume = Volume::CreateForSftpGuestOs( + display_name, sftp_mount_path, remote_mount_path); + // Ignore if volume already exists. + if (mounted_volumes_.find(volume->volume_id()) != mounted_volumes_.end()) + return; + DoMountEvent(chromeos::MOUNT_ERROR_NONE, std::move(volume)); +} + void VolumeManager::RemoveSshfsCrostiniVolume( const base::FilePath& sshfs_mount_path, RemoveSshfsCrostiniVolumeCallback callback) { @@ -738,6 +772,17 @@ std::move(callback))); } +void VolumeManager::RemoveSftpGuestOsVolume( + const base::FilePath& sftp_mount_path, + RemoveSshfsCrostiniVolumeCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + disk_mount_manager_->UnmountPath( + sftp_mount_path.value(), + base::BindOnce(&VolumeManager::OnSftpGuestOsUnmountCallback, + base::Unretained(this), sftp_mount_path, + std::move(callback))); +} + bool VolumeManager::RegisterAndroidFilesDirectoryForTesting( const base::FilePath& path) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -1519,4 +1564,28 @@ std::move(callback).Run(false); } +void VolumeManager::OnSftpGuestOsUnmountCallback( + const base::FilePath& sftp_mount_path, + RemoveSftpGuestOsVolumeCallback callback, + chromeos::MountError error_code) { + if ((error_code == chromeos::MOUNT_ERROR_NONE) || + (error_code == chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED)) { + // Remove metadata associated with the mount. It will be a no-op if it + // wasn't mounted or unmounted out of band. We need the VolumeId to be + // consistent, which means the mount path needs to be the same. display_name + // and remote_mount_path aren't needed and we don't know them at unmount so + // leave them blank. + DoUnmountEvent( + chromeos::MOUNT_ERROR_NONE, + *Volume::CreateForSftpGuestOs("", sftp_mount_path, base::FilePath())); + if (callback) + std::move(callback).Run(true); + return; + } + + LOG(ERROR) << "Unmounting SFTP path failed with error: " << error_code; + if (callback) + std::move(callback).Run(false); +} + } // namespace file_manager
diff --git a/chrome/browser/ash/file_manager/volume_manager.h b/chrome/browser/ash/file_manager/volume_manager.h index 33f0a13..df6e99c 100644 --- a/chrome/browser/ash/file_manager/volume_manager.h +++ b/chrome/browser/ash/file_manager/volume_manager.h
@@ -67,6 +67,7 @@ VOLUME_TYPE_SMB, VOLUME_TYPE_SYSTEM_INTERNAL, // Internal volume which is never exposed to // users. + VOLUME_TYPE_GUEST_OS, // Guest OS volumes (Crostini, Bruschetta, etc) // The enum values must be kept in sync with FileManagerVolumeType in // tools/metrics/histograms/enums.xml. Since enums for histograms are // append-only (for keeping the number consistent across versions), new values @@ -118,6 +119,10 @@ static std::unique_ptr<Volume> CreateForSshfsCrostini( const base::FilePath& crostini_path, const base::FilePath& remote_mount_path); + static std::unique_ptr<Volume> CreateForSftpGuestOs( + const std::string display_name, + const base::FilePath& sftp_mount_path, + const base::FilePath& remote_mount_path); static std::unique_ptr<Volume> CreateForAndroidFiles( const base::FilePath& mount_path); static std::unique_ptr<Volume> CreateForDocumentsProvider( @@ -300,9 +305,12 @@ const std::string&, device::mojom::MtpManager::GetStorageInfoCallback)>; - // Callback for |RemoveSshfsCrostiniVolume|. + // Callback for `RemoveSshfsCrostiniVolume`. using RemoveSshfsCrostiniVolumeCallback = base::OnceCallback<void(bool)>; + // Callback for `RemoveSftpGuestOsVolume`. + using RemoveSftpGuestOsVolumeCallback = base::OnceCallback<void(bool)>; + VolumeManager( Profile* profile, drive::DriveIntegrationService* drive_integration_service, @@ -344,16 +352,29 @@ // path, is located. Returns nullptr if no volume is found. base::WeakPtr<Volume> FindVolumeFromPath(const base::FilePath& path); - // Add sshfs crostini volume mounted at specified path. + // Add sshfs crostini volume mounted at `sshfs_mount_path` path. Will + // automatically remove the volume on container shutdown. void AddSshfsCrostiniVolume(const base::FilePath& sshfs_mount_path, const base::FilePath& remote_mount_path); - // Removes specified sshfs crostini mount. Runs |callback| with true if the + // Add sftp Guest OS volume mounted at `sftp_mount_path`. Note: volume must be + // removed on unmount (including Guest OS shutdown). + void AddSftpGuestOsVolume(const std::string display_name, + const base::FilePath& sftp_mount_path, + const base::FilePath& remote_mount_path); + + // Removes specified sshfs crostini mount. Runs `callback` with true if the // mount was removed successfully or wasn't mounted to begin with. Runs - // |callback| with false in all other cases. + // `callback` with false in all other cases. void RemoveSshfsCrostiniVolume(const base::FilePath& sshfs_mount_path, RemoveSshfsCrostiniVolumeCallback callback); + // Removes specified sftp Guest OS mount. Runs `callback` with true if the + // mount was removed successfully or wasn't mounted to begin with. Runs + // `callback` with false in all other cases. + void RemoveSftpGuestOsVolume(const base::FilePath& sftp_mount_path, + RemoveSftpGuestOsVolumeCallback callback); + // Removes Downloads volume used for testing. void RemoveDownloadsDirectoryForTesting(); @@ -495,6 +516,10 @@ RemoveSshfsCrostiniVolumeCallback callback, chromeos::MountError error_code); + void OnSftpGuestOsUnmountCallback(const base::FilePath& sftp_mount_path, + RemoveSftpGuestOsVolumeCallback callback, + chromeos::MountError error_code); + Profile* profile_; drive::DriveIntegrationService* drive_integration_service_; // Not owned. ash::disks::DiskMountManager* disk_mount_manager_; // Not owned.
diff --git a/chrome/browser/ash/guest_os/public/guest_os_mount_provider.cc b/chrome/browser/ash/guest_os/public/guest_os_mount_provider.cc new file mode 100644 index 0000000..5d7befc --- /dev/null +++ b/chrome/browser/ash/guest_os/public/guest_os_mount_provider.cc
@@ -0,0 +1,169 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider.h" +#include <memory> + +#include "ash/components/disks/disk_mount_manager.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/callback_helpers.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/stringprintf.h" +#include "chrome/browser/ash/borealis/infra/expected.h" +#include "chrome/browser/ash/crostini/crostini_util.h" +#include "chrome/browser/ash/file_manager/path_util.h" +#include "chrome/browser/ash/file_manager/volume_manager.h" +#include "chrome/browser/ash/guest_os/infra/cached_callback.h" +#include "storage/browser/file_system/external_mount_points.h" + +namespace guest_os { + +// An RAII-style class controlling the lifetime of the SFTP volume. Will add the +// volume on creation and remove it on destruction. +class ScopedVolume { + public: + explicit ScopedVolume( + Profile* profile, + std::string display_name, + std::string mount_label, + base::FilePath homedir, + const ash::disks::DiskMountManager::MountPointInfo& mount_info) + : profile_(profile), mount_label_(mount_label) { + base::FilePath mount_path = base::FilePath(mount_info.mount_path); + if (!storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( + mount_label_, storage::kFileSystemTypeLocal, + storage::FileSystemMountOption(), mount_path)) { + // We don't revoke the filesystem on unmount and this call fails if a + // filesystem of the same name already exists, so ignore errors. + // TODO(crbug/1293229): This follows the logic of existing code, but we + // can probably change it to revoke the filesystem on unmount. + } + + auto* vmgr = file_manager::VolumeManager::Get(profile_); + if (vmgr) { + // vmgr is null in unit tests. + vmgr->AddSftpGuestOsVolume(display_name, mount_path, homedir); + } + } + + ~ScopedVolume() { + if (profile_->ShutdownStarted()) { + // We're shutting down, but because we're not a keyed service we don't get + // two-phase shutdown, we just can't call anything. Either the whole + // system is shutting down (in which case everything gets undone anyway) + // or it's just the browser (in which case it's basically the same as a + // browser crash which we also need to handle). + // So do nothing. + return; + } + + auto* vmgr = file_manager::VolumeManager::Get(profile_); + if (vmgr) { + // vmgr is null in unit tests. Also, this calls disk_manager to unmount + // for us (and we never unregister the filesystem) hence unmount doesn't + // seem symmetric with mount. + vmgr->RemoveSftpGuestOsVolume( + file_manager::util::GetGuestOsMountDirectory(mount_label_), + base::DoNothing()); + } + } + + Profile* profile_; + std::string mount_label_; +}; + +class GuestOsMountProviderInner : public CachedCallback<ScopedVolume, bool> { + public: + explicit GuestOsMountProviderInner(Profile* profile, + std::string display_name, + crostini::ContainerId container_id, + int cid, + int port, + base::FilePath homedir) + : profile_(profile), + display_name_(display_name), + container_id_(container_id), + cid_(cid), + port_(port), + homedir_(homedir) {} + + // Mount. + void Build(RealCallback callback) override { + mount_label_ = + file_manager::util::GetGuestOsMountPointName(profile_, container_id_); + auto* dmgr = ash::disks::DiskMountManager::GetInstance(); + + // Call to sshfs to mount. + std::string source_path = base::StringPrintf("sftp://%d:%d", cid_, port_); + + dmgr->MountPath( + source_path, "", mount_label_, {}, chromeos::MOUNT_TYPE_NETWORK_STORAGE, + chromeos::MOUNT_ACCESS_MODE_READ_WRITE, + base::BindOnce(&GuestOsMountProviderInner::OnMountEvent, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + } + void OnMountEvent( + RealCallback callback, + chromeos::MountError error_code, + const ash::disks::DiskMountManager::MountPointInfo& mount_info) { + if (error_code != chromeos::MountError::MOUNT_ERROR_NONE) { + LOG(ERROR) << "Error mounting Guest OS container: error_code=" + << error_code << ", source_path=" << mount_info.source_path + << ", mount_path=" << mount_info.mount_path + << ", mount_type=" << mount_info.mount_type + << ", mount_condition=" << mount_info.mount_condition; + std::move(callback).Run(Failure(false)); + return; + } + auto scoped_volume = std::make_unique<ScopedVolume>( + profile_, display_name_, mount_label_, homedir_, mount_info); + + // CachedCallback magic keeps the scope alive until we're destroyed or it's + // invalidated. + std::move(callback).Run(RealResult(std::move(scoped_volume))); + } + + Profile* profile_; + std::string display_name_; + crostini::ContainerId container_id_; + std::string mount_label_; + int cid_; + int port_; // vsock port + base::FilePath homedir_; + + // Note: This should remain the last member so it'll be destroyed and + // invalidate its weak pointers before any other members are destroyed. + base::WeakPtrFactory<GuestOsMountProviderInner> weak_ptr_factory_{this}; +}; + +void GuestOsMountProvider::Mount(base::OnceCallback<void(bool)> callback) { + if (!callback_) { + callback_ = std::make_unique<GuestOsMountProviderInner>( + profile(), DisplayName(), ContainerId(), cid(), port(), homedir()); + } + callback_->Get(base::BindOnce( + [](base::OnceCallback<void(bool)> callback, + guest_os::GuestOsMountProviderInner::Result result) { + std::move(callback).Run(!!result); + }, + std::move(callback))); +} + +void GuestOsMountProvider::Unmount() { + callback_->Invalidate(); +} + +GuestOsMountProvider::GuestOsMountProvider() = default; +GuestOsMountProvider::~GuestOsMountProvider() = default; +int GuestOsMountProvider::cid() { + return 0; +} +int GuestOsMountProvider::port() { + return 0; +} +base::FilePath GuestOsMountProvider::homedir() { + return base::FilePath("/home/fake"); +} +} // namespace guest_os
diff --git a/chrome/browser/ash/guest_os/public/guest_os_mount_provider.h b/chrome/browser/ash/guest_os/public/guest_os_mount_provider.h index 9f1e4d9..446a6476 100644 --- a/chrome/browser/ash/guest_os/public/guest_os_mount_provider.h +++ b/chrome/browser/ash/guest_os/public/guest_os_mount_provider.h
@@ -6,15 +6,48 @@ #define CHROME_BROWSER_ASH_GUEST_OS_PUBLIC_GUEST_OS_MOUNT_PROVIDER_H_ #include <string> +#include "base/callback_forward.h" +#include "base/files/file_path.h" +#include "chrome/browser/ash/crostini/crostini_util.h" + +class Profile; namespace guest_os { +class GuestOsMountProviderInner; class GuestOsMountProvider { public: - virtual ~GuestOsMountProvider() = default; + GuestOsMountProvider(); + virtual ~GuestOsMountProvider(); + + GuestOsMountProvider(const GuestOsMountProvider&) = delete; + GuestOsMountProvider& operator=(const GuestOsMountProvider&) = delete; + + // Get the profile for this provider. + virtual Profile* profile() = 0; // The localised name to show in UI elements such as the files app sidebar. virtual std::string DisplayName() = 0; + + virtual crostini::ContainerId ContainerId() = 0; + + // TODO(crbug/1293229): How exactly we perform an SFTP mount is TBD, so these + // are subject to change. For now we put random fake values in so we don't + // need to keep changing subclasses as we figure out the format. Assuming we + // keep a similar format to now we get cid from concierge or cicerone, port + // from garcon and homedir is either hardcoded or from tremplin or garcon. + virtual int cid(); + virtual int port(); + virtual base::FilePath homedir(); + + // Requests the provider to mount its volume. + void Mount(base::OnceCallback<void(bool)> callback); + + // Requests the provider to unmount. + void Unmount(); + + private: + std::unique_ptr<GuestOsMountProviderInner> callback_; }; } // namespace guest_os
diff --git a/chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry_unittest.cc b/chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry_unittest.cc index 450ce7a..34df193 100644 --- a/chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry_unittest.cc +++ b/chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry_unittest.cc
@@ -16,6 +16,10 @@ class MockProvider : public GuestOsMountProvider { std::string DisplayName() override { return "Ptery"; } + Profile* profile() override { return nullptr; } + crostini::ContainerId ContainerId() override { + return crostini::ContainerId::GetDefault(); + } }; class MockObserver : public GuestOsMountProviderRegistry::Observer {
diff --git a/chrome/browser/ash/guest_os/public/guest_os_mount_provider_unittest.cc b/chrome/browser/ash/guest_os/public/guest_os_mount_provider_unittest.cc new file mode 100644 index 0000000..4686d12 --- /dev/null +++ b/chrome/browser/ash/guest_os/public/guest_os_mount_provider_unittest.cc
@@ -0,0 +1,203 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider.h" + +#include <memory> + +#include "ash/components/disks/disk_mount_manager.h" +#include "ash/components/disks/mock_disk_mount_manager.h" +#include "base/base64.h" +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/files/file_path.h" +#include "base/memory/weak_ptr.h" +#include "base/run_loop.h" +#include "base/test/bind.h" +#include "base/test/task_environment.h" +#include "chrome/browser/ash/file_manager/fake_disk_mount_manager.h" +#include "chrome/browser/ash/file_manager/volume_manager.h" +#include "chrome/browser/ash/file_manager/volume_manager_factory.h" +#include "chrome/test/base/testing_profile.h" +#include "chromeos/dbus/cros_disks/cros_disks_client.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "content/public/browser/browser_context.h" +#include "content/public/test/browser_task_environment.h" +#include "storage/browser/file_system/external_mount_points.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::ash::disks::DiskMountManager; +using testing::_; + +namespace { + +// Creates a new VolumeManager for tests. +// By default, VolumeManager KeyedService is null for testing. +std::unique_ptr<KeyedService> BuildVolumeManager( + content::BrowserContext* context) { + return std::make_unique<file_manager::VolumeManager>( + Profile::FromBrowserContext(context), + nullptr /* drive_integration_service */, + nullptr /* power_manager_client */, DiskMountManager::GetInstance(), + nullptr /* file_system_provider_service */, + file_manager::VolumeManager::GetMtpStorageInfoCallback()); +} +} // namespace + +namespace guest_os { + +// TODO(crbug/1293229): This is MockProvider2 because we already have a +// MockProvider and ODR will ruin our day if we have the same name. Should move +// this into a separate file and have both test suites use the same object, +// since both just need a placeholder. +class MockProvider2 : public GuestOsMountProvider { + public: + explicit MockProvider2(Profile* profile, crostini::ContainerId container_id) + : profile_(profile), container_id_(container_id) {} + std::string DisplayName() override { return "Ptery"; } + Profile* profile() override { return profile_; } + Profile* profile_; + crostini::ContainerId ContainerId() override { return container_id_; } + // TODO(crbug/1293229): Make ContainerId generic and in guest_os namespace. + crostini::ContainerId container_id_; +}; + +class GuestOsMountProviderTest : public testing::Test { + public: + GuestOsMountProviderTest() { + profile_ = std::make_unique<TestingProfile>(); + // DiskMountManager::InitializeForTesting takes ownership and works with + // a raw pointer, hence the new with no matching delete. + disk_manager_ = new ash::disks::MockDiskMountManager; + provider_ = std::make_unique<MockProvider2>(profile_.get(), kContainerId); + file_manager::VolumeManagerFactory::GetInstance()->SetTestingFactory( + profile_.get(), base::BindRepeating(&BuildVolumeManager)); + + DiskMountManager::InitializeForTesting(disk_manager_); + volume_manager_ = file_manager::VolumeManagerFactory::Get(profile_.get()); + } + + GuestOsMountProviderTest(const GuestOsMountProviderTest&) = delete; + GuestOsMountProviderTest& operator=(const GuestOsMountProviderTest&) = delete; + + ~GuestOsMountProviderTest() override { + storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem( + kMountName); + // Set an empty factory to shut down our testing factory. + file_manager::VolumeManagerFactory::GetInstance()->SetTestingFactory( + profile_.get(), BrowserContextKeyedServiceFactory::TestingFactory{}); + DiskMountManager::Shutdown(); + provider_.reset(); + profile_.reset(); + } + + protected: + void NotifyMountEvent( + const std::string& source_path, + const std::string& source_format, + const std::string& mount_label, + const std::vector<std::string>& mount_options, + chromeos::MountType type, + chromeos::MountAccessMode access_mode, + ash::disks::DiskMountManager::MountPathCallback callback) { + auto event = DiskMountManager::MountEvent::MOUNTING; + auto code = chromeos::MountError::MOUNT_ERROR_NONE; + auto info = DiskMountManager::MountPointInfo( + "sftp://0:0", "/media/fuse/" + kMountName, + chromeos::MOUNT_TYPE_NETWORK_STORAGE, ash::disks::MOUNT_CONDITION_NONE); + disk_manager_->NotifyMountEvent(event, code, info); + std::move(callback).Run(code, info); + } + + void ExpectMountCalls(int n) { + std::vector<std::string> default_mount_options; + EXPECT_CALL(*disk_manager_, + MountPath("sftp://0:0", "", kMountName, default_mount_options, + chromeos::MOUNT_TYPE_NETWORK_STORAGE, + chromeos::MOUNT_ACCESS_MODE_READ_WRITE, _)) + .Times(n) + .WillRepeatedly( + Invoke(this, &GuestOsMountProviderTest::NotifyMountEvent)); + } + + // guestos_${UserHash}_${base64(kContainerId.ToString())}. Note that UserHash + // is an empty string in these tests. + const crostini::ContainerId kContainerId = + crostini::ContainerId("cow", "ptery/daccy"); + const std::string kMountName = std::string{"guestos++cow+ptery%2Fdaccy"}; + + content::BrowserTaskEnvironment task_environment_; + ash::disks::MockDiskMountManager* disk_manager_; + std::unique_ptr<TestingProfile> profile_; + file_manager::VolumeManager* volume_manager_; + std::unique_ptr<MockProvider2> provider_; +}; + +TEST_F(GuestOsMountProviderTest, MountDiskMountsDisk) { + ExpectMountCalls(1); + bool result = false; + + provider_->Mount( + base::BindLambdaForTesting([&result](bool res) { result = res; })); + task_environment_.RunUntilIdle(); + + EXPECT_TRUE(result); + base::FilePath path; + EXPECT_TRUE( + storage::ExternalMountPoints::GetSystemInstance()->GetRegisteredPath( + kMountName, &path)); + EXPECT_EQ(base::FilePath("/media/fuse/" + kMountName), path); + auto volume = volume_manager_->FindVolumeById("guest_os:" + kMountName); + ASSERT_TRUE(volume); + EXPECT_EQ(volume->type(), file_manager::VOLUME_TYPE_GUEST_OS); + EXPECT_EQ(volume->volume_label(), provider_->DisplayName()); +} + +TEST_F(GuestOsMountProviderTest, MultipleCallsAreQueuedAndOnlyMountOnce) { + ExpectMountCalls(1); + int successes = 0; + provider_->Mount(base::BindLambdaForTesting( + [&successes](bool result) { successes += result; })); + provider_->Mount(base::BindLambdaForTesting( + [&successes](bool result) { successes += result; })); + task_environment_.RunUntilIdle(); + provider_->Mount(base::BindLambdaForTesting( + [&successes](bool result) { successes += result; })); + task_environment_.RunUntilIdle(); + + EXPECT_EQ(successes, 3); + base::FilePath path; + EXPECT_TRUE( + storage::ExternalMountPoints::GetSystemInstance()->GetRegisteredPath( + kMountName, &path)); + EXPECT_EQ(base::FilePath("/media/fuse/" + kMountName), path); +} + +TEST_F(GuestOsMountProviderTest, CanRemountAfterUnmount) { + ExpectMountCalls(2); + EXPECT_CALL(*disk_manager_, UnmountPath) + .WillOnce(testing::Invoke( + [this](const std::string& mount_path, + DiskMountManager::UnmountPathCallback callback) { + EXPECT_EQ(mount_path, "/media/fuse/" + kMountName); + std::move(callback).Run(chromeos::MOUNT_ERROR_NONE); + })); + + provider_->Mount( + base::BindLambdaForTesting([](bool res) { EXPECT_TRUE(res); })); + task_environment_.RunUntilIdle(); + provider_->Unmount(); + task_environment_.RunUntilIdle(); + provider_->Mount( + base::BindLambdaForTesting([](bool res) { EXPECT_TRUE(res); })); + task_environment_.RunUntilIdle(); + + base::FilePath path; + EXPECT_TRUE( + storage::ExternalMountPoints::GetSystemInstance()->GetRegisteredPath( + kMountName, &path)); + EXPECT_EQ(base::FilePath("/media/fuse/" + kMountName), path); +} +} // namespace guest_os
diff --git a/chrome/browser/ash/login/easy_unlock/OWNERS b/chrome/browser/ash/login/easy_unlock/OWNERS index df774b8..2a6e671 100644 --- a/chrome/browser/ash/login/easy_unlock/OWNERS +++ b/chrome/browser/ash/login/easy_unlock/OWNERS
@@ -1,3 +1,3 @@ hansberry@chromium.org -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ash/login/easy_unlock/chrome_proximity_auth_client.cc b/chrome/browser/ash/login/easy_unlock/chrome_proximity_auth_client.cc index 3d3c57a..e3dbfb18 100644 --- a/chrome/browser/ash/login/easy_unlock/chrome_proximity_auth_client.cc +++ b/chrome/browser/ash/login/easy_unlock/chrome_proximity_auth_client.cc
@@ -6,6 +6,7 @@ #include <stdint.h> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "base/logging.h" #include "base/system/sys_info.h" @@ -17,7 +18,6 @@ #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_window.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_service.h" #include "components/version_info/version_info.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_challenge_wrapper.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_challenge_wrapper.cc index bb4739e..89f07b7 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_challenge_wrapper.cc +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_challenge_wrapper.cc
@@ -4,9 +4,9 @@ #include "chrome/browser/ash/login/easy_unlock/easy_unlock_challenge_wrapper.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "third_party/securemessage/proto/securemessage.pb.h" namespace ash {
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_create_keys_operation.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_create_keys_operation.cc index 744d2c01..e1319ea 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_create_keys_operation.cc +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_create_keys_operation.cc
@@ -13,13 +13,13 @@ #include "ash/components/cryptohome/system_salt_getter.h" #include "ash/components/cryptohome/userdataauth_util.h" #include "ash/components/login/auth/key.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/base64url.h" #include "base/bind.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_types.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/easy_unlock/easy_unlock_client.h" #include "chromeos/dbus/userdataauth/userdataauth_client.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_get_keys_operation.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_get_keys_operation.cc index f432fb6..83914c20 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_get_keys_operation.cc +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_get_keys_operation.cc
@@ -9,10 +9,10 @@ #include <vector> #include "ash/components/cryptohome/userdataauth_util.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/logging.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/userdataauth/userdataauth_client.h" #include "components/account_id/account_id.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.cc index 6468bf8..4c76711 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.cc +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/logging.h" #include "base/strings/stringprintf.h" @@ -14,7 +15,6 @@ #include "chrome/browser/ash/login/easy_unlock/easy_unlock_key_names.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager_factory.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/account_id/account_id.h" namespace ash {
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc index 72c3566c..1c97433 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc
@@ -8,6 +8,7 @@ #include <utility> #include "ash/components/login/auth/user_context.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/proximity_auth/proximity_auth_local_state_pref_manager.h" #include "ash/components/proximity_auth/proximity_auth_profile_pref_manager.h" #include "ash/components/proximity_auth/proximity_auth_system.h" @@ -39,7 +40,6 @@ #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/power/power_manager_client.h" #include "components/account_id/account_id.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.h b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.h index 9b96d70..70d0965 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.h +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.h
@@ -9,6 +9,7 @@ #include <set> #include <string> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/proximity_auth/smart_lock_metrics_recorder.h" #include "base/callback_forward.h" #include "base/memory/weak_ptr.h" @@ -19,7 +20,6 @@ #include "chrome/browser/ash/login/easy_unlock/easy_unlock_types.h" #include "chrome/browser/ash/login/easy_unlock/smartlock_feature_usage_metrics.h" #include "chrome/browser/ash/login/easy_unlock/smartlock_state_handler.h" -#include "chromeos/components/multidevice/remote_device_ref.h" // TODO(https://crbug.com/1164001): move to forward declaration #include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h" #include "components/keyed_service/core/keyed_service.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc index b5bf1926..79b63c7 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
@@ -10,6 +10,7 @@ #include <utility> #include "apps/app_lifetime_monitor_factory.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/proximity_auth/proximity_auth_pref_names.h" #include "ash/components/proximity_auth/proximity_auth_profile_pref_manager.h" #include "ash/components/proximity_auth/proximity_auth_system.h" @@ -40,7 +41,6 @@ #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/pref_names.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/gcm_driver/gcm_profile_service.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h index 4fa8721c..5b0328ae 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h
@@ -8,17 +8,17 @@ #include <memory> #include <string> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/proximity_auth/screenlock_bridge.h" #include "ash/services/device_sync/proto/cryptauth_api.pb.h" #include "ash/services/device_sync/public/cpp/device_sync_client.h" #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h" +// TODO(https://crbug.com/1164001): move to forward declaration +#include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h" #include "base/callback.h" #include "base/time/time.h" #include "build/build_config.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -// TODO(https://crbug.com/1164001): move to forward declaration -#include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h" #include "components/prefs/pref_change_registrar.h" namespace base {
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc index b84192d..b6125b9 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
@@ -11,6 +11,8 @@ #include <string> #include <utility> +#include "ash/components/multidevice/beacon_seed.h" +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/proximity_auth/fake_lock_handler.h" #include "ash/components/proximity_auth/screenlock_bridge.h" #include "ash/constants/ash_features.h" @@ -37,8 +39,6 @@ #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" -#include "chromeos/components/multidevice/beacon_seed.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power/power_manager_client.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.cc index 7f79065..ba86f0f 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.cc +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.cc
@@ -9,6 +9,11 @@ #include <memory> #include "ash/components/login/auth/user_context.h" +#include "ash/components/multidevice/logging/logging.h" +#include "ash/components/multidevice/remote_device.h" +#include "ash/components/multidevice/remote_device_cache.h" +#include "ash/components/multidevice/remote_device_ref.h" +#include "ash/components/multidevice/software_feature_state.h" #include "ash/components/proximity_auth/proximity_auth_local_state_pref_manager.h" #include "ash/components/proximity_auth/smart_lock_metrics_recorder.h" #include "ash/constants/ash_features.h" @@ -31,11 +36,6 @@ #include "chrome/browser/ash/login/session/user_session_manager.h" #include "chrome/browser/browser_process.h" #include "chrome/common/pref_names.h" -#include "chromeos/components/multidevice/logging/logging.h" -#include "chromeos/components/multidevice/remote_device.h" -#include "chromeos/components/multidevice/remote_device_cache.h" -#include "chromeos/components/multidevice/remote_device_ref.h" -#include "chromeos/components/multidevice/software_feature_state.h" #include "chromeos/login/login_state/login_state.h" #include "chromeos/tpm/tpm_token_loader.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.h b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.h index 1bd1115..83a7891 100644 --- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.h +++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.h
@@ -9,6 +9,8 @@ #include <memory> #include <string> +// TODO(https://crbug.com/1164001): move to forward declaration +#include "ash/components/multidevice/remote_device_cache.h" #include "ash/components/proximity_auth/screenlock_bridge.h" // TODO(https://crbug.com/1164001): move to forward declaration #include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h" @@ -17,8 +19,6 @@ #include "base/values.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_types.h" -// TODO(https://crbug.com/1164001): move to forward declaration -#include "chromeos/components/multidevice/remote_device_cache.h" namespace proximity_auth { class ProximityAuthLocalStatePrefManager;
diff --git a/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc b/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc index e8a8369..174eff1 100644 --- a/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc +++ b/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
@@ -279,6 +279,8 @@ std::vector<std::string> expected_input_methods; // kPreferredKeyboardLayout is now set to last focused POD. expected_input_methods.push_back(user_input_methods[0]); + // Owner input method. + expected_input_methods.push_back(user_input_methods[2]); // Locale default input methods (the first one also is hardware IM). Append_en_US_InputMethods(&expected_input_methods);
diff --git a/chrome/browser/ash/login/oobe_interactive_ui_test.cc b/chrome/browser/ash/login/oobe_interactive_ui_test.cc index f798e9d..1a7246d5 100644 --- a/chrome/browser/ash/login/oobe_interactive_ui_test.cc +++ b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
@@ -671,7 +671,7 @@ } void PerformStepsBeforeEnrollmentCheck(); - void PerformSessionSignInSteps(); + void PerformSessionSignInSteps(bool is_enterprise_enrolled); void SimpleEndToEnd(); @@ -713,7 +713,8 @@ test::ExitUpdateScreenNoUpdate(); } -void OobeInteractiveUITest::PerformSessionSignInSteps() { +void OobeInteractiveUITest::PerformSessionSignInSteps( + bool is_enterprise_enrolled) { ForceBrandedBuild(); if (GetFirstSigninScreen() == UserCreationView::kScreenId) { test::WaitForUserCreationScreen(); @@ -722,7 +723,9 @@ WaitForGaiaSignInScreen(test_setup()->arc_state() != ArcState::kNotAvailable); LogInAsRegularUser(); - if (chromeos::features::IsOobeConsolidatedConsentEnabled()) { + if (chromeos::features::IsOobeConsolidatedConsentEnabled() && + (!is_enterprise_enrolled || + test_setup()->arc_state() != ArcState::kNotAvailable)) { test::WaitForConsolidatedConsentScreen(); RunConsolidatedConsentScreenChecks(); test::TapConsolidatedConsentAccept(); @@ -766,7 +769,7 @@ void OobeInteractiveUITest::SimpleEndToEnd() { PerformStepsBeforeEnrollmentCheck(); - PerformSessionSignInSteps(); + PerformSessionSignInSteps(false /* is_enterprise_enrolled */); WaitForLoginDisplayHostShutdown(); } @@ -855,7 +858,7 @@ enrollment_ui_.LeaveSuccessScreen(); login_screen_waiter->WaitEvenIfShown(); - PerformSessionSignInSteps(); + PerformSessionSignInSteps(true /* is_enterprise_enrolled */); WaitForLoginDisplayHostShutdown(); }
diff --git a/chrome/browser/ash/login/screens/consolidated_consent_screen.cc b/chrome/browser/ash/login/screens/consolidated_consent_screen.cc index 4035b03..28407c2 100644 --- a/chrome/browser/ash/login/screens/consolidated_consent_screen.cc +++ b/chrome/browser/ash/login/screens/consolidated_consent_screen.cc
@@ -18,10 +18,14 @@ #include "chrome/browser/ash/login/ui/login_display_host.h" #include "chrome/browser/ash/login/wizard_context.h" #include "chrome/browser/ash/login/wizard_controller.h" +#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/ash/settings/device_settings_service.h" +#include "chrome/browser/ash/settings/stats_reporting_controller.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_process_platform_part.h" #include "chrome/browser/consent_auditor/consent_auditor_factory.h" +#include "chrome/browser/metrics/metrics_reporting_state.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" @@ -30,6 +34,7 @@ #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" #include "components/consent_auditor/consent_auditor.h" +#include "components/metrics/metrics_service.h" #include "components/prefs/pref_service.h" #include "components/signin/public/base/consent_level.h" #include "components/signin/public/identity_manager/identity_manager.h" @@ -114,18 +119,14 @@ if (arc::IsArcDemoModeSetupFlow()) return false; - // For managed users, admins are required to accept ToS on the server side. - // So, if the user is managed and no arc negotiation is needed, skip the - // screen. IsManaged() returns true for child users, don't skip consolidated - // consent in that case. - Profile* profile = ProfileManager::GetActiveUserProfile(); - CHECK(profile); - bool is_child_account = - user_manager::UserManager::Get()->IsLoggedInAsChildUser(); - bool is_enterprise_managed = - profile->GetProfilePolicyConnector()->IsManaged() && !is_child_account; - if ((is_enterprise_managed && - !arc::IsArcTermsOfServiceOobeNegotiationNeeded()) || + // For managed devices, admins are required to accept ToS on the server side. + // So, if the device is managed and no arc negotiation is needed, skip the + // screen. + policy::BrowserPolicyConnectorAsh* connector = + g_browser_process->platform_part()->browser_policy_connector_ash(); + bool is_device_managed = connector->IsDeviceEnterpriseManaged(); + + if ((is_device_managed && !arc::IsArcTermsOfServiceOobeNegotiationNeeded()) || !context->is_branded_build) { exit_callback_.Run(Result::NOT_APPLICABLE); return true; @@ -148,24 +149,9 @@ base::BindOnce(&ConsolidatedConsentScreen::OnOwnershipStatusCheckDone, weak_factory_.GetWeakPtr())); - bool is_demo = arc::IsArcDemoModeSetupFlow(); - bool is_arc_enabled = arc::IsArcTermsOfServiceOobeNegotiationNeeded(); - if (!is_demo && is_arc_enabled) { - // Enable ARC to match ArcSessionManager logic. ArcSessionManager expects - // that ARC is enabled (prefs::kArcEnabled = true) on showing Terms of - // Service. If user accepts ToS then prefs::kArcEnabled is left activated. - // If user skips ToS then prefs::kArcEnabled is automatically reset in - // ArcSessionManager. - arc::SetArcPlayStoreEnabledForProfile(profile, true); - - pref_handler_ = std::make_unique<arc::ArcOptInPreferenceHandler>( - this, profile->GetPrefs()); - pref_handler_->Start(); - } - ConsolidatedConsentScreenView::ScreenConfig config; - config.is_arc_enabled = is_arc_enabled; - config.is_demo = is_demo; + config.is_arc_enabled = arc::IsArcTermsOfServiceOobeNegotiationNeeded(); + config.is_demo = arc::IsArcDemoModeSetupFlow(); config.is_enterprise_managed_account = is_enterprise_managed_account_; config.is_child_account = is_child_account_; config.country_code = base::CountryCodeForCurrentTimezone(); @@ -195,8 +181,10 @@ void ConsolidatedConsentScreen::OnMetricsModeChanged(bool enabled, bool managed) { + // When the usage opt-in is not managed, override the enabled value + // with `true` to encourage users to consent with it during OptIn flow. if (view_) - view_->SetUsageMode(enabled, managed); + view_->SetUsageMode(/*enabled=*/!managed || enabled, managed); } void ConsolidatedConsentScreen::OnBackupAndRestoreModeChanged(bool enabled, @@ -215,18 +203,50 @@ void ConsolidatedConsentScreen::OnOwnershipStatusCheckDone( DeviceSettingsService::OwnershipStatus status) { - bool is_owner = false; - // If no ownership is established yet, then the current user is the first // user to sign in. Therefore, the current user would be the owner. - if (status == DeviceSettingsService::OWNERSHIP_NONE) { - is_owner = true; - } else if (status == DeviceSettingsService::OWNERSHIP_TAKEN) { - is_owner = user_manager::UserManager::Get()->IsCurrentUserOwner(); + if (status == DeviceSettingsService::OWNERSHIP_NONE) + is_owner_ = true; + else if (status == DeviceSettingsService::OWNERSHIP_TAKEN) + is_owner_ = user_manager::UserManager::Get()->IsCurrentUserOwner(); + + const bool is_negotiation_needed = + arc::IsArcTermsOfServiceOobeNegotiationNeeded(); + // If the user is not the owner and the owner disabled metrics, the user + // is not allowed to update the usage opt-in. + if (!is_owner_) { + const bool is_metrics_enabled = + ash::StatsReportingController::Get()->IsEnabled(); + + if (!is_negotiation_needed && !is_metrics_enabled) { + exit_callback_.Run(Result::NOT_APPLICABLE); + return; + } + + if (!is_metrics_enabled) { + view_->HideUsageOptin(); + } + } + + const bool is_demo = arc::IsArcDemoModeSetupFlow(); + if (!is_demo && is_negotiation_needed) { + // Enable ARC to match ArcSessionManager logic. ArcSessionManager expects + // that ARC is enabled (prefs::kArcEnabled = true) on showing Terms of + // Service. If user accepts ToS then prefs::kArcEnabled is left activated. + // If user skips ToS then prefs::kArcEnabled is automatically reset in + // ArcSessionManager. + Profile* profile = ProfileManager::GetActiveUserProfile(); + DCHECK(profile); + + arc::SetArcPlayStoreEnabledForProfile(profile, true); + + pref_handler_ = std::make_unique<arc::ArcOptInPreferenceHandler>( + this, profile->GetPrefs()); + pref_handler_->Start(); } if (view_) - view_->SetIsDeviceOwner(is_owner); + view_->SetIsDeviceOwner(is_owner_.value()); } void ConsolidatedConsentScreen::RecordConsents( @@ -290,12 +310,27 @@ } } +void ConsolidatedConsentScreen::ReportUsageOptIn(bool is_enabled) { + DCHECK(is_owner_.has_value()); + if (is_owner_.value()) { + ash::StatsReportingController::Get()->SetEnabled( + ProfileManager::GetActiveUserProfile(), is_enabled); + return; + } + + auto* metrics_service = g_browser_process->metrics_service(); + DCHECK(metrics_service); + + // If user is not eligible for per-user, this will no-op. See details at + // chrome/browser/metrics/per_user_state_manager_chromeos.h. + metrics_service->UpdateCurrentUserMetricsConsent(is_enabled); +} + void ConsolidatedConsentScreen::OnAccept(bool enable_stats_usage, bool enable_backup_restore, bool enable_location_services, const std::string& tos_content) { - // Should be called regardless of ARC. - pref_handler_->EnableMetrics(enable_stats_usage); + ReportUsageOptIn(enable_stats_usage); if (arc::IsArcDemoModeSetupFlow() || !arc::IsArcTermsOfServiceOobeNegotiationNeeded()) {
diff --git a/chrome/browser/ash/login/screens/consolidated_consent_screen.h b/chrome/browser/ash/login/screens/consolidated_consent_screen.h index 61b8102..71f336296 100644 --- a/chrome/browser/ash/login/screens/consolidated_consent_screen.h +++ b/chrome/browser/ash/login/screens/consolidated_consent_screen.h
@@ -107,11 +107,15 @@ void OnOwnershipStatusCheckDone( DeviceSettingsService::OwnershipStatus status); + void ReportUsageOptIn(bool is_enabled); + // Exits the screen with `Result::ACCEPTED` in the normal flow, and // `Result::ACCEPTED_DEMO_ONLINE` or `Result::ACCEPTED_DEMO_OFFLINE` in the // demo setup flow. void ExitScreenWithAcceptedResult(); + absl::optional<bool> is_owner_; + bool is_child_account_ = false; bool is_enterprise_managed_account_ = false;
diff --git a/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc b/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc index 78bd441..346114e 100644 --- a/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc +++ b/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
@@ -9,6 +9,7 @@ #include "base/hash/sha1.h" #include "chrome/browser/ash/arc/session/arc_service_launcher.h" #include "chrome/browser/ash/login/oobe_screen.h" +#include "chrome/browser/ash/login/test/device_state_mixin.h" #include "chrome/browser/ash/login/test/fake_arc_tos_mixin.h" #include "chrome/browser/ash/login/test/fake_eula_mixin.h" #include "chrome/browser/ash/login/test/js_checker.h" @@ -597,14 +598,15 @@ ArcManagedOptin::kManagedDisabled); } -// When both ARC opt ins are managed, skip the screen. -// TODO(crbug.com/1273975): this test should be updated once the per user -// metrics is integrated into consolidated consent. -IN_PROC_BROWSER_TEST_F(ConsolidatedConsentScreenManagedUserTest, Skip) { - SetUpArcEnabledPolicy(); - SetUpManagedOptIns(ArcManagedOptin::kManagedEnabled, - ArcManagedOptin::kManagedEnabled); - LoginManagedUser(); +class ConsolidatedConsentScreenManagedDeviceTest + : public ConsolidatedConsentScreenTest { + private: + DeviceStateMixin device_state_{ + &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED}; +}; + +IN_PROC_BROWSER_TEST_F(ConsolidatedConsentScreenManagedDeviceTest, Skip) { + LoginAsRegularUser(); WaitForScreenExit(); EXPECT_EQ(screen_result_.value(), ConsolidatedConsentScreen::Result::NOT_APPLICABLE);
diff --git a/chrome/browser/ash/login/screens/mock_consolidated_consent_screen.h b/chrome/browser/ash/login/screens/mock_consolidated_consent_screen.h index 3aabb2e..35d1d24 100644 --- a/chrome/browser/ash/login/screens/mock_consolidated_consent_screen.h +++ b/chrome/browser/ash/login/screens/mock_consolidated_consent_screen.h
@@ -40,6 +40,7 @@ MOCK_METHOD(void, SetBackupMode, (bool enabled, bool managed)); MOCK_METHOD(void, SetLocationMode, (bool enabled, bool managed)); MOCK_METHOD(void, SetIsDeviceOwner, (bool is_owner)); + MOCK_METHOD(void, HideUsageOptin, ()); private: ConsolidatedConsentScreen* screen_ = nullptr;
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc index 38076c8..725a4c1f4 100644 --- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc +++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -371,6 +371,15 @@ multi_profile_user_controller_ = std::make_unique<MultiProfileUserController>(this, GetLocalState()); + // |this| is sometimes initialized before owner is ready in CrosSettings for + // the consoldiated consent screen flow. Listen for changes to owner setting + // to ensure that owner changes are reflected in |this|. + // TODO(crbug.com/1307359): Investigate using RetrieveTrustedDevicePolicies + // instead of UpdateOwnerId. + owner_subscription_ = cros_settings_->AddSettingsObserver( + kDeviceOwner, base::BindRepeating(&ChromeUserManagerImpl::UpdateOwnerId, + weak_factory_.GetWeakPtr())); + policy::DeviceLocalAccountPolicyService* device_local_account_policy_service = g_browser_process->platform_part() ->browser_policy_connector_ash() @@ -404,6 +413,16 @@ enterprise_user_session_metrics::RecordStoredSessionLength(); } +void ChromeUserManagerImpl::UpdateOwnerId() { + std::string owner_email; + cros_settings_->GetString(kDeviceOwner, &owner_email); + + user_manager::KnownUser known_user(GetLocalState()); + const AccountId owner_account_id = known_user.GetAccountId( + owner_email, std::string() /* id */, AccountType::UNKNOWN); + SetOwnerId(owner_account_id); +} + ChromeUserManagerImpl::~ChromeUserManagerImpl() { if (g_browser_process->profile_manager()) g_browser_process->profile_manager()->RemoveObserver(this);
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.h b/chrome/browser/ash/login/users/chrome_user_manager_impl.h index 95bbf3a6..9e69e22 100644 --- a/chrome/browser/ash/login/users/chrome_user_manager_impl.h +++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.h
@@ -240,6 +240,8 @@ const AccountId& account_id, const policy::DeviceLocalAccount::Type type) const; + void UpdateOwnerId(); + // Interface to the signed settings store. CrosSettings* cros_settings_; @@ -272,6 +274,7 @@ base::CallbackListSubscription allow_guest_subscription_; base::CallbackListSubscription users_subscription_; base::CallbackListSubscription family_link_accounts_subscription_; + base::CallbackListSubscription owner_subscription_; base::CallbackListSubscription local_accounts_subscription_;
diff --git a/chrome/browser/ash/multidevice_setup/OWNERS b/chrome/browser/ash/multidevice_setup/OWNERS index 7027ab73..18377141 100644 --- a/chrome/browser/ash/multidevice_setup/OWNERS +++ b/chrome/browser/ash/multidevice_setup/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc b/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc index e93de16a..72e3dd5 100644 --- a/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc +++ b/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/multidevice_setup/multidevice_setup_service.h" #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h" #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h" @@ -14,7 +15,6 @@ #include "chrome/browser/ash/device_sync/device_sync_client_factory.h" #include "chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.h" #include "chrome/browser/profiles/profile.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/core/keyed_service.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.cc b/chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.cc index 87e5da9d..fd6f5b4 100644 --- a/chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.cc +++ b/chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/multidevice_setup/multidevice_setup_service.h" #include "ash/services/multidevice_setup/public/cpp/prefs.h" #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" @@ -22,7 +23,6 @@ #include "chrome/browser/ash/multidevice_setup/oobe_completion_tracker_factory.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/core/keyed_service.h" #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ash/note_taking_helper.cc b/chrome/browser/ash/note_taking_helper.cc index eb1857f..ca29872 100644 --- a/chrome/browser/ash/note_taking_helper.cc +++ b/chrome/browser/ash/note_taking_helper.cc
@@ -43,6 +43,7 @@ #include "components/services/app_service/public/cpp/app_registry_cache.h" #include "components/services/app_service/public/cpp/app_types.h" #include "components/services/app_service/public/cpp/app_update.h" +#include "components/services/app_service/public/cpp/intent_filter.h" #include "components/services/app_service/public/cpp/intent_util.h" #include "components/services/app_service/public/cpp/types_util.h" #include "components/services/app_service/public/mojom/types.mojom.h" @@ -140,9 +141,9 @@ return name; } -bool IsNoteTakingIntentFilter(const apps::mojom::IntentFilterPtr& filter) { +bool IsNoteTakingIntentFilter(const apps::IntentFilterPtr& filter) { for (const auto& condition : filter->conditions) { - if (condition->condition_type != apps::mojom::ConditionType::kAction) + if (condition->condition_type != apps::ConditionType::kAction) continue; for (const auto& condition_value : condition->condition_values) { @@ -154,8 +155,8 @@ } bool HasNoteTakingIntentFilter( - const std::vector<apps::mojom::IntentFilterPtr>& filters) { - for (const apps::mojom::IntentFilterPtr& filter : filters) { + const std::vector<apps::IntentFilterPtr>& filters) { + for (const apps::IntentFilterPtr& filter : filters) { if (IsNoteTakingIntentFilter(filter)) return true; }
diff --git a/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.cc b/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.cc index f118d9c..4e8595eb 100644 --- a/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.cc +++ b/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.cc
@@ -4,9 +4,9 @@ #include "chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.h" +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/phonehub/browser_tabs_metadata_fetcher.h" #include "ash/components/phonehub/browser_tabs_model.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "components/sync/base/model_type.h" #include "components/sync/driver/sync_service.h" #include "components/sync_sessions/open_tabs_ui_delegate.h"
diff --git a/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc b/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc index 5d07ade..7bf36da4 100644 --- a/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc +++ b/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc
@@ -8,11 +8,11 @@ #include <utility> #include <vector> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/phonehub/fake_browser_tabs_metadata_fetcher.h" #include "ash/components/phonehub/mutable_phone_model.h" #include "ash/components/phonehub/phone_model_test_util.h" #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/sync/driver/mock_sync_service.h" #include "components/sync_sessions/open_tabs_ui_delegate.h" #include "components/sync_sessions/session_sync_service.h"
diff --git a/chrome/browser/ash/phonehub/camera_roll_download_manager_impl.cc b/chrome/browser/ash/phonehub/camera_roll_download_manager_impl.cc index 17a8e16..8cb3f5e2 100644 --- a/chrome/browser/ash/phonehub/camera_roll_download_manager_impl.cc +++ b/chrome/browser/ash/phonehub/camera_roll_download_manager_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/camera_roll_download_manager.h" #include "ash/components/phonehub/proto/phonehub_api.pb.h" #include "ash/public/cpp/holding_space/holding_space_progress.h" @@ -29,7 +30,6 @@ #include "base/task/thread_pool.h" #include "base/time/time.h" #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash {
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc index 33dcee58..4de41f02 100644 --- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc +++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc
@@ -104,6 +104,7 @@ } DlpBooleanHistogram(dlp::kScreenshotBlockedUMA, IsBlocked(info.restriction_info)); + DlpBooleanHistogram(dlp::kScreenshotWarnedUMA, IsWarn(info.restriction_info)); CheckScreenCaptureRestriction(info, std::move(callback)); } @@ -144,10 +145,15 @@ void DlpContentManagerAsh::CheckStoppedVideoCapture( ash::OnCaptureModeDlpRestrictionChecked callback) { + if (!running_video_capture_info_.has_value()) { + std::move(callback).Run(/*proceed=*/true); + return; + } // If some confidential content was shown during the recording, but not // before, warn the user before saving the file. - if (running_video_capture_info_.has_value() && - !running_video_capture_info_->confidential_contents.IsEmpty()) { + DlpBooleanHistogram(dlp::kScreenshotWarnedUMA, + running_video_capture_info_->had_warning_restriction); + if (!running_video_capture_info_->confidential_contents.IsEmpty()) { const GURL& url = running_video_capture_info_->confidential_contents.GetContents() .begin() @@ -170,6 +176,7 @@ DlpRulesManager::Restriction::kScreenshot, std::move(callback)), running_video_capture_info_->confidential_contents); } else { + DlpBooleanHistogram(dlp::kScreenshotWarnSilentProceededUMA, true); std::move(callback).Run(/*proceed=*/true); } @@ -206,6 +213,8 @@ DlpBooleanHistogram(dlp::kCaptureModeInitBlockedUMA, IsBlocked(info.restriction_info)); + DlpBooleanHistogram(dlp::kCaptureModeInitWarnedUMA, + IsWarn(info.restriction_info)); CheckScreenCaptureRestriction(info, std::move(callback)); } @@ -623,6 +632,7 @@ DlpRulesManager::Restriction::kScreenshot); running_video_capture_info_->confidential_contents.UnionWith( info.confidential_contents); + running_video_capture_info_->had_warning_restriction = true; return; } } @@ -647,6 +657,7 @@ DlpRulesManager::Restriction::kScreenshot); if (info.confidential_contents.IsEmpty()) { // The user already allowed all the visible content. + DlpBooleanHistogram(dlp::kScreenshotWarnSilentProceededUMA, true); std::move(callback).Run(true); return; }
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h index 33e88356..37576bb 100644 --- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h +++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h
@@ -129,6 +129,10 @@ // informs if we already sent a warning proceeded event for the warning // mode. bool was_reported_warning_proceeded = false; + // Flag that indicates that there was some content with warn level + // restriction captured. Used to indicate that the warn UMA should be + // logged, even if no warning is shown. + bool had_warning_restriction = false; }; DlpContentManagerAsh();
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc index e8b94179..811bf6a 100644 --- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc +++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
@@ -272,63 +272,63 @@ ScreenshotArea partial_in = ScreenshotArea::CreateForPartialWindow(root_window, in_rect); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(window, /*expected=*/true); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 0); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 4); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(window, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0, + /*total_count=*/4, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kBlock, 0u); helper_->ChangeConfidentiality(web_contents, kScreenshotRestricted); - CheckScreenshotRestriction(fullscreen, /*expected=*/false); - CheckScreenshotRestriction(window, /*expected=*/false); - CheckScreenshotRestriction(partial_in, /*expected=*/false); - CheckScreenshotRestriction(partial_out, /*expected=*/true); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 3); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 5); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/false); + CheckScreenshotRestriction(window, /*expected_allowed=*/false); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/false); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/3, /*warned_count=*/0, + /*total_count=*/8, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kBlock, 3u); web_contents->WasHidden(); helper_->ChangeVisibility(web_contents); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(window, /*expected=*/false); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 4); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 8); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(window, /*expected_allowed=*/false); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/4, /*warned_count=*/0, + /*total_count=*/12, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kBlock, 4u); web_contents->WasShown(); helper_->ChangeVisibility(web_contents); - CheckScreenshotRestriction(fullscreen, /*expected=*/false); - CheckScreenshotRestriction(window, /*expected=*/false); - CheckScreenshotRestriction(partial_in, /*expected=*/false); - CheckScreenshotRestriction(partial_out, /*expected=*/true); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 7); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 9); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/false); + CheckScreenshotRestriction(window, /*expected_allowed=*/false); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/false); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/7, /*warned_count=*/0, + /*total_count=*/16, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kBlock, 7u); helper_->DestroyWebContents(web_contents); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 7); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 12); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/7, /*warned_count=*/0, + /*total_count=*/19, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kBlock, 7u); } @@ -357,43 +357,63 @@ ScreenshotArea partial_in = ScreenshotArea::CreateForPartialWindow(root_window, in_rect); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(window, /*expected=*/true); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(window, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0, + /*total_count=*/4, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kWarn, 0u); helper_->ChangeConfidentiality(web_contents, kScreenshotWarned); - CheckScreenshotRestriction(fullscreen, /*expected=*/false); - CheckScreenshotRestriction(window, /*expected=*/false); - CheckScreenshotRestriction(partial_in, /*expected=*/false); - CheckScreenshotRestriction(partial_out, /*expected=*/true); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/false); + CheckScreenshotRestriction(window, /*expected_allowed=*/false); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/false); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/3, + /*total_count=*/8, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kWarn, 3u); web_contents->WasHidden(); helper_->ChangeVisibility(web_contents); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(window, /*expected=*/false); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(window, /*expected_allowed=*/false); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/4, + /*total_count=*/12, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kWarn, 4u); web_contents->WasShown(); helper_->ChangeVisibility(web_contents); - CheckScreenshotRestriction(fullscreen, /*expected=*/false); - CheckScreenshotRestriction(window, /*expected=*/false); - CheckScreenshotRestriction(partial_in, /*expected=*/false); - CheckScreenshotRestriction(partial_out, /*expected=*/true); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/false); + CheckScreenshotRestriction(window, /*expected_allowed=*/false); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/false); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/7, + /*total_count=*/16, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kWarn, 7u); helper_->DestroyWebContents(web_contents); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/7, + /*total_count=*/19, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kWarn, 7u); } @@ -419,47 +439,47 @@ ScreenshotArea partial_in = ScreenshotArea::CreateForPartialWindow(root_window, in_rect); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(window, /*expected=*/true); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(window, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kReport, 0u); helper_->ChangeConfidentiality(web_contents, kScreenshotReported); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(window, /*expected=*/true); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(window, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kReport, 0u); web_contents->WasHidden(); helper_->ChangeVisibility(web_contents); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(window, /*expected=*/true); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(window, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kReport, 0u); web_contents->WasShown(); helper_->ChangeVisibility(web_contents); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(window, /*expected=*/true); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(window, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kReport, 0u); helper_->DestroyWebContents(web_contents); - CheckScreenshotRestriction(fullscreen, /*expected=*/true); - CheckScreenshotRestriction(partial_in, /*expected=*/true); - CheckScreenshotRestriction(partial_out, /*expected=*/true); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 0); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 19); + CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true); + CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0, + /*total_count=*/19, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); CheckEvents(DlpRulesManager::Restriction::kScreenshot, DlpRulesManager::Level::kReport, 0u); } @@ -715,12 +735,16 @@ EXPECT_FALSE(helper_->HasAnyContentCached()); capture_mode_delegate->StopObservingRestrictedContent( on_dlp_checked_at_video_end_cb.Get()); + histogram_tester_.ExpectUniqueSample( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnedUMA, true, 1); // Check that the warning is now shown. EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 1); // Hit Enter to "Save anyway". ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_RETURN, /*control=*/false, /*shift=*/false, /*alt=*/false, /*command=*/false)); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, true, 1); EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 0); EXPECT_TRUE(helper_->HasContentCachedForRestriction( web_contents1, DlpRulesManager::Restriction::kScreenshot)); @@ -780,12 +804,16 @@ EXPECT_FALSE(helper_->HasAnyContentCached()); capture_mode_delegate->StopObservingRestrictedContent( on_dlp_checked_at_video_end_cb.Get()); + histogram_tester_.ExpectUniqueSample( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnedUMA, true, 1); // Check that the warning is now shown. EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 1); // Hit Enter to "Cancel". ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_ESCAPE, /*control=*/false, /*shift=*/false, /*alt=*/false, /*command=*/false)); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, false, 1); EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 0); EXPECT_FALSE(helper_->HasAnyContentCached());
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc index 92a933f..cc5de3c 100644 --- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc +++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc
@@ -579,8 +579,6 @@ // The warning should be shown only once. EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(1); - SetReportQueueForReportingManager(); - SetupDlpRulesManager(); EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern(_, _, _)) .Times(3) .WillRepeatedly(::testing::Return(kSrcPattern)); @@ -668,8 +666,6 @@ // If the user cancels, the warning can be shown again for the same contents. EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(2); - SetReportQueueForReportingManager(); - SetupDlpRulesManager(); EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern(_, _, _)) .Times(2) .WillRepeatedly(::testing::Return(kSrcPattern)); @@ -758,10 +754,10 @@ GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); VerifyAndResetActionAllowed(true /*expected*/); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, true, 0); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, false, 1); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0, + /*total_count=*/1, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); // Block restriction is enforced for web_contents: block. helper_.ChangeConfidentiality(web_contents.get(), kScreenshotRestricted); @@ -771,11 +767,10 @@ GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); VerifyAndResetActionAllowed(false /*expected*/); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, true, 1); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, false, 1); - + VerifyHistogramCounts(/*blocked_count=*/1, /*warned_count=*/0, + /*total_count=*/2, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); EXPECT_EQ(events_.size(), 1u); EXPECT_THAT(events_[0], IsDlpPolicyEvent(CreateDlpPolicyEvent( @@ -789,22 +784,20 @@ GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); VerifyAndResetActionAllowed(true /*expected*/); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, true, 1); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, false, 2); + VerifyHistogramCounts(/*blocked_count=*/1, /*warned_count=*/0, + /*total_count=*/3, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); } TEST_F(DlpContentManagerAshCheckRestrictionTest, CaptureModeInitWarnedContinued) { // Set the notifier to "Proceed" on the warning. MockDlpWarnNotifier* mock_dlp_warn_notifier = - CreateAndSetDlpWarnNotifier(true /*should_proceed*/); + CreateAndSetDlpWarnNotifier(/*should_proceed=*/true); // The warning should be shown only once. EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(1); - SetReportQueueForReportingManager(); - SetupDlpRulesManager(); EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern) .Times(1) .WillRepeatedly(::testing::Return(kSrcPattern)); @@ -816,7 +809,11 @@ EXPECT_FALSE(helper_.HasAnyContentCached()); GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0, + /*total_count=*/1, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); + VerifyAndResetActionAllowed(/*expected=*/true); EXPECT_FALSE(helper_.HasAnyContentCached()); EXPECT_TRUE(events_.empty()); @@ -826,7 +823,14 @@ kScreenshotWarned); GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyAndResetActionAllowed(/*expected=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/1, + /*total_count=*/2, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, true, 1); + EXPECT_TRUE(helper_.HasContentCachedForRestriction( web_contents.get(), DlpRulesManager::Restriction::kScreenshot)); EXPECT_EQ(events_.size(), 1u); @@ -838,7 +842,16 @@ // Check again: allow based on cached user's response - no dialog is shown. GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2, + /*total_count=*/3, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); + VerifyAndResetActionAllowed(/*expected=*/true); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, true, 1); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnSilentProceededUMA, true, + 1); EXPECT_TRUE(helper_.HasContentCachedForRestriction( web_contents.get(), DlpRulesManager::Restriction::kScreenshot)); EXPECT_EQ(events_.size(), 1u); @@ -849,7 +862,11 @@ kEmptyRestrictionSet); GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyAndResetActionAllowed(/*expected=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2, + /*total_count=*/4, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); EXPECT_EQ(events_.size(), 1u); } @@ -857,12 +874,10 @@ CaptureModeInitWarnedCancelled) { // Set the notifier to "Proceed" on the warning. MockDlpWarnNotifier* mock_dlp_warn_notifier = - CreateAndSetDlpWarnNotifier(false /*should_proceed*/); + CreateAndSetDlpWarnNotifier(/*should_proceed=*/false); // If the user cancels, the warning can be shown again for the same contents. EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(2); - SetReportQueueForReportingManager(); - SetupDlpRulesManager(); EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern) .Times(2) .WillRepeatedly(::testing::Return(kSrcPattern)); @@ -874,7 +889,11 @@ EXPECT_FALSE(helper_.HasAnyContentCached()); GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0, + /*total_count=*/1, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); + VerifyAndResetActionAllowed(/*expected=*/true); EXPECT_FALSE(helper_.HasAnyContentCached()); EXPECT_TRUE(events_.empty()); @@ -884,7 +903,13 @@ kScreenshotWarned); GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(false /*expected*/); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/1, + /*total_count=*/2, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, false, 1); + VerifyAndResetActionAllowed(/*expected=*/false); EXPECT_FALSE(helper_.HasAnyContentCached()); EXPECT_EQ(events_.size(), 1u); EXPECT_THAT(events_[0], @@ -895,7 +920,13 @@ // Check again: since the user previously cancelled, dialog is shown again. GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(false /*expected*/); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2, + /*total_count=*/3, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, false, 2); + VerifyAndResetActionAllowed(/*expected=*/false); EXPECT_FALSE(helper_.HasAnyContentCached()); EXPECT_EQ(events_.size(), 2u); EXPECT_THAT(events_[1], @@ -909,8 +940,11 @@ kEmptyRestrictionSet); GetManager()->CheckCaptureModeInitRestriction( base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - - VerifyAndResetActionAllowed(true /*expected*/); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2, + /*total_count=*/4, + /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA, + /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA); + VerifyAndResetActionAllowed(/*expected=*/true); EXPECT_EQ(events_.size(), 2u); } @@ -930,11 +964,11 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 0); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 1); + VerifyAndResetActionAllowed(/*expected=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0, + /*total_count=*/1, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); // Block restriction is enforced for web_contents: block. helper_.ChangeConfidentiality(web_contents.get(), kScreenshotRestricted); @@ -944,11 +978,11 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(false /*expected*/); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 1); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 1); + VerifyAndResetActionAllowed(/*expected=*/false); + VerifyHistogramCounts(/*blocked_count=*/1, /*warned_count=*/0, + /*total_count=*/2, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); EXPECT_EQ(events_.size(), 1u); EXPECT_THAT(events_[0], @@ -963,22 +997,20 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 1); - histogram_tester_.ExpectBucketCount( - GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 2); + VerifyAndResetActionAllowed(/*expected=*/true); + VerifyHistogramCounts(/*blocked_count=*/1, /*warned_count=*/0, + /*total_count=*/3, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); } TEST_F(DlpContentManagerAshCheckRestrictionTest, ScreenshotWarnedContinued) { // Set the notifier to "Proceed" on the warning. MockDlpWarnNotifier* mock_dlp_warn_notifier = - CreateAndSetDlpWarnNotifier(true /*should_proceed*/); + CreateAndSetDlpWarnNotifier(/*should_proceed=*/true); // The warning should be shown only once. EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(1); - SetReportQueueForReportingManager(); - SetupDlpRulesManager(); EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern) .Times(1) .WillRepeatedly(::testing::Return(kSrcPattern)); @@ -993,7 +1025,11 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyAndResetActionAllowed(/*expected=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0, + /*total_count=*/1, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); EXPECT_FALSE(helper_.HasAnyContentCached()); EXPECT_TRUE(events_.empty()); @@ -1004,7 +1040,11 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyAndResetActionAllowed(/*expected=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/1, + /*total_count=*/2, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); EXPECT_TRUE(helper_.HasContentCachedForRestriction( web_contents.get(), DlpRulesManager::Restriction::kScreenshot)); EXPECT_EQ(events_.size(), 1u); @@ -1017,7 +1057,13 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyAndResetActionAllowed(/*expected=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2, + /*total_count=*/3, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, true, 1); EXPECT_TRUE(helper_.HasContentCachedForRestriction( web_contents.get(), DlpRulesManager::Restriction::kScreenshot)); EXPECT_EQ(events_.size(), 1u); @@ -1029,19 +1075,26 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyAndResetActionAllowed(/*expected=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2, + /*total_count=*/4, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, true, 1); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnSilentProceededUMA, true, + 1); EXPECT_EQ(events_.size(), 1u); } TEST_F(DlpContentManagerAshCheckRestrictionTest, ScreenshotWarnedCancelled) { // Set the notifier to "Proceed" on the warning. MockDlpWarnNotifier* mock_dlp_warn_notifier = - CreateAndSetDlpWarnNotifier(false /*should_proceed*/); + CreateAndSetDlpWarnNotifier(/*should_proceed=*/false); // If the user cancels, the warning can be shown again for the same contents. EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(2); - SetReportQueueForReportingManager(); - SetupDlpRulesManager(); EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern) .Times(2) .WillRepeatedly(::testing::Return(kSrcPattern)); @@ -1056,7 +1109,11 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyAndResetActionAllowed(/*expected=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0, + /*total_count=*/1, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); EXPECT_FALSE(helper_.HasAnyContentCached()); EXPECT_TRUE(events_.empty()); @@ -1067,7 +1124,13 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(false /*expected*/); + VerifyAndResetActionAllowed(/*expected=*/false); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/1, + /*total_count=*/2, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, false, 1); EXPECT_FALSE(helper_.HasAnyContentCached()); EXPECT_EQ(events_.size(), 1u); EXPECT_THAT(events_[0], @@ -1079,7 +1142,13 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(false /*expected*/); + VerifyAndResetActionAllowed(/*expected=*/false); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2, + /*total_count=*/3, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); + histogram_tester_.ExpectBucketCount( + GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, false, 2); EXPECT_FALSE(helper_.HasAnyContentCached()); EXPECT_EQ(events_.size(), 2u); EXPECT_THAT(events_[1], @@ -1094,7 +1163,11 @@ GetManager()->CheckScreenshotRestriction( area, base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_)); - VerifyAndResetActionAllowed(true /*expected*/); + VerifyAndResetActionAllowed(/*expected=*/true); + VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2, + /*total_count=*/4, + /*blocked_suffix=*/dlp::kScreenshotBlockedUMA, + /*warned_suffix=*/dlp::kScreenshotWarnedUMA); EXPECT_EQ(events_.size(), 2u); }
diff --git a/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc b/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc index d9f4130..8911629 100644 --- a/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc +++ b/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc
@@ -79,7 +79,8 @@ // the invalidation::InvalidationService that is currently being made available. class FakeConsumer : public AffiliatedInvalidationServiceProvider::Consumer { public: - explicit FakeConsumer(AffiliatedInvalidationServiceProviderImpl* provider); + FakeConsumer(AffiliatedInvalidationServiceProviderImpl* provider, + const std::string& invalidation_owner_name); FakeConsumer(const FakeConsumer&) = delete; FakeConsumer& operator=(const FakeConsumer&) = delete; @@ -158,8 +159,9 @@ session_manager::SessionManager session_manager_; }; -FakeConsumer::FakeConsumer(AffiliatedInvalidationServiceProviderImpl* provider) - : provider_(provider) { +FakeConsumer::FakeConsumer(AffiliatedInvalidationServiceProviderImpl* provider, + const std::string& invalidation_owner_name) + : provider_(provider), invalidation_handler_(invalidation_owner_name) { provider_->RegisterConsumer(this); } @@ -386,7 +388,7 @@ NoInvalidationServiceAvailable) { // Register a consumer. Verify that the consumer is not called back // immediately as no connected invalidation service exists yet. - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount()); } @@ -397,7 +399,7 @@ // connects, it is made available to the consumer. TEST_F(AffiliatedInvalidationServiceProviderImplTest, UseDeviceInvalidationService) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Indicate that the device-global invalidation service connected. Verify that // that the consumer is informed about this. @@ -421,7 +423,7 @@ // affiliated user connects, it is made available to the consumer. TEST_F(AffiliatedInvalidationServiceProviderImplTest, UseAffiliatedProfileInvalidationService) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Verify that a device-global invalidation service has been created. EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); @@ -443,7 +445,7 @@ // unaffiliated user connects, it is ignored. TEST_F(AffiliatedInvalidationServiceProviderImplTest, DoNotUseUnaffiliatedProfileInvalidationService) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Verify that a device-global invalidation service has been created. EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); @@ -462,7 +464,7 @@ // consumer instead and the device-global invalidation service is destroyed. TEST_F(AffiliatedInvalidationServiceProviderImplTest, SwitchToAffiliatedProfileInvalidationService) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Indicate that the device-global invalidation service connected. Verify that // that the consumer is informed about this. @@ -479,7 +481,7 @@ // |invalidation::INVALIDATIONS_ENABLED| are treated as disconnected. TEST_F(AffiliatedInvalidationServiceProviderImplTest, FlipInvalidationServiceState) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Create and make |profile_invalidation_service_| enabled. LogInAsAffiliatedUserAndConnectInvalidationService(); @@ -509,7 +511,7 @@ // consumer. TEST_F(AffiliatedInvalidationServiceProviderImplTest, DoNotSwitchToUnaffiliatedProfileInvalidationService) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Indicate that the device-global invalidation service connected. Verify that // that the consumer is informed about this. @@ -530,7 +532,7 @@ // service connects, it is made available to the consumer. TEST_F(AffiliatedInvalidationServiceProviderImplTest, SwitchToDeviceInvalidationService) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Verify that a device-global invalidation service has been created. EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); @@ -560,7 +562,7 @@ // to the second user is made available to the consumer instead. TEST_F(AffiliatedInvalidationServiceProviderImplTest, SwitchBetweenAffiliatedProfileInvalidationServices) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Verify that a device-global invalidation service has been created. EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); @@ -614,7 +616,7 @@ // consumer. Further verifies that when the second consumer also unregisters, // the device-global invalidation service is destroyed. TEST_F(AffiliatedInvalidationServiceProviderImplTest, MultipleConsumers) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Indicate that the device-global invalidation service connected. Verify that // that the consumer is informed about this. @@ -623,7 +625,7 @@ // Register a second consumer. Verify that the consumer is called back // immediately as a connected invalidation service is available. std::unique_ptr<FakeConsumer> second_consumer( - new FakeConsumer(provider_.get())); + new FakeConsumer(provider_.get(), "second_consumer")); EXPECT_EQ(1, second_consumer->GetAndClearInvalidationServiceSetCount()); EXPECT_EQ(device_invalidation_service_, second_consumer->GetInvalidationService()); @@ -650,7 +652,7 @@ // service belonging to a second affiliated user that subsequently connects is // ignored. TEST_F(AffiliatedInvalidationServiceProviderImplTest, NoServiceAfterShutdown) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Verify that a device-global invalidation service has been created. EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); @@ -701,7 +703,7 @@ // This is a regression test for http://crbug.com/455504. TEST_F(AffiliatedInvalidationServiceProviderImplTest, ConnectedDeviceGlobalInvalidationServiceOnShutdown) { - consumer_ = std::make_unique<FakeConsumer>(provider_.get()); + consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer"); // Verify that a device-global invalidation service has been created. EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_reboot_handler.cc b/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_reboot_handler.cc index 82c934c..ee6ba8d 100644 --- a/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_reboot_handler.cc +++ b/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_reboot_handler.cc
@@ -24,7 +24,6 @@ #include "chrome/browser/ash/policy/scheduled_task_handler/scheduled_task_util.h" #include "chrome/browser/ash/policy/scheduled_task_handler/scoped_wake_lock.h" #include "chromeos/dbus/power/power_manager_client.h" -#include "components/session_manager/core/session_manager.h" #include "components/user_manager/user_manager.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -99,8 +98,7 @@ // If the device is on the sign-in screen, skip reboot only if the grace // period is applied. - if (!skip_reboot_ && - session_manager::SessionManager::Get()->IsScreenLocked()) { + if (!skip_reboot_ && !user_manager::UserManager::Get()->IsUserLoggedIn()) { RebootDevice(kRebootDescriptionOnTimerExpired); return; }
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.cc b/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.cc index 0ad0c3b..eb9de50e 100644 --- a/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.cc +++ b/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.cc
@@ -66,8 +66,7 @@ notification_timer_.Stop(); if (dialog_timer_.IsRunning()) dialog_timer_.Stop(); - notification_controller_.CloseRebootNotification(); - notification_controller_.CloseRebootDialog(); + CloseNotifications(); reboot_callback_.Reset(); } @@ -109,4 +108,9 @@ return (reboot_time - GetCurrentTime()); } +void RebootNotificationsScheduler::CloseNotifications() { + notification_controller_.CloseRebootNotification(); + notification_controller_.CloseRebootDialog(); +} + } // namespace policy \ No newline at end of file
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.h b/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.h index cc41fa6..d3e6a97 100644 --- a/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.h +++ b/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.h
@@ -57,6 +57,9 @@ // Returns delay from now until |reboot_time|. base::TimeDelta GetRebootDelay(const base::Time& reboot_time) const; + // Closes the reboot notification and the reboot dialog. + virtual void CloseNotifications(); + // Timers for scheduling notification or dialog displaying. base::WallClockTimer notification_timer_, dialog_timer_; // Controller responsible for creating notifications and dialog.
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/test/device_scheduled_reboot_handler_unittest.cc b/chrome/browser/ash/policy/scheduled_task_handler/test/device_scheduled_reboot_handler_unittest.cc index ee0243ab0..c082c8a 100644 --- a/chrome/browser/ash/policy/scheduled_task_handler/test/device_scheduled_reboot_handler_unittest.cc +++ b/chrome/browser/ash/policy/scheduled_task_handler/test/device_scheduled_reboot_handler_unittest.cc
@@ -24,7 +24,6 @@ #include "chrome/test/base/testing_browser_process.h" #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power/power_manager_client.h" -#include "components/session_manager/core/session_manager.h" #include "components/user_manager/scoped_user_manager.h" #include "services/device/public/cpp/test/test_wake_lock_provider.h" #include "testing/gtest/include/gtest/gtest.h" @@ -96,7 +95,6 @@ std::move(notifications_scheduler)); // Set 0 delay for tests. device_scheduled_reboot_handler_->SetRebootDelayForTest(base::TimeDelta()); - session_manager_.SetSessionState(session_manager::SessionState::ACTIVE); } ~DeviceScheduledRebootHandlerTest() override { @@ -156,6 +154,18 @@ task_environment_.GetMockClock()->Now()); } + void InitWithFeatureFlag(bool enable_force_scheduled_reboots) { + if (enable_force_scheduled_reboots) { + scoped_feature_list_.InitWithFeatures( + /* enabled_features */ {ash::features::kDeviceForceScheduledReboot}, + /* disabled_features */ {}); + return; + } + scoped_feature_list_.InitWithFeatures( + /* enabled_features */ {}, + /* disabled_features */ {ash::features::kDeviceForceScheduledReboot}); + } + base::test::TaskEnvironment task_environment_; ash::MockUserManager* mock_user_manager_; // Not owned. user_manager::ScopedUserManager user_manager_enabler_; @@ -166,14 +176,11 @@ device::TestWakeLockProvider wake_lock_provider_; FakeRebootNotificationsScheduler* notifications_scheduler_; base::test::ScopedFeatureList scoped_feature_list_; - session_manager::SessionManager session_manager_; }; TEST_F(DeviceScheduledRebootHandlerTest, CheckIfDailyRebootIsScheduledForKiosk) { - scoped_feature_list_.InitWithFeatures( - /* enabled_features */ {}, - /* disabled_features */ {ash::features::kDeviceForceScheduledReboot}); + InitWithFeatureFlag(false /* enable_force_scheduled_reboots */); EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp()) .WillRepeatedly(testing::Return(true)); @@ -212,11 +219,9 @@ TEST_F(DeviceScheduledRebootHandlerTest, CheckIfDailyRebootIsScheduledForNonKiosk) { - scoped_feature_list_.InitWithFeatures( - /* enabled_features */ {}, - /* disabled_features */ {ash::features::kDeviceForceScheduledReboot}); - EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp()) - .WillRepeatedly(testing::Return(false)); + InitWithFeatureFlag(false /* enable_force_scheduled_reboots */); + EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn()) + .WillRepeatedly(testing::Return(true)); // Calculate time from one hour from now and set the reboot policy to // happen daily at that time. @@ -261,11 +266,11 @@ TEST_F(DeviceScheduledRebootHandlerTest, CheckIfWeeklyUpdateCheckIsScheduledForKiosk) { - scoped_feature_list_.InitWithFeatures( - /* enabled_features */ {}, - /* disabled_features */ {ash::features::kDeviceForceScheduledReboot}); + InitWithFeatureFlag(false /* enable_force_scheduled_reboots */); EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp()) - .WillOnce(testing::Return(false)); + .WillRepeatedly(testing::Return(false)); + EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn()) + .WillRepeatedly(testing::Return(true)); // Set the first reboot to happen 49 hours from now (i.e. 1 hour from 2 // days from now) and then weekly after. base::TimeDelta delay_from_now = base::Hours(49); @@ -303,11 +308,12 @@ TEST_F(DeviceScheduledRebootHandlerTest, CheckIfMonthlyRebootIsScheduledForKiosk) { - scoped_feature_list_.InitWithFeatures( - /* enabled_features */ {}, - /* disabled_features */ {ash::features::kDeviceForceScheduledReboot}); + InitWithFeatureFlag(false /* enable_force_scheduled_reboots */); EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp()) - .WillOnce(testing::Return(false)); + .WillRepeatedly(testing::Return(false)); + EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn()) + .WillRepeatedly(testing::Return(true)); + // Set the first reboot to happen 1 hour from now. base::TimeDelta delay_from_now = base::Hours(1); auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy( @@ -357,6 +363,11 @@ TEST_F(DeviceScheduledRebootHandlerTest, CheckIfDailyRebootIsScheduledWithExternalDelay) { + // Login user and disable kDeviceScheduledReboot flag. The reboot should not + // occur. + InitWithFeatureFlag(false /* enable_force_scheduled_reboots */); + EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn()) + .WillRepeatedly(testing::Return(true)); device_scheduled_reboot_handler_->SetRebootDelayForTest(kExternalRebootDelay); // Calculate time from one hour from now and set the reboot policy to @@ -396,11 +407,10 @@ } TEST_F(DeviceScheduledRebootHandlerTest, - CheckIfDailyRebootIsScheduledForLockScreen) { - scoped_feature_list_.InitWithFeatures( - /* enabled_features */ {}, - /* disabled_features */ {ash::features::kDeviceForceScheduledReboot}); - session_manager_.SetSessionState(session_manager::SessionState::LOCKED); + CheckIfDailyRebootIsScheduledForLoginScreen) { + InitWithFeatureFlag(false /* enable_force_scheduled_reboots */); + EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn()) + .WillRepeatedly(testing::Return(false)); // Set device uptime to 10 minutes and schedule reboot in 30 minutes. Apply // grace time - reboot should not occur. @@ -443,9 +453,7 @@ } TEST_F(DeviceScheduledRebootHandlerTest, EnableForceRebootFeatureInKiosk) { - scoped_feature_list_.InitWithFeatures( - /* enabled_features */ {ash::features::kDeviceForceScheduledReboot}, - /* disabled_features */ {}); + InitWithFeatureFlag(true /* enable_force_scheduled_reboots */); // Set device uptime to 10 minutes and enable kiosk mode. We don't apply grace // period to kiosks, so reboot should occur. @@ -482,11 +490,9 @@ TEST_F(DeviceScheduledRebootHandlerTest, EnableForceRebootFeatureNonKioskSession) { - scoped_feature_list_.InitWithFeatures( - /* enabled_features */ {ash::features::kDeviceForceScheduledReboot}, - /* disabled_features */ {}); - EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp()) - .WillRepeatedly(testing::Return(false)); + InitWithFeatureFlag(true /* enable_force_scheduled_reboots */); + EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn()) + .WillRepeatedly(testing::Return(true)); // Set device uptime to 10 minutes and schedule reboot in 30 minutes. Apply // grace time - reboot should not occur. @@ -532,11 +538,9 @@ } TEST_F(DeviceScheduledRebootHandlerTest, SimulateNotificationButtonClick) { - scoped_feature_list_.InitWithFeatures( - /* enabled_features */ {ash::features::kDeviceForceScheduledReboot}, - /* disabled_features */ {}); - EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp()) - .WillRepeatedly(testing::Return(false)); + InitWithFeatureFlag(true /* enable_force_scheduled_reboots */); + EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn()) + .WillRepeatedly(testing::Return(true)); /// Schedule reboot to happen in 3 hours. base::TimeDelta delay_from_now = base::Hours(3);
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.cc b/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.cc index 561f33f..7338a578 100644 --- a/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.cc +++ b/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.cc
@@ -48,4 +48,7 @@ const { return uptime_; } + +void FakeRebootNotificationsScheduler::CloseNotifications() {} + } // namespace policy
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.h b/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.h index d4ad5a93..15c7718 100644 --- a/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.h +++ b/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.h
@@ -36,6 +36,8 @@ const base::TimeDelta GetSystemUptime() const override; + void CloseNotifications() override; + int show_dialog_calls_ = 0, show_notification_calls_ = 0; const base::Clock* clock_; // Default uptime for test is 10h.
diff --git a/chrome/browser/ash/printing/oauth2/http_exchange.cc b/chrome/browser/ash/printing/oauth2/http_exchange.cc index 6cca59d..85ecaff 100644 --- a/chrome/browser/ash/printing/oauth2/http_exchange.cc +++ b/chrome/browser/ash/printing/oauth2/http_exchange.cc
@@ -29,7 +29,7 @@ std::string ToString(ContentFormat format) { switch (format) { case ContentFormat::kJson: - return "application/json;charset=UTF-8"; + return "application/json"; case ContentFormat::kXWwwFormUrlencoded: return "application/x-www-form-urlencoded"; default: @@ -115,6 +115,7 @@ resource_request->method = http_method; resource_request->url = url; resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; + resource_request->headers.SetHeader("Accept", "application/json"); net::NetworkTrafficAnnotationTag traffic_annotation = net::CompleteNetworkTrafficAnnotation("printing_oauth2_http_exchange", partial_traffic_annotation, R"(
diff --git a/chrome/browser/ash/printing/oauth2/status_code.h b/chrome/browser/ash/printing/oauth2/status_code.h index 1d500cf..9d3ee87 100644 --- a/chrome/browser/ash/printing/oauth2/status_code.h +++ b/chrome/browser/ash/printing/oauth2/status_code.h
@@ -45,10 +45,11 @@ kUnexpectedError }; -// This type of callback is returned to the caller. When `status` equals -// StatusCode::kOK the access token or authorization URL is returned in -// `data`. In other case `data` contains an error message or an empty string. -using AuthServerSessionCallback = +// This is the standard callback used in oauth2 namespace. When `status` equals +// StatusCode::kOK, `data` may contain an access token or authorization URL. +// When `status` is different than StatusCode::kOK, `data` may contain +// an additional error message. +using StatusCallback = base::OnceCallback<void(StatusCode status, const std::string& data)>; } // namespace oauth2
diff --git a/chrome/browser/ash/printing/oauth2/test_authorization_server.cc b/chrome/browser/ash/printing/oauth2/test_authorization_server.cc new file mode 100644 index 0000000..a5dfa67 --- /dev/null +++ b/chrome/browser/ash/printing/oauth2/test_authorization_server.cc
@@ -0,0 +1,215 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/printing/oauth2/test_authorization_server.h" + +#include <list> +#include <string> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/check.h" +#include "base/containers/flat_map.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/memory/scoped_refptr.h" +#include "base/strings/escape.h" +#include "base/strings/string_split.h" +#include "base/strings/stringprintf.h" +#include "base/test/bind.h" +#include "base/test/task_environment.h" +#include "base/values.h" +#include "chrome/browser/ash/printing/oauth2/status_code.h" +#include "chromeos/printing/uri.h" +#include "net/base/escape.h" +#include "net/http/http_status_code.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "services/network/test/test_url_loader_factory.h" +#include "services/network/test/test_utils.h" +#include "url/gurl.h" + +namespace ash { +namespace printing { +namespace oauth2 { + +namespace { + +// Checks 'value' == 'expected'. Returns empty string when true, otherwise +// returns error message. +std::string ExpectEqual(const std::string& name, + const std::string& value, + const std::string& expected) { + if (value == expected) { + return ""; + } + return base::StringPrintf("Invalid %s: got \"%s\", expected \"%s\"; ", + name.c_str(), value.c_str(), expected.c_str()); +} + +// Returns a list with a single element `value`. +base::Value OneElementArray(const std::string& value) { + base::Value::List arr; + arr.Append(value); + return base::Value(std::move(arr)); +} + +} // namespace + +bool ParseURLParameters(const std::string& params_str, + base::flat_map<std::string, std::string>& results) { + results.clear(); + auto params_vect = base::SplitString(params_str, "&", base::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL); + for (auto& key_val : params_vect) { + auto equal_sign = key_val.find('='); + std::string key; + std::string val; + if (equal_sign < key_val.size()) { + key = key_val.substr(0, equal_sign); + val = key_val.substr(equal_sign + 1); + } else { + key = key_val; + } + key = net::UnescapeBinaryURLComponent(key); + if (key.empty() || results.contains(key)) { + return false; + } + results[key] = net::UnescapeBinaryURLComponent(val); + } + return true; +} + +base::OnceCallback<void(StatusCode, const std::string&)> BindResult( + CallbackResult& target) { + target.status = StatusCode::kUnexpectedError; + target.data.clear(); + auto save_results = [](CallbackResult* target, StatusCode status, + const std::string& data) { + target->status = status; + target->data = data; + }; + return base::BindOnce(save_results, base::Unretained(&target)); +} + +base::flat_map<std::string, base::Value> BuildMetadata( + const std::string& authorization_server_uri, + const std::string& authorization_uri, + const std::string& token_uri, + const std::string& registration_uri, + const std::string& revocation_uri) { + base::flat_map<std::string, base::Value> fields; + fields["issuer"] = base::Value(authorization_server_uri); + fields["authorization_endpoint"] = base::Value(authorization_uri); + fields["token_endpoint"] = base::Value(token_uri); + if (!registration_uri.empty()) { + fields["registration_endpoint"] = base::Value(registration_uri); + } + if (!revocation_uri.empty()) { + fields["revocation_endpoint"] = base::Value(revocation_uri); + } + fields["response_types_supported"] = OneElementArray("code"); + fields["response_modes_supported"] = OneElementArray("query"); + fields["grant_types_supported"] = OneElementArray("authorization_code"); + fields["token_endpoint_auth_methods_supported"] = OneElementArray("none"); + fields["code_challenge_methods_supported"] = OneElementArray("S256"); + return fields; +} + +FakeAuthorizationServer::FakeAuthorizationServer() { + fake_server_.SetInterceptor( + base::BindLambdaForTesting([&](const network::ResourceRequest& request) { + upload_data_.push(network::GetUploadData(request)); + })); +} + +FakeAuthorizationServer::~FakeAuthorizationServer() = default; + +std::string FakeAuthorizationServer::ReceiveGET(const std::string& url) { + std::string payload; + auto msg = GetNextRequest("GET", url, "", payload); + if (!payload.empty()) { + msg += "Unexpected payload: \"" + payload.substr(0, 256) + "\""; + } + return msg; +} + +std::string FakeAuthorizationServer::ReceivePOSTWithJSON( + const std::string& url, + base::flat_map<std::string, base::Value>& out_params) { + std::string payload; + auto msg = GetNextRequest("POST", url, "application/json", payload); + auto content = base::JSONReader::Read(payload); + out_params.clear(); + if (content && content->type() == base::Value::Type::DICTIONARY) { + for (auto elem : content->GetDict()) { + out_params[elem.first] = std::move(elem.second); + } + } else { + msg += "Cannot parse the payload: \"" + payload.substr(0, 256) + "\""; + } + return msg; +} + +std::string FakeAuthorizationServer::ReceivePOSTWithURLParams( + const std::string& url, + base::flat_map<std::string, std::string>& out_params) { + std::string payload; + auto msg = + GetNextRequest("POST", url, "application/x-www-form-urlencoded", payload); + if (!ParseURLParameters(payload, out_params)) { + msg += "Cannot parse the payload: \"" + payload.substr(0, 256) + "\""; + } + return msg; +} + +void FakeAuthorizationServer::ResponseWithJSON( + net::HttpStatusCode status, + const base::flat_map<std::string, base::Value>& params) { + CHECK(current_request_); + std::string response_content; + CHECK(base::JSONWriter::Write(base::Value(params), &response_content)); + network::mojom::URLResponseHeadPtr response_head( + network::CreateURLResponseHead(status)); + response_head->headers->SetHeader("Content-Type", "application/json"); + response_head->headers->SetHeader("Cache-Control", "no-store"); + response_head->headers->SetHeader("Pragma", "no-cache"); + network::URLLoaderCompletionStatus compl_status; + CHECK(fake_server_.SimulateResponseForPendingRequest( + current_request_->request.url, compl_status, std::move(response_head), + response_content)); + current_request_ = nullptr; + task_environment_.RunUntilIdle(); +} + +std::string FakeAuthorizationServer::GetNextRequest( + const std::string& method, + const std::string& url, + const std::string& content_type, + std::string& content) { + CHECK(!current_request_); + task_environment_.RunUntilIdle(); + current_request_ = fake_server_.GetPendingRequest(0); + if (!current_request_) { + return "There is no pending requests"; + } + CHECK(!upload_data_.empty()); + content = std::move(upload_data_.front()); + upload_data_.pop(); + + std::string msg; + msg += ExpectEqual("HTTP method", current_request_->request.method, method); + msg += ExpectEqual("URL", current_request_->request.url.spec(), url); + std::string value; + current_request_->request.headers.GetHeader("Content-Type", &value); + msg += ExpectEqual("header Content-Type", value, content_type); + if (current_request_->request.headers.GetHeader("Accept", &value)) { + msg += ExpectEqual("header Accept", value, "application/json"); + } + return msg; +} + +} // namespace oauth2 +} // namespace printing +} // namespace ash
diff --git a/chrome/browser/ash/printing/oauth2/test_authorization_server.h b/chrome/browser/ash/printing/oauth2/test_authorization_server.h new file mode 100644 index 0000000..c16627f --- /dev/null +++ b/chrome/browser/ash/printing/oauth2/test_authorization_server.h
@@ -0,0 +1,123 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ASH_PRINTING_OAUTH2_TEST_AUTHORIZATION_SERVER_H_ +#define CHROME_BROWSER_ASH_PRINTING_OAUTH2_TEST_AUTHORIZATION_SERVER_H_ + +#include <string> + +#include "base/callback.h" +#include "base/containers/flat_map.h" +#include "base/containers/queue.h" +#include "base/memory/scoped_refptr.h" +#include "base/test/task_environment.h" +#include "base/values.h" +#include "chrome/browser/ash/printing/oauth2/status_code.h" +#include "chromeos/printing/uri.h" +#include "net/http/http_status_code.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" + +namespace network { +class SharedURLLoaderFactory; +} // namespace network + +namespace ash { +namespace printing { +namespace oauth2 { + +// Helper function that parses parameters formatted as URL query. Returns false +// <=> the parsing failed (at least one of the keys is empty or repeated). The +// results are saved to `results` that is cleared at the beginning. +bool ParseURLParameters(const std::string& params_str, + base::flat_map<std::string, std::string>& results); + +// Represents results returned by callback void(StatusCode, const string&). +struct CallbackResult { + StatusCode status = StatusCode::kUnexpectedError; + std::string data; +}; + +// Helper function that returns callback saving its parameters to a given +// structure. +StatusCallback BindResult(CallbackResult& target); + +// Builds metadata returned in the response for Metadata request. +base::flat_map<std::string, base::Value> BuildMetadata( + const std::string& authorization_server_uri, + const std::string& authorization_uri, + const std::string& token_uri, + const std::string& registration_uri = "", + const std::string& revocation_uri = ""); + +// Simulates Authorization Server. It contains base::test::TaskEnvironment that +// can be accessed via TaskEnvironment() method. +class FakeAuthorizationServer { + public: + FakeAuthorizationServer(); + ~FakeAuthorizationServer(); + + base::test::TaskEnvironment& TaskEnvironment() { return task_environment_; } + + // Returns the URLLoaderFactory that must be used to send HTTP requests and + // receive responses. + scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() { + return fake_server_.GetSafeWeakWrapper(); + } + + // Processes all pending tasks and get the next request from the fake server. + // Checks if it is a GET `url` request without a payload. The method returns + // an empty string <=> there is no errors. Otherwise it returns an error + // message. + std::string ReceiveGET(const std::string& url); + + // Processes all pending tasks and get the next request from the fake server. + // Checks if it is a POST `url` request with a JSON payload. The payload is + // parsed and saved to `out_params` that is always overwritten. The method + // returns an empty string <=> there is no errors. Otherwise it returns + // an error message. + std::string ReceivePOSTWithJSON( + const std::string& url, + base::flat_map<std::string, base::Value>& out_params); + + // Processes all pending tasks and get the next request from the fake server. + // Checks if it is a POST `url` request with a payload containing URL-encoded + // parameters. The payload is parsed and saved to `out_params` that is always + // overwritten. The method returns an empty string <=> there is no errors. + // Otherwise it returns an error message. + std::string ReceivePOSTWithURLParams( + const std::string& url, + base::flat_map<std::string, std::string>& out_params); + + // Sends a response to the current pending request with the JSON content given + // in `params` and processes all pending tasks. The call to this method must + // be preceded by a previous call to one of Receive* methods. + void ResponseWithJSON(net::HttpStatusCode status, + const base::flat_map<std::string, base::Value>& params); + + private: + // Processes all pending tasks and get the next request from the fake server. + // Returns an error message if there is no requests or the request doesn't + // match `method`, `url` and `content_type. The payload is saved to `content`. + // The method returns an empty string <=> there is no errors. + std::string GetNextRequest(const std::string& method, + const std::string& url, + const std::string& content_type, + std::string& content); + + // Payloads of the incoming requests are stored here (FIFO). + base::queue<std::string> upload_data_; + + // The pending request that is currently being processed. + network::TestURLLoaderFactory::PendingRequest* current_request_ = nullptr; + + network::TestURLLoaderFactory fake_server_; + base::test::TaskEnvironment task_environment_; +}; + +} // namespace oauth2 +} // namespace printing +} // namespace ash + +#endif // CHROME_BROWSER_ASH_PRINTING_OAUTH2_TEST_AUTHORIZATION_SERVER_H_
diff --git a/chrome/browser/ash/printing/oauth2/test_authorization_server_unittest.cc b/chrome/browser/ash/printing/oauth2/test_authorization_server_unittest.cc new file mode 100644 index 0000000..1b5419e --- /dev/null +++ b/chrome/browser/ash/printing/oauth2/test_authorization_server_unittest.cc
@@ -0,0 +1,158 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/printing/oauth2/test_authorization_server.h" + +#include <memory> +#include <string> +#include <utility> + +#include "base/bind.h" +#include "base/check.h" +#include "base/containers/flat_map.h" +#include "base/json/json_reader.h" +#include "base/values.h" +#include "net/http/http_status_code.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace ash { +namespace printing { +namespace oauth2 { +namespace { + +// Helper function that moves the content of `response_body` to `target`. +void SavePayload(std::string* target, + std::unique_ptr<std::string> response_body) { + CHECK(target); + if (response_body) { + *target = std::move(*response_body); + } else { + target->clear(); + } +} + +// Helper function to retrieve HTTP status from `url_loader`. +// Returns -1 when it is not possible. +int GetHttpStatus(network::SimpleURLLoader* url_loader) { + if (url_loader && url_loader->ResponseInfo() && + url_loader->ResponseInfo()->headers) { + return url_loader->ResponseInfo()->headers->response_code(); + } + return -1; +} + +TEST(PrintingOAuth2TestAuthorizationServerTest, ParseURLParameters) { + base::flat_map<std::string, std::string> results; + results["trash"] = "something"; + EXPECT_TRUE(ParseURLParameters("ala=ma&kota", results)); + EXPECT_EQ(results.size(), 2); + EXPECT_TRUE(results.contains("kota")); + EXPECT_EQ(results["ala"], "ma"); + EXPECT_EQ(results["kota"], ""); +} + +TEST(PrintingOAuth2TestAuthorizationServerTest, BuildMetadata) { + auto data = BuildMetadata("server_uri", "auth", "token", "reg", "rev"); + EXPECT_EQ(data["issuer"].GetString(), "server_uri"); + EXPECT_EQ(data["authorization_endpoint"].GetString(), "auth"); + EXPECT_EQ(data["token_endpoint"].GetString(), "token"); + EXPECT_EQ(data["registration_endpoint"].GetString(), "reg"); + EXPECT_EQ(data["revocation_endpoint"].GetString(), "rev"); + ASSERT_TRUE(data["response_types_supported"].is_list()); + ASSERT_EQ(data["response_types_supported"].GetList().size(), 1); + EXPECT_EQ(data["response_types_supported"].GetList().front().GetString(), + "code"); +} + +TEST(PrintingOAuth2TestAuthorizationServerTest, ReceiveGETAndResponse) { + FakeAuthorizationServer server; + + // Prepare SimpleURLLoader and send the request. + auto resource_request = std::make_unique<network::ResourceRequest>(); + resource_request->method = "GET"; + resource_request->url = GURL("https://abc/def"); + auto url_loader = network::SimpleURLLoader::Create( + std::move(resource_request), TRAFFIC_ANNOTATION_FOR_TESTS); + std::string response_payload; + url_loader->DownloadToString(server.GetURLLoaderFactory().get(), + base::BindOnce(&SavePayload, &response_payload), + 1024); + + // Process and check the request and send the response. + ASSERT_EQ(server.ReceiveGET("https://abc/def"), ""); + base::flat_map<std::string, base::Value> content; + server.ResponseWithJSON(net::HttpStatusCode::HTTP_CREATED, content); + + // Check the response. + EXPECT_EQ(response_payload, "{}"); + EXPECT_EQ(GetHttpStatus(url_loader.get()), + static_cast<int>(net::HttpStatusCode::HTTP_CREATED)); +} + +TEST(PrintingOAuth2TestAuthorizationServerTest, + ReceivePOSTWithJSONAndResponse) { + FakeAuthorizationServer server; + + // Prepare SimpleURLLoader and send the request. + auto resource_request = std::make_unique<network::ResourceRequest>(); + resource_request->method = "POST"; + resource_request->url = GURL("https://abc/def"); + auto url_loader = network::SimpleURLLoader::Create( + std::move(resource_request), TRAFFIC_ANNOTATION_FOR_TESTS); + url_loader->AttachStringForUpload(R"({ "field1": "val1", "field2":"val2"})", + "application/json"); + std::string response_payload; + url_loader->DownloadToString(server.GetURLLoaderFactory().get(), + base::BindOnce(&SavePayload, &response_payload), + 1024); + + // Process and check the request and send the response. + base::flat_map<std::string, base::Value> content; + ASSERT_EQ(server.ReceivePOSTWithJSON("https://abc/def", content), ""); + EXPECT_EQ(content.size(), 2); + EXPECT_EQ(content["field1"].GetString(), "val1"); + EXPECT_EQ(content["field2"].GetString(), "val2"); + content["field3"] = base::Value("val3"); + server.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, content); + + // Check the response. + EXPECT_EQ(GetHttpStatus(url_loader.get()), + static_cast<int>(net::HttpStatusCode::HTTP_OK)); + auto parsed = base::JSONReader::Read(response_payload); + ASSERT_TRUE(parsed); + ASSERT_TRUE(parsed->is_dict()); + EXPECT_EQ(*parsed, base::Value(std::move(content))); +} + +TEST(PrintingOAuth2TestAuthorizationServerTest, + ReceivePOSTWithURLParamsAndError) { + FakeAuthorizationServer server; + + // Prepare SimpleURLLoader and send the request. + auto resource_request = std::make_unique<network::ResourceRequest>(); + resource_request->method = "PUT"; // wrong method + resource_request->url = GURL("https://abc/def"); + auto url_loader = network::SimpleURLLoader::Create( + std::move(resource_request), TRAFFIC_ANNOTATION_FOR_TESTS); + url_loader->AttachStringForUpload("f=v", "application/x-www-form-urlencoded"); + std::string response_payload; + url_loader->DownloadToString(server.GetURLLoaderFactory().get(), + base::BindOnce(&SavePayload, &response_payload), + 1024); + + // Process the request and check the error message. + base::flat_map<std::string, std::string> content; + auto err_msg = server.ReceivePOSTWithURLParams("https://abc/def", content); + EXPECT_EQ(err_msg, "Invalid HTTP method: got \"PUT\", expected \"POST\"; "); +} + +} // namespace +} // namespace oauth2 +} // namespace printing +} // namespace ash
diff --git a/chrome/browser/ash/secure_channel/OWNERS b/chrome/browser/ash/secure_channel/OWNERS index 7027ab73..18377141 100644 --- a/chrome/browser/ash/secure_channel/OWNERS +++ b/chrome/browser/ash/secure_channel/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ash/secure_channel/nearby_connection_broker_impl.cc b/chrome/browser/ash/secure_channel/nearby_connection_broker_impl.cc index 05037b2..8eedfbe 100644 --- a/chrome/browser/ash/secure_channel/nearby_connection_broker_impl.cc +++ b/chrome/browser/ash/secure_channel/nearby_connection_broker_impl.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "ash/components/multidevice/logging/logging.h" #include "ash/constants/ash_features.h" #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h" #include "base/bind.h" @@ -23,7 +24,6 @@ #include "base/time/time.h" #include "chrome/browser/ash/secure_channel/nearby_endpoint_finder.h" #include "chrome/browser/ash/secure_channel/util/histogram_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "mojo/public/cpp/bindings/remote.h" namespace ash {
diff --git a/chrome/browser/ash/secure_channel/nearby_connector_impl.cc b/chrome/browser/ash/secure_channel/nearby_connector_impl.cc index 1cf4499..0d43986 100644 --- a/chrome/browser/ash/secure_channel/nearby_connector_impl.cc +++ b/chrome/browser/ash/secure_channel/nearby_connector_impl.cc
@@ -4,13 +4,13 @@ #include "chrome/browser/ash/secure_channel/nearby_connector_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/nearby/public/cpp/nearby_process_manager.h" #include "ash/services/secure_channel/public/cpp/client/nearby_connector.h" #include "base/bind.h" #include "chrome/browser/ash/secure_channel/nearby_connection_broker_impl.h" #include "chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.h" #include "chrome/browser/ash/secure_channel/util/histogram_util.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/keyed_service/core/keyed_service.h" namespace ash {
diff --git a/chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.cc b/chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.cc index e714575..507ed07 100644 --- a/chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.cc +++ b/chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.cc
@@ -4,12 +4,12 @@ #include "chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/services/secure_channel/public/mojom/nearby_connector.mojom.h" #include "base/base64.h" #include "base/memory/ptr_util.h" #include "base/rand_util.h" #include "chrome/browser/ash/secure_channel/util/histogram_util.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace ash { namespace secure_channel {
diff --git a/chrome/browser/ash/tether/tether_service.cc b/chrome/browser/ash/tether/tether_service.cc index b4a897f..cd142ffc 100644 --- a/chrome/browser/ash/tether/tether_service.cc +++ b/chrome/browser/ash/tether/tether_service.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/tether/tether_service.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/tether/gms_core_notifications_state_tracker_impl.h" #include "ash/components/tether/tether_component.h" #include "ash/components/tether/tether_component_impl.h" @@ -19,7 +20,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ash/network/tether_notification_presenter.h" #include "chrome/common/pref_names.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/device_state.h" #include "chromeos/network/network_connect.h" #include "chromeos/network/network_type_pattern.h"
diff --git a/chrome/browser/ash/tether/tether_service_unittest.cc b/chrome/browser/ash/tether/tether_service_unittest.cc index 1d5cfcaa..814c9ef 100644 --- a/chrome/browser/ash/tether/tether_service_unittest.cc +++ b/chrome/browser/ash/tether/tether_service_unittest.cc
@@ -6,6 +6,8 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" +#include "ash/components/multidevice/software_feature.h" #include "ash/components/tether/fake_notification_presenter.h" #include "ash/components/tether/fake_tether_component.h" #include "ash/components/tether/fake_tether_host_fetcher.h" @@ -39,8 +41,6 @@ #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" -#include "chromeos/components/multidevice/software_feature.h" #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power/power_manager_client.h" #include "chromeos/dbus/power_manager/suspend.pb.h"
diff --git a/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc b/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc index 0bb5d903..23a26cf 100644 --- a/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc +++ b/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc
@@ -7,13 +7,59 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" #include "components/browsing_topics/browsing_topics_service.h" #include "content/public/test/browser_test.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/request_handler_util.h" #include "third_party/blink/public/common/features.h" namespace browsing_topics { -class BrowsingTopicsDisabledBrowserTest : public InProcessBrowserTest { +class BrowsingTopicsBrowserTestBase : public InProcessBrowserTest { + public: + void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); + https_server_.AddDefaultHandlers(GetChromeTestDataDir()); + + content::SetupCrossSiteRedirector(&https_server_); + ASSERT_TRUE(https_server_.Start()); + + content::SetupCrossSiteRedirector(embedded_test_server()); + ASSERT_TRUE(embedded_test_server()->Start()); + } + + std::string InvokeTopicsAPI(const content::ToRenderFrameHost& adapter) { + return EvalJs(adapter, R"( + if (!(document.browsingTopics instanceof Function)) { + 'not a function'; + } else { + document.browsingTopics() + .then(topics => { + let result = "["; + for (const topic of topics) { + result += JSON.stringify(topic, Object.keys(topic).sort()) + ";" + } + result += "]"; + return result; + }) + .catch(error => error.message); + } + )") + .ExtractString(); + } + + content::WebContents* web_contents() { + return browser()->tab_strip_model()->GetActiveWebContents(); + } + + protected: + net::EmbeddedTestServer https_server_{ + net::test_server::EmbeddedTestServer::TYPE_HTTPS}; +}; + +class BrowsingTopicsDisabledBrowserTest : public BrowsingTopicsBrowserTestBase { public: BrowsingTopicsDisabledBrowserTest() { scoped_feature_list_.InitWithFeatures( @@ -31,7 +77,16 @@ BrowsingTopicsServiceFactory::GetForProfile(browser()->profile())); } -class BrowsingTopicsBrowserTest : public InProcessBrowserTest { +IN_PROC_BROWSER_TEST_F(BrowsingTopicsDisabledBrowserTest, NoTopicsAPI) { + GURL main_frame_url = + https_server_.GetURL("a.test", "/browsing_topics/empty_page.html"); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url)); + + EXPECT_EQ("not a function", InvokeTopicsAPI(web_contents())); +} + +class BrowsingTopicsBrowserTest : public BrowsingTopicsBrowserTestBase { public: BrowsingTopicsBrowserTest() { scoped_feature_list_.InitWithFeatures( @@ -72,4 +127,145 @@ EXPECT_TRUE(browsing_topics_service()->GetTopTopicsForDisplay().empty()); } +IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, EmptyPage_TopicsAPI) { + GURL main_frame_url = + https_server_.GetURL("a.test", "/browsing_topics/empty_page.html"); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url)); + + EXPECT_EQ("[]", InvokeTopicsAPI(web_contents())); +} + +IN_PROC_BROWSER_TEST_F( + BrowsingTopicsBrowserTest, + EmptyPage_PermissionsPolicyBrowsingTopicsNone_TopicsAPI) { + GURL main_frame_url = https_server_.GetURL( + "a.test", "/browsing_topics/empty_page_browsing_topics_none.html"); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url)); + + EXPECT_EQ( + "The \"browsing-topics\" Permissions Policy denied the use of " + "document.browsingTopics().", + InvokeTopicsAPI(web_contents())); +} + +IN_PROC_BROWSER_TEST_F( + BrowsingTopicsBrowserTest, + EmptyPage_PermissionsPolicyInterestCohortNone_TopicsAPI) { + GURL main_frame_url = https_server_.GetURL( + "a.test", "/browsing_topics/empty_page_interest_cohort_none.html"); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url)); + + EXPECT_EQ( + "The \"interest-cohort\" Permissions Policy denied the use of " + "document.browsingTopics().", + InvokeTopicsAPI(web_contents())); +} + +IN_PROC_BROWSER_TEST_F( + BrowsingTopicsBrowserTest, + OneIframePage_SubframePermissionsPolicyBrowsingTopicsNone_TopicsAPI) { + GURL main_frame_url = + https_server_.GetURL("a.test", "/browsing_topics/one_iframe_page.html"); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url)); + + GURL subframe_url = https_server_.GetURL( + "a.test", "/browsing_topics/empty_page_browsing_topics_none.html"); + + ASSERT_TRUE(content::NavigateIframeToURL(web_contents(), + /*iframe_id=*/"frame", + subframe_url)); + + EXPECT_EQ("[]", InvokeTopicsAPI(web_contents())); + + EXPECT_EQ( + "The \"browsing-topics\" Permissions Policy denied the use of " + "document.browsingTopics().", + InvokeTopicsAPI( + content::ChildFrameAt(web_contents()->GetMainFrame(), 0))); +} + +IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, + PermissionsPolicyAllowCertainOrigin_TopicsAPI) { + base::StringPairs port_replacement; + port_replacement.push_back( + std::make_pair("{{PORT}}", base::NumberToString(https_server_.port()))); + + GURL main_frame_url = https_server_.GetURL( + "a.test", net::test_server::GetFilePathWithReplacements( + "/browsing_topics/" + "one_iframe_page_browsing_topics_allow_certain_origin.html", + port_replacement)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url)); + + EXPECT_EQ("[]", InvokeTopicsAPI(web_contents())); + + GURL subframe_url = + https_server_.GetURL("c.test", "/browsing_topics/empty_page.html"); + + ASSERT_TRUE(content::NavigateIframeToURL(web_contents(), + /*iframe_id=*/"frame", + subframe_url)); + EXPECT_EQ("[]", InvokeTopicsAPI(content::ChildFrameAt( + web_contents()->GetMainFrame(), 0))); + + subframe_url = + https_server_.GetURL("b.test", "/browsing_topics/empty_page.html"); + + ASSERT_TRUE(content::NavigateIframeToURL(web_contents(), + /*iframe_id=*/"frame", + subframe_url)); + + EXPECT_EQ( + "The \"browsing-topics\" Permissions Policy denied the use of " + "document.browsingTopics().", + InvokeTopicsAPI( + content::ChildFrameAt(web_contents()->GetMainFrame(), 0))); +} + +IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, + TopicsAPINotAllowedInInsecureContext) { + GURL main_frame_url = embedded_test_server()->GetURL( + "a.test", "/browsing_topics/one_iframe_page.html"); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url)); + + // Navigate the iframe to a https site. + GURL subframe_url = https_server_.GetURL("b.test", "/empty_page.html"); + content::NavigateIframeToURL(web_contents(), + /*iframe_id=*/"frame", subframe_url); + + // Both the main frame and the subframe are insecure context because the main + // frame is loaded over HTTP. Expect that the API isn't available in either + // frame. + EXPECT_EQ("not a function", InvokeTopicsAPI(web_contents())); + EXPECT_EQ("not a function", InvokeTopicsAPI(content::ChildFrameAt( + web_contents()->GetMainFrame(), 0))); +} + +IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, + TopicsAPINotAllowedInDetachedDocument) { + GURL main_frame_url = + https_server_.GetURL("a.test", "/browsing_topics/one_iframe_page.html"); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url)); + + EXPECT_EQ( + "Failed to execute 'browsingTopics' on 'Document': A browsing " + "context is required when calling document.browsingTopics().", + EvalJs(web_contents(), R"( + const iframe = document.getElementById('frame'); + const childDocument = iframe.contentWindow.document; + iframe.remove(); + + childDocument.browsingTopics() + .then(topics => "success") + .catch(error => error.message); + )")); +} + } // namespace browsing_topics
diff --git a/chrome/browser/cart/cart_discount_metric_collector.cc b/chrome/browser/cart/cart_discount_metric_collector.cc index 60d178d..650bf88 100644 --- a/chrome/browser/cart/cart_discount_metric_collector.cc +++ b/chrome/browser/cart/cart_discount_metric_collector.cc
@@ -48,58 +48,58 @@ } void CartDiscountMetricCollector::RecordDiscountConsentStatusAcceptedIn( - ntp_features::DiscountConsentNtpVariation variation) { + commerce::DiscountConsentNtpVariation variation) { base::UmaHistogramEnumeration( "NewTabPage.Carts.DiscountConsentStatusAtLoad.AcceptedIn", variation); } void CartDiscountMetricCollector::RecordDiscountConsentStatusRejectedIn( - ntp_features::DiscountConsentNtpVariation variation) { + commerce::DiscountConsentNtpVariation variation) { base::UmaHistogramEnumeration( "NewTabPage.Carts.DiscountConsentStatusAtLoad.RejectedIn", variation); } void CartDiscountMetricCollector:: RecordDiscountConsentStatusNoShowAfterDecidedIn( - ntp_features::DiscountConsentNtpVariation variation) { + commerce::DiscountConsentNtpVariation variation) { base::UmaHistogramEnumeration( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized", variation); } void CartDiscountMetricCollector::RecordDiscountConsentStatusDismissedIn( - ntp_features::DiscountConsentNtpVariation variation) { + commerce::DiscountConsentNtpVariation variation) { base::UmaHistogramEnumeration( "NewTabPage.Carts.DiscountConsentStatusAtLoad.DismissedIn", variation); } void CartDiscountMetricCollector::RecordDiscountConsentStatusShowInterestIn( - ntp_features::DiscountConsentNtpVariation variation) { + commerce::DiscountConsentNtpVariation variation) { base::UmaHistogramEnumeration( "NewTabPage.Carts.DiscountConsentStatusAtLoad.InterestedButNoActionIn", variation); } void CartDiscountMetricCollector::RecordDiscountConsentStatusNeverShowIn( - ntp_features::DiscountConsentNtpVariation variation) { + commerce::DiscountConsentNtpVariation variation) { base::UmaHistogramEnumeration( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NeverShownIn", variation); } void CartDiscountMetricCollector::RecordDiscountConsentStatusNoShowIn( - ntp_features::DiscountConsentNtpVariation variation) { + commerce::DiscountConsentNtpVariation variation) { base::UmaHistogramEnumeration( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowIn", variation); } void CartDiscountMetricCollector::RecordDiscountConsentStatusIgnoredIn( - ntp_features::DiscountConsentNtpVariation variation) { + commerce::DiscountConsentNtpVariation variation) { base::UmaHistogramEnumeration( "NewTabPage.Carts.DiscountConsentStatusAtLoad.IgnoredIn", variation); } void CartDiscountMetricCollector::RecordDiscountConsentStatusShownIn( - ntp_features::DiscountConsentNtpVariation variation) { + commerce::DiscountConsentNtpVariation variation) { base::UmaHistogramEnumeration( "NewTabPage.Carts.DiscountConsentStatusAtLoad.ShownIn", variation); }
diff --git a/chrome/browser/cart/cart_discount_metric_collector.h b/chrome/browser/cart/cart_discount_metric_collector.h index 3c5b43e..7c9e467 100644 --- a/chrome/browser/cart/cart_discount_metric_collector.h +++ b/chrome/browser/cart/cart_discount_metric_collector.h
@@ -5,7 +5,7 @@ #ifndef CHROME_BROWSER_CART_CART_DISCOUNT_METRIC_COLLECTOR_H_ #define CHROME_BROWSER_CART_CART_DISCOUNT_METRIC_COLLECTOR_H_ -#include "components/search/ntp_features.h" +#include "components/commerce/core/commerce_feature_list.h" // This is used to collect metric related to the Cart Discount. // These values are persisted to logs. Entries should not be renumbered and @@ -47,23 +47,23 @@ // The following get called when cart module shows to record histogram for // detail discount consent status. static void RecordDiscountConsentStatusAcceptedIn( - ntp_features::DiscountConsentNtpVariation variation); + commerce::DiscountConsentNtpVariation variation); static void RecordDiscountConsentStatusRejectedIn( - ntp_features::DiscountConsentNtpVariation variation); + commerce::DiscountConsentNtpVariation variation); static void RecordDiscountConsentStatusNoShowAfterDecidedIn( - ntp_features::DiscountConsentNtpVariation variation); + commerce::DiscountConsentNtpVariation variation); static void RecordDiscountConsentStatusDismissedIn( - ntp_features::DiscountConsentNtpVariation variation); + commerce::DiscountConsentNtpVariation variation); static void RecordDiscountConsentStatusShowInterestIn( - ntp_features::DiscountConsentNtpVariation variation); + commerce::DiscountConsentNtpVariation variation); static void RecordDiscountConsentStatusNeverShowIn( - ntp_features::DiscountConsentNtpVariation variation); + commerce::DiscountConsentNtpVariation variation); static void RecordDiscountConsentStatusNoShowIn( - ntp_features::DiscountConsentNtpVariation variation); + commerce::DiscountConsentNtpVariation variation); static void RecordDiscountConsentStatusIgnoredIn( - ntp_features::DiscountConsentNtpVariation variation); + commerce::DiscountConsentNtpVariation variation); static void RecordDiscountConsentStatusShownIn( - ntp_features::DiscountConsentNtpVariation variation); + commerce::DiscountConsentNtpVariation variation); }; #endif // CHROME_BROWSER_CART_CART_DISCOUNT_METRIC_COLLECTOR_H_
diff --git a/chrome/browser/cart/cart_features.cc b/chrome/browser/cart/cart_features.cc deleted file mode 100644 index fd40c95..0000000 --- a/chrome/browser/cart/cart_features.cc +++ /dev/null
@@ -1,55 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/cart/cart_features.h" - -#include "base/no_destructor.h" -#include "components/commerce/core/commerce_feature_list.h" -#include "third_party/re2/src/re2/re2.h" - -namespace cart_features { - -namespace { - -constexpr base::FeatureParam<std::string> kPartnerMerchantPattern{ - &ntp_features::kNtpChromeCartModule, "partner-merchant-pattern", - // This regex does not match anything. - "\\b\\B"}; - -const re2::RE2& GetPartnerMerchantPattern() { - re2::RE2::Options options; - options.set_case_sensitive(false); - static base::NoDestructor<re2::RE2> instance(kPartnerMerchantPattern.Get(), - options); - return *instance; -} - -} // namespace - -bool IsRuleDiscountPartnerMerchant(const GURL& url) { - const std::string& url_string = url.spec(); - return RE2::PartialMatch( - re2::StringPiece(url_string.data(), url_string.size()), - GetPartnerMerchantPattern()); -} - -bool IsPartnerMerchant(const GURL& url) { - return commerce::IsCouponDiscountPartnerMerchant(url) || - IsRuleDiscountPartnerMerchant(url); -} - -bool IsFakeDataEnabled() { - return base::GetFieldTrialParamValueByFeature( - ntp_features::kNtpChromeCartModule, - ntp_features::kNtpChromeCartModuleDataParam) == "fake"; -} - -bool IsCartDiscountFeatureEnabled() { - return base::GetFieldTrialParamValueByFeature( - ntp_features::kNtpChromeCartModule, - ntp_features::kNtpChromeCartModuleAbandonedCartDiscountParam) == - "true"; -} - -} // namespace cart_features
diff --git a/chrome/browser/cart/cart_features.h b/chrome/browser/cart/cart_features.h deleted file mode 100644 index e54d57ba..0000000 --- a/chrome/browser/cart/cart_features.h +++ /dev/null
@@ -1,35 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_CART_CART_FEATURES_H_ -#define CHROME_BROWSER_CART_CART_FEATURES_H_ - -#include "base/feature_list.h" -#include "base/metrics/field_trial_params.h" -#include "components/search/ntp_features.h" -#include "url/gurl.h" - -namespace cart_features { -// Default value is 6 hours. -constexpr base::FeatureParam<base::TimeDelta> kDiscountFetchDelayParam( - &ntp_features::kNtpChromeCartModule, - "discount-fetch-delay", - base::Hours(6)); - -// Check if a URL belongs to a partner merchant of rule discount. -bool IsRuleDiscountPartnerMerchant(const GURL& url); - -// Check if a URL belongs to a partner merchant of any discount types. -// TODO(crbug.com/1253633): Move this method to commerce_feature_list after -// modularizing CartService-related components. -bool IsPartnerMerchant(const GURL& url); - -// Check if the variation with fake data is enabled. -bool IsFakeDataEnabled(); - -// Check if cart discount feature is enabled. -bool IsCartDiscountFeatureEnabled(); -} // namespace cart_features - -#endif // CHROME_BROWSER_CART_CART_FEATURES_H_
diff --git a/chrome/browser/cart/cart_handler.cc b/chrome/browser/cart/cart_handler.cc index b67cbd34..12b4c047 100644 --- a/chrome/browser/cart/cart_handler.cc +++ b/chrome/browser/cart/cart_handler.cc
@@ -7,9 +7,9 @@ #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_functions.h" #include "chrome/browser/cart/cart_db_content.pb.h" -#include "chrome/browser/cart/cart_features.h" #include "chrome/browser/cart/cart_service.h" #include "chrome/browser/cart/cart_service_factory.h" +#include "components/commerce/core/commerce_feature_list.h" #include "components/search/ntp_features.h" CartHandler::CartHandler( @@ -71,7 +71,7 @@ auto cart = chrome_cart::mojom::MerchantCart::New(); cart->merchant = std::move(proto_pair.second.merchant()); - if (cart_features::IsRuleDiscountPartnerMerchant( + if (commerce::IsRuleDiscountPartnerMerchant( GURL(proto_pair.second.merchant_cart_url()))) { cart->cart_url = CartService::AppendUTM( GURL(std::move(proto_pair.second.merchant_cart_url())),
diff --git a/chrome/browser/cart/cart_service.cc b/chrome/browser/cart/cart_service.cc index 261c46b..1545467 100644 --- a/chrome/browser/cart/cart_service.cc +++ b/chrome/browser/cart/cart_service.cc
@@ -11,7 +11,6 @@ #include "base/task/thread_pool.h" #include "chrome/browser/cart/cart_db_content.pb.h" #include "chrome/browser/cart/cart_discount_metric_collector.h" -#include "chrome/browser/cart/cart_features.h" #include "chrome/browser/commerce/coupons/coupon_service_factory.h" #include "chrome/browser/history/history_service_factory.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h" @@ -63,9 +62,8 @@ std::string GetKeyForURL(const GURL& url) { std::string domain = eTLDPlusOne(url); - return cart_features::IsFakeDataEnabled() - ? std::string(kFakeDataPrefix) + domain - : domain; + return commerce::IsFakeDataEnabled() ? std::string(kFakeDataPrefix) + domain + : domain; } bool CompareTimeStampForProtoPair(const CartDB::KeyAndValue pair1, @@ -129,7 +127,7 @@ history_service_observation_.Observe(HistoryServiceFactory::GetForProfile( profile_, ServiceAccessType::EXPLICIT_ACCESS)); coupon_service_->MaybeFeatureStatusChanged(IsCartAndDiscountEnabled()); - if (cart_features::IsFakeDataEnabled()) { + if (commerce::IsFakeDataEnabled()) { AddCartsWithFakeData(); } else { // In case last deconstruction is interrupted and fake data is not deleted. @@ -176,7 +174,7 @@ GURL CartService::AppendUTM(const GURL& base_url, bool is_discount_enabled) { DCHECK(base_url.is_valid() && - cart_features::IsRuleDiscountPartnerMerchant(base_url)); + commerce::IsRuleDiscountPartnerMerchant(base_url)); if (kRbdUtmParam.Get()) { return net::AppendOrReplaceQueryParameter( @@ -280,22 +278,22 @@ } void CartService::AcknowledgeDiscountConsent(bool should_enable) { - if (cart_features::IsFakeDataEnabled()) { + if (commerce::IsFakeDataEnabled()) { return; } profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountAcknowledged, true); profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, should_enable); profile_->GetPrefs()->SetInteger( prefs::kDiscountConsentDecisionMadeIn, - ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation.Get()); + commerce::kNtpChromeCartModuleDiscountConsentNtpVariation.Get()); - if (should_enable && cart_features::IsCartDiscountFeatureEnabled()) { + if (should_enable && commerce::IsCartDiscountFeatureEnabled()) { StartGettingDiscount(); } } void CartService::DismissedDiscountConsent() { - if (cart_features::IsFakeDataEnabled()) { + if (commerce::IsFakeDataEnabled()) { return; } profile_->GetPrefs()->SetTime(prefs::kDiscountConsentLastDimissedTime, @@ -306,23 +304,23 @@ past_dimissed_count + 1); profile_->GetPrefs()->SetInteger( prefs::kDiscountConsentDismissedIn, - ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation.Get()); + commerce::kNtpChromeCartModuleDiscountConsentNtpVariation.Get()); } void CartService::InterestedInDiscountConsent() { - if (cart_features::IsFakeDataEnabled()) { + if (commerce::IsFakeDataEnabled()) { return; } profile_->GetPrefs()->SetBoolean(prefs::kDiscountConsentShowInterest, true); profile_->GetPrefs()->SetInteger( prefs::kDiscountConsentShowInterestIn, - ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation.Get()); + commerce::kNtpChromeCartModuleDiscountConsentNtpVariation.Get()); } void CartService::ShouldShowDiscountConsent( base::OnceCallback<void(bool)> callback) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - if (cart_features::IsFakeDataEnabled()) { + if (commerce::IsFakeDataEnabled()) { content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING}) ->PostTask(FROM_HERE, base::BindOnce( [](base::OnceCallback<void(bool)> callback) { @@ -343,8 +341,8 @@ ? CartDiscountMetricCollector::DiscountConsentStatus::ACCEPTED : CartDiscountMetricCollector::DiscountConsentStatus::DECLINED); - ntp_features::DiscountConsentNtpVariation decision_made_in_variation = - static_cast<ntp_features::DiscountConsentNtpVariation>( + commerce::DiscountConsentNtpVariation decision_made_in_variation = + static_cast<commerce::DiscountConsentNtpVariation>( profile_->GetPrefs()->GetInteger( prefs::kDiscountConsentDecisionMadeIn)); if (profile_->GetPrefs()->GetBoolean(prefs::kCartDiscountEnabled)) { @@ -361,26 +359,25 @@ if (profile_->GetPrefs()->GetInteger( prefs::kDiscountConsentPastDismissedCount) > 0) { CartDiscountMetricCollector::RecordDiscountConsentStatusDismissedIn( - static_cast<ntp_features::DiscountConsentNtpVariation>( + static_cast<commerce::DiscountConsentNtpVariation>( profile_->GetPrefs()->GetInteger( prefs::kDiscountConsentDismissedIn))); } if (profile_->GetPrefs()->GetBoolean(prefs::kDiscountConsentShowInterest)) { CartDiscountMetricCollector::RecordDiscountConsentStatusShowInterestIn( - static_cast<ntp_features::DiscountConsentNtpVariation>( + static_cast<commerce::DiscountConsentNtpVariation>( profile_->GetPrefs()->GetInteger( prefs::kDiscountConsentShowInterestIn))); } - ntp_features::DiscountConsentNtpVariation last_shown_in_variation = - static_cast<ntp_features::DiscountConsentNtpVariation>( + commerce::DiscountConsentNtpVariation last_shown_in_variation = + static_cast<commerce::DiscountConsentNtpVariation>( profile_->GetPrefs()->GetInteger( prefs::kDiscountConsentLastShownInVariation)); - ntp_features::DiscountConsentNtpVariation current_variation = - static_cast<ntp_features::DiscountConsentNtpVariation>( - ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation - .Get()); + commerce::DiscountConsentNtpVariation current_variation = + static_cast<commerce::DiscountConsentNtpVariation>( + commerce::kNtpChromeCartModuleDiscountConsentNtpVariation.Get()); if (!should_show_consent) { CartDiscountMetricCollector::RecordDiscountConsentStatus( profile_->GetPrefs()->GetBoolean(prefs::kCartDiscountConsentShown) @@ -418,7 +415,7 @@ bool success, std::vector<CartDB::KeyAndValue> proto_pairs) { if (proto_pairs.size() == 0 || ShouldShowWelcomeSurface() || - !cart_features::IsCartDiscountFeatureEnabled()) { + !commerce::IsCartDiscountFeatureEnabled()) { std::move(callback).Run(false); return; } @@ -428,7 +425,7 @@ // Only show discount consent when there is abandoned cart(s) from partner // merchants. for (auto proto_pair : proto_pairs) { - should_show |= cart_features::IsPartnerMerchant( + should_show |= commerce::IsPartnerMerchant( GURL(proto_pair.second.merchant_cart_url())); } @@ -436,7 +433,7 @@ base::Time last_dismissed_time = profile_->GetPrefs()->GetTime( prefs::kDiscountConsentLastDimissedTime); base::TimeDelta reshow_time_delta = - ntp_features::kNtpChromeCartModuleDiscountConsentReshowTime.Get() - + commerce::kNtpChromeCartModuleDiscountConsentReshowTime.Get() - (base::Time::Now() - last_dismissed_time); int last_dismissed_count = profile_->GetPrefs()->GetInteger( prefs::kDiscountConsentPastDismissedCount); @@ -444,7 +441,7 @@ (last_dismissed_time == base::Time() || reshow_time_delta.is_negative()) && last_dismissed_count < - ntp_features::kNtpChromeCartModuleDiscountConsentMaxDismissalCount + commerce::kNtpChromeCartModuleDiscountConsentMaxDismissalCount .Get(); } } @@ -455,14 +452,14 @@ } bool CartService::IsCartDiscountEnabled() { - if (!cart_features::IsCartDiscountFeatureEnabled()) { + if (!commerce::IsCartDiscountFeatureEnabled()) { return false; } return profile_->GetPrefs()->GetBoolean(prefs::kCartDiscountEnabled); } void CartService::SetCartDiscountEnabled(bool enabled) { - DCHECK(cart_features::IsCartDiscountFeatureEnabled()); + DCHECK(commerce::IsCartDiscountFeatureEnabled()); profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, enabled); if (enabled) { @@ -478,11 +475,11 @@ const GURL& cart_url, base::OnceCallback<void(const ::GURL&)> callback) { auto url = cart_url; - if (cart_features::IsRuleDiscountPartnerMerchant(cart_url)) { + if (commerce::IsRuleDiscountPartnerMerchant(cart_url)) { url = AppendUTM(cart_url, IsCartDiscountEnabled()); } - if (!cart_features::IsRuleDiscountPartnerMerchant(cart_url) || + if (!commerce::IsRuleDiscountPartnerMerchant(cart_url) || !IsCartDiscountEnabled()) { std::move(callback).Run(url); CartDiscountMetricCollector::RecordClickedOnDiscount(false); @@ -542,8 +539,7 @@ void CartService::PrepareForNavigation(const GURL& cart_url, bool is_navigating) { metrics_tracker_->PrepareToRecordUKM(cart_url); - if (is_navigating || - !cart_features::IsRuleDiscountPartnerMerchant(cart_url) || + if (is_navigating || !commerce::IsRuleDiscountPartnerMerchant(cart_url) || !IsCartDiscountEnabled()) { return; } @@ -574,7 +570,7 @@ void CartService::Shutdown() { history_service_observation_.Reset(); - if (cart_features::IsFakeDataEnabled()) { + if (commerce::IsFakeDataEnabled()) { DeleteCartsWithFakeData(); } // Delete content of all carts that are removed. @@ -796,7 +792,7 @@ void CartService::OnLoadCarts(CartDB::LoadCallback callback, bool success, std::vector<CartDB::KeyAndValue> proto_pairs) { - if (cart_features::IsFakeDataEnabled()) { + if (commerce::IsFakeDataEnabled()) { std::sort(proto_pairs.begin(), proto_pairs.end(), CompareTimeStampForProtoPair); std::move(callback).Run(success, std::move(proto_pairs)); @@ -1034,7 +1030,7 @@ base::Time last_fetched_time = profile_->GetPrefs()->GetTime(prefs::kCartDiscountLastFetchedTime); - base::TimeDelta fetch_delay = cart_features::kDiscountFetchDelayParam.Get() - + base::TimeDelta fetch_delay = commerce::kDiscountFetchDelayParam.Get() - (base::Time::Now() - last_fetched_time); if (last_fetched_time == base::Time() || fetch_delay.is_negative() || kBypassDisocuntFetchingThreshold.Get()) {
diff --git a/chrome/browser/cart/cart_service_unittest.cc b/chrome/browser/cart/cart_service_unittest.cc index dc484ca..afcdd745 100644 --- a/chrome/browser/cart/cart_service_unittest.cc +++ b/chrome/browser/cart/cart_service_unittest.cc
@@ -2282,23 +2282,23 @@ histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.AcceptedIn", - ntp_features::DiscountConsentNtpVariation::kDefault, 0); + commerce::DiscountConsentNtpVariation::kDefault, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.AcceptedIn", - ntp_features::DiscountConsentNtpVariation::kDefault, 1); + commerce::DiscountConsentNtpVariation::kDefault, 1); // Simulate consent has been accepted in the Inline variation. profile_->GetPrefs()->SetInteger(prefs::kDiscountConsentDecisionMadeIn, 2); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.AcceptedIn", - ntp_features::DiscountConsentNtpVariation::kInline, 0); + commerce::DiscountConsentNtpVariation::kInline, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.AcceptedIn", - ntp_features::DiscountConsentNtpVariation::kInline, 1); + commerce::DiscountConsentNtpVariation::kInline, 1); } TEST_F(CartServiceDiscountConsentV2Test, @@ -2311,23 +2311,23 @@ histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.RejectedIn", - ntp_features::DiscountConsentNtpVariation::kDefault, 0); + commerce::DiscountConsentNtpVariation::kDefault, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.RejectedIn", - ntp_features::DiscountConsentNtpVariation::kDefault, 1); + commerce::DiscountConsentNtpVariation::kDefault, 1); // Simulate consent has been rejected in the Inline variation. profile_->GetPrefs()->SetInteger(prefs::kDiscountConsentDecisionMadeIn, 2); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.RejectedIn", - ntp_features::DiscountConsentNtpVariation::kInline, 0); + commerce::DiscountConsentNtpVariation::kInline, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.RejectedIn", - ntp_features::DiscountConsentNtpVariation::kInline, 1); + commerce::DiscountConsentNtpVariation::kInline, 1); } TEST_F(CartServiceDiscountConsentV2Test, @@ -2340,12 +2340,12 @@ histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized", - ntp_features::DiscountConsentNtpVariation::kDefault, 0); + commerce::DiscountConsentNtpVariation::kDefault, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized", - ntp_features::DiscountConsentNtpVariation::kDefault, 1); + commerce::DiscountConsentNtpVariation::kDefault, 1); // Simulate consent has been accepted in the Default variation. profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, true); @@ -2353,7 +2353,7 @@ RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized", - ntp_features::DiscountConsentNtpVariation::kDefault, 2); + commerce::DiscountConsentNtpVariation::kDefault, 2); // Simulate consent has been accepted in the Inline variation. profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, true); @@ -2361,12 +2361,12 @@ histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized", - ntp_features::DiscountConsentNtpVariation::kInline, 0); + commerce::DiscountConsentNtpVariation::kInline, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized", - ntp_features::DiscountConsentNtpVariation::kInline, 1); + commerce::DiscountConsentNtpVariation::kInline, 1); } TEST_F(CartServiceDiscountConsentV2Test, @@ -2380,24 +2380,24 @@ histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.DismissedIn", - ntp_features::DiscountConsentNtpVariation::kInline, 0); + commerce::DiscountConsentNtpVariation::kInline, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.DismissedIn", - ntp_features::DiscountConsentNtpVariation::kInline, 1); + commerce::DiscountConsentNtpVariation::kInline, 1); // Simulate consent has been dismissed in the Dialog variation. profile_->GetPrefs()->SetInteger(prefs::kDiscountConsentDismissedIn, 3); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.DismissedIn", - ntp_features::DiscountConsentNtpVariation::kDialog, 0); + commerce::DiscountConsentNtpVariation::kDialog, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.DismissedIn", - ntp_features::DiscountConsentNtpVariation::kDialog, 1); + commerce::DiscountConsentNtpVariation::kDialog, 1); } TEST_F(CartServiceDiscountConsentV2Test, @@ -2410,23 +2410,23 @@ histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.InterestedButNoActionIn", - ntp_features::DiscountConsentNtpVariation::kInline, 0); + commerce::DiscountConsentNtpVariation::kInline, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.InterestedButNoActionIn", - ntp_features::DiscountConsentNtpVariation::kInline, 1); + commerce::DiscountConsentNtpVariation::kInline, 1); // Simulate 'continue' button is clicked in the Dialog variation. profile_->GetPrefs()->SetInteger(prefs::kDiscountConsentShowInterestIn, 3); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.InterestedButNoActionIn", - ntp_features::DiscountConsentNtpVariation::kDialog, 0); + commerce::DiscountConsentNtpVariation::kDialog, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.InterestedButNoActionIn", - ntp_features::DiscountConsentNtpVariation::kDialog, 1); + commerce::DiscountConsentNtpVariation::kDialog, 1); } TEST_F(CartServiceDiscountConsentV2Test, @@ -2440,12 +2440,12 @@ histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NeverShownIn", - ntp_features::DiscountConsentNtpVariation::kInline, 0); + commerce::DiscountConsentNtpVariation::kInline, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NeverShownIn", - ntp_features::DiscountConsentNtpVariation::kInline, 1); + commerce::DiscountConsentNtpVariation::kInline, 1); // Simulate consent is shown in the Inline variation before. profile_->GetPrefs()->SetInteger(prefs::kDiscountConsentLastShownInVariation, @@ -2454,7 +2454,7 @@ RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NeverShownIn", - ntp_features::DiscountConsentNtpVariation::kInline, 1); + commerce::DiscountConsentNtpVariation::kInline, 1); } TEST_F(CartServiceDiscountConsentV2Test, @@ -2469,17 +2469,17 @@ histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowIn", - ntp_features::DiscountConsentNtpVariation::kInline, 0); + commerce::DiscountConsentNtpVariation::kInline, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowIn", - ntp_features::DiscountConsentNtpVariation::kInline, 1); + commerce::DiscountConsentNtpVariation::kInline, 1); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowIn", - ntp_features::DiscountConsentNtpVariation::kInline, 2); + commerce::DiscountConsentNtpVariation::kInline, 2); } TEST_F(CartServiceDiscountConsentV2Test, @@ -2493,17 +2493,17 @@ histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.IgnoredIn", - ntp_features::DiscountConsentNtpVariation::kInline, 0); + commerce::DiscountConsentNtpVariation::kInline, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.IgnoredIn", - ntp_features::DiscountConsentNtpVariation::kInline, 1); + commerce::DiscountConsentNtpVariation::kInline, 1); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.IgnoredIn", - ntp_features::DiscountConsentNtpVariation::kInline, 2); + commerce::DiscountConsentNtpVariation::kInline, 2); } TEST_F(CartServiceDiscountConsentV2Test, @@ -2514,17 +2514,17 @@ histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.ShownIn", - ntp_features::DiscountConsentNtpVariation::kInline, 0); + commerce::DiscountConsentNtpVariation::kInline, 0); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.ShownIn", - ntp_features::DiscountConsentNtpVariation::kInline, 1); + commerce::DiscountConsentNtpVariation::kInline, 1); RecordDiscountConsentStatusAtLoad(should_show); histogram_tester_.ExpectBucketCount( "NewTabPage.Carts.DiscountConsentStatusAtLoad.ShownIn", - ntp_features::DiscountConsentNtpVariation::kInline, 2); + commerce::DiscountConsentNtpVariation::kInline, 2); } TEST_F(CartServiceDiscountConsentV2Test, TestLastShownInVariationUpdated) {
diff --git a/chrome/browser/cart/commerce_hint_service.cc b/chrome/browser/cart/commerce_hint_service.cc index aa0d368..49266fd9 100644 --- a/chrome/browser/cart/commerce_hint_service.cc +++ b/chrome/browser/cart/commerce_hint_service.cc
@@ -10,12 +10,12 @@ #include "base/metrics/histogram_functions.h" #include "base/time/time.h" #include "chrome/browser/cart/cart_db_content.pb.h" -#include "chrome/browser/cart/cart_features.h" #include "chrome/browser/cart/cart_service.h" #include "chrome/browser/cart/cart_service_factory.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "components/commerce/core/commerce_feature_list.h" #include "components/search/ntp_features.h" #include "components/ukm/content/source_url_recorder.h" #include "content/public/browser/document_service.h" @@ -196,7 +196,7 @@ // When rule-based discount is enabled, do not accept cart page URLs from // partner merchants as there could be things like discount tokens in them. if (service_->IsCartDiscountEnabled() && - cart_features::IsRuleDiscountPartnerMerchant(navigation_url) && + commerce::IsRuleDiscountPartnerMerchant(navigation_url) && product_id.empty()) { validated_cart = absl::nullopt; } @@ -225,7 +225,7 @@ // When rule-based discount is enabled, do not accept cart page URLs from // partner merchants as there could be things like discount tokens in them. if (service_->IsCartDiscountEnabled() && - cart_features::IsRuleDiscountPartnerMerchant(cart_url)) { + commerce::IsRuleDiscountPartnerMerchant(cart_url)) { validated_cart = absl::nullopt; } cart_db::ChromeCartContentProto proto;
diff --git a/chrome/browser/cart/fetch_discount_worker.cc b/chrome/browser/cart/fetch_discount_worker.cc index e5f6bc89..e8d239e5 100644 --- a/chrome/browser/cart/fetch_discount_worker.cc +++ b/chrome/browser/cart/fetch_discount_worker.cc
@@ -9,7 +9,6 @@ #include "base/task/thread_pool.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/cart/cart_discount_fetcher.h" -#include "chrome/browser/cart/cart_features.h" #include "chrome/browser/commerce/coupons/coupon_db_content.pb.h" #include "components/commerce/core/commerce_feature_list.h" #include "components/search/ntp_features.h" @@ -149,14 +148,13 @@ // post another delayed fetch. bool has_partner_merchant = false; for (auto pair : proto_pairs) { - if (cart_features::IsPartnerMerchant( - GURL(pair.second.merchant_cart_url()))) { + if (commerce::IsPartnerMerchant(GURL(pair.second.merchant_cart_url()))) { has_partner_merchant = true; break; } } if (!has_partner_merchant) { - Start(cart_features::kDiscountFetchDelayParam.Get()); + Start(commerce::kDiscountFetchDelayParam.Get()); return; } backend_task_runner_->PostTask( @@ -306,6 +304,6 @@ ntp_features::kNtpChromeCartModuleAbandonedCartDiscountParam, false)) { // Continue to work. - Start(cart_features::kDiscountFetchDelayParam.Get()); + Start(commerce::kDiscountFetchDelayParam.Get()); } }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 019fd680..372f1e1 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4959,6 +4959,8 @@ // are allowed to use chrome://resources/ and chrome://theme/ URLs. allowed_webui_hosts.emplace_back(content::kChromeUIResourcesHost); allowed_webui_hosts.emplace_back(chrome::kChromeUIThemeHost); + // For testing purposes chrome://webui-test/ is also allowed. + allowed_webui_hosts.emplace_back(chrome::kChromeUIWebUITestHost); } if (extension->is_extension() || extension->is_legacy_packaged_app() || (extension->is_platform_app() &&
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index d92fc91..df4b620 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -100,6 +100,9 @@ "//ash/components/geolocation", "//ash/components/login/auth", "//ash/components/login/session", + "//ash/components/multidevice", + "//ash/components/multidevice:stub_multidevice_util", + "//ash/components/multidevice/logging", "//ash/components/peripheral_notification", "//ash/components/phonehub", "//ash/components/phonehub/proto", @@ -234,9 +237,6 @@ "//chromeos/components/hibernate:buildflags", "//chromeos/components/local_search_service/public/cpp:cpp", "//chromeos/components/mojo_bootstrap", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice:stub_multidevice_util", - "//chromeos/components/multidevice/logging", "//chromeos/components/onc", "//chromeos/components/quick_answers", "//chromeos/components/quick_answers/public/cpp:cpp", @@ -904,6 +904,8 @@ "../ash/arc/input_overlay/ui/action_edit_menu.h", "../ash/arc/input_overlay/ui/action_label.cc", "../ash/arc/input_overlay/ui/action_label.h", + "../ash/arc/input_overlay/ui/action_tag.cc", + "../ash/arc/input_overlay/ui/action_tag.h", "../ash/arc/input_overlay/ui/action_view.cc", "../ash/arc/input_overlay/ui/action_view.h", "../ash/arc/input_overlay/ui/input_mapping_view.cc", @@ -1677,6 +1679,7 @@ "../ash/guest_os/guest_os_stability_monitor.cc", "../ash/guest_os/guest_os_stability_monitor.h", "../ash/guest_os/infra/cached_callback.h", + "../ash/guest_os/public/guest_os_mount_provider.cc", "../ash/guest_os/public/guest_os_mount_provider.h", "../ash/guest_os/public/guest_os_mount_provider_registry.cc", "../ash/guest_os/public/guest_os_mount_provider_registry.h", @@ -4309,6 +4312,7 @@ "../ash/guest_os/guest_os_stability_monitor_unittest.cc", "../ash/guest_os/infra/cached_callback_unittest.cc", "../ash/guest_os/public/guest_os_mount_provider_registry_unittest.cc", + "../ash/guest_os/public/guest_os_mount_provider_unittest.cc", "../ash/guest_os/vm_sk_forwarding_native_message_host_unittest.cc", "../ash/hats/hats_dialog_unittest.cc", "../ash/hats/hats_finch_helper_unittest.cc", @@ -4640,6 +4644,9 @@ "../ash/printing/history/test_print_job_history_service_observer.cc", "../ash/printing/history/test_print_job_history_service_observer.h", "../ash/printing/oauth2/http_exchange_unittest.cc", + "../ash/printing/oauth2/test_authorization_server.cc", + "../ash/printing/oauth2/test_authorization_server.h", + "../ash/printing/oauth2/test_authorization_server_unittest.cc", "../ash/printing/ppd_resolution_state_unittest.cc", "../ash/printing/ppd_resolution_tracker_unittest.cc", "../ash/printing/print_management/print_job_info_mojom_conversions_unittest.cc", @@ -4864,6 +4871,7 @@ "//ash/components/audio", "//ash/components/disks:test_support", "//ash/components/login/auth", + "//ash/components/multidevice:test_support", "//ash/components/phonehub:test_support", "//ash/components/proximity_auth", "//ash/components/proximity_auth:test_support", @@ -4898,7 +4906,6 @@ "//chrome/test:test_support_ui", "//chrome/test:test_support_unit", "//chromeos", - "//chromeos/components/multidevice:test_support", "//chromeos/components/sensors:test_support", "//chromeos/dbus:test_support", "//chromeos/dbus/anomaly_detector",
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc index c783891..95b93f8 100644 --- a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc +++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
@@ -504,6 +504,9 @@ volume_metadata->volume_type = file_manager_private::VOLUME_TYPE_SYSTEM_INTERNAL; break; + case VOLUME_TYPE_GUEST_OS: + volume_metadata->volume_type = file_manager_private::VOLUME_TYPE_GUEST_OS; + break; case NUM_VOLUME_TYPE: NOTREACHED(); break;
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc index 073f068..d7ea3de 100644 --- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc +++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
@@ -84,8 +84,7 @@ case DlpRulesManager::Restriction::kPrinting: return absl::make_optional(dlp::kPrintingWarnProceededUMA); case DlpRulesManager::Restriction::kScreenshot: - // TODO(crbug.com/1304750): Add UMA stats for warning for screenshots. - return absl::nullopt; + return absl::make_optional(dlp::kScreenshotWarnProceededUMA); case DlpRulesManager::Restriction::kUnknownRestriction: case DlpRulesManager::Restriction::kClipboard: case DlpRulesManager::Restriction::kPrivacyScreen: @@ -173,6 +172,7 @@ ReportWarningEvent(restriction_info.url, DlpRulesManager::Restriction::kScreenshot); DlpBooleanHistogram(dlp::kScreenshotBlockedUMA, IsBlocked(restriction_info)); + DlpBooleanHistogram(dlp::kScreenshotWarnedUMA, IsWarn(restriction_info)); // TODO(crbug.com/1252736): Properly handle WARN for screenshots API. return IsBlocked(restriction_info) || IsWarn(restriction_info); }
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h b/chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h index a844ada..b14bcf0 100644 --- a/chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h +++ b/chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h
@@ -15,6 +15,7 @@ // Constants with UMA histogram name suffixes. constexpr char kCaptureModeInitBlockedUMA[] = "CaptureModeInitBlocked"; +constexpr char kCaptureModeInitWarnedUMA[] = "CaptureModeInitWarned"; constexpr char kClipboardReadBlockedUMA[] = "ClipboardReadBlocked"; constexpr char kDataTransferReportingTimeDiffUMA[] = "DataTransferReportingTimeDiff"; @@ -36,6 +37,10 @@ "ScreenShareWarnSilentProceeded"; constexpr char kScreenSharePausedOrResumedUMA[] = "ScreenSharePausedOrResumed"; constexpr char kScreenshotBlockedUMA[] = "ScreenshotBlocked"; +constexpr char kScreenshotWarnedUMA[] = "ScreenshotWarned"; +constexpr char kScreenshotWarnProceededUMA[] = "ScreenshotWarnProceeded"; +constexpr char kScreenshotWarnSilentProceededUMA[] = + "ScreenshotWarnSilentProceeded"; constexpr char kVideoCaptureInterruptedUMA[] = "VideoCaptureInterrupted"; constexpr char kReportedBlockLevelRestriction[] = "ReportedBlockLevelRestriction";
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc index e6bac3a..d60c68f 100644 --- a/chrome/browser/client_hints/client_hints_browsertest.cc +++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -263,13 +263,13 @@ status_code_ = status_code; } - static constexpr char kCriticaCH[] = "/critical-ch"; + static constexpr char kCriticalCH[] = "/critical-ch"; private: // A response that flips between two critical-ch headers std::unique_ptr<net::test_server::HttpResponse> DifferentCriticalCH( const net::test_server::HttpRequest& request) { - if (!base::StartsWith(request.relative_url, kCriticaCH)) + if (!base::StartsWith(request.relative_url, kCriticalCH)) return nullptr; request_count_++; @@ -1694,7 +1694,9 @@ EXPECT_EQ(0u, count_client_hints_headers_seen()); } -IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, DelegateToFoo_HttpEquiv) { +// Flaky on all platforms. https://crbug.com/1285479. +IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, + DISABLED_DelegateToFoo_HttpEquiv) { // Go to a page which delegates hints to `foo.com`. GURL gurl = http_equiv_accept_ch_delegation_foo(); SetClientHintExpectationsOnMainFrame(false); @@ -1707,7 +1709,8 @@ } // Flaky on all platforms. https://crbug.com/1285479. -IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, DelegateToFoo_MetaName) { +IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, + DISABLED_DelegateToFoo_MetaName) { // Go to a page which delegates hints to `foo.com`. GURL gurl = meta_name_accept_ch_delegation_foo(); SetClientHintExpectationsOnMainFrame(false); @@ -3216,7 +3219,7 @@ base::HistogramTester histogram; ASSERT_TRUE(ui_test_utils::NavigateToURL( browser(), - https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH))); + https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH))); histogram.ExpectBucketCount("ClientHints.CriticalCHRestart", 2 /*=kNavigationRestarted*/, 1); EXPECT_EQ(2, handler.request_count()); @@ -3237,10 +3240,10 @@ base::HistogramTester histogram; ASSERT_TRUE(ui_test_utils::NavigateToURL( browser(), - https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH))); + https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH))); ASSERT_TRUE(ui_test_utils::NavigateToURL( browser(), - https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH))); + https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH))); histogram.ExpectBucketCount("ClientHints.CriticalCHRestart", 2 /*=kNavigationRestarted*/, 2); EXPECT_EQ(4, handler.request_count()); @@ -3382,7 +3385,7 @@ base::HistogramTester histogram; ASSERT_TRUE(ui_test_utils::NavigateToURL( browser(), - https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH))); + https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH))); histogram.ExpectBucketCount("ClientHints.CriticalCHRestart", 2 /*=kNavigationRestarted*/, 1); EXPECT_EQ(2, handler.request_count()); @@ -3415,9 +3418,9 @@ // This will send the two servers redirecting to each other in a loop until // the navigation redirect break is tripped. handler_1.SetRedirectLocation( - https_server_2.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH)); + https_server_2.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH)); handler_2.SetRedirectLocation( - https_server_1.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH)); + https_server_1.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH)); handler_1.SetStatusCode(GetParam()); handler_2.SetStatusCode(GetParam()); @@ -3425,7 +3428,7 @@ base::HistogramTester histogram; ASSERT_TRUE(ui_test_utils::NavigateToURL( browser(), - https_server_1.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH))); + https_server_1.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH))); histogram.ExpectBucketCount("ClientHints.CriticalCHRestart", 2 /*=kNavigationRestarted*/, 2); EXPECT_EQ(net::URLRequest::kMaxRedirects, @@ -5133,14 +5136,9 @@ } protected: - virtual std::string BuildOriginTrialHeader() const = 0; + virtual std::string BuildOriginTrialHeader() const { return ""; } - OriginTrialTestOptions test_options_; - std::set<GURL> expected_request_urls_; - std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_; - - private: - std::unique_ptr<base::FeatureList> EnabledFeatures() { + virtual std::unique_ptr<base::FeatureList> EnabledFeatures() { std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); feature_list->InitializeFromCommandLine( "UserAgentClientHint,CriticalClientHint,AcceptCHFrame," @@ -5149,6 +5147,9 @@ return feature_list; } + OriginTrialTestOptions test_options_; + std::set<GURL> expected_request_urls_; + std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_; base::test::ScopedFeatureList scoped_feature_list_; }; @@ -5641,6 +5642,82 @@ EXPECT_FALSE(cookies[0].IsPartitioned()); } +class PartitionedCookiesBypassOriginTrialBrowserTest + : public PartitionedCookiesOriginTrialBrowserTest { + public: + PartitionedCookiesBypassOriginTrialBrowserTest() + : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) { + https_server_.ServeFilesFromSourceDirectory( + "chrome/test/data/client_hints"); + https_server_.RegisterRequestMonitor(base::BindRepeating( + &PartitionedCookiesBypassOriginTrialBrowserTest::MonitorResourceRequest, + base::Unretained(this))); + EXPECT_TRUE(https_server_.Start()); + } + + // The URL of the site receiving cookies. + // Requests to this origin should be handled by the test server. + static constexpr char kCookieOriginUrlNoPort[] = "https://127.0.0.1:"; + + GURL partitioned_cookies_url() const { + return GURL(base::StrCat({kCookieOriginUrlNoPort, + base::NumberToString(https_server_.port()), + "/partitioned_cookies_embeddee.html"})); + } + + absl::optional<std::string> last_sec_ch_partitioned_cookies_value() { + base::AutoLock lock(last_request_lock_); + return last_sec_ch_partitioned_cookies_value_; + } + + void MonitorResourceRequest(const net::test_server::HttpRequest& request) { + base::AutoLock lock(last_request_lock_); + const auto& it = request.headers.find("sec-ch-partitioned-cookies"); + last_sec_ch_partitioned_cookies_value_ = + it != request.headers.end() ? absl::make_optional(it->second) + : absl::nullopt; + } + + protected: + std::unique_ptr<base::FeatureList> EnabledFeatures() override { + std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); + feature_list->InitializeFromCommandLine( + "UserAgentClientHint,CriticalClientHint,AcceptCHFrame," + "PartitionedCookies,PartitionedCookiesBypassOriginTrial", + ""); + return feature_list; + } + + net::EmbeddedTestServer https_server_; + absl::optional<std::string> last_sec_ch_partitioned_cookies_value_; + base::Lock last_request_lock_; +}; + +IN_PROC_BROWSER_TEST_F(PartitionedCookiesBypassOriginTrialBrowserTest, + ShouldAllowCookiesWithoutToken) { + SetCookie( + "__Host-A", "0", partitioned_cookies_url(), + net::CookiePartitionKey::FromURLForTesting(partitioned_cookies_url())); + + auto cookies = GetCookies(partitioned_cookies_url()); + EXPECT_EQ(cookies.size(), 1u); + EXPECT_EQ(cookies[0].Name(), "__Host-A"); + EXPECT_TRUE(cookies[0].IsPartitioned()); + + NavigateTo(partitioned_cookies_url()); + + // Check that the partitioned cookie did not get converted to unpartitioned + // even though the site never opted into the origin trial. + cookies = GetCookies(partitioned_cookies_url()); + EXPECT_EQ(cookies.size(), 1u); + EXPECT_EQ(cookies[0].Name(), "__Host-A"); + EXPECT_TRUE(cookies[0].IsPartitioned()); + + // We will still send the client hint in the false state if there are + // partitioned cookies on the machine. + EXPECT_EQ(last_sec_ch_partitioned_cookies_value(), "?0"); +} + // CrOS multi-profiles implementation is too different for these tests. #if !BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/commerce/coupons/coupon_service.cc b/chrome/browser/commerce/coupons/coupon_service.cc index 3c70f5b..0fb4523 100644 --- a/chrome/browser/commerce/coupons/coupon_service.cc +++ b/chrome/browser/commerce/coupons/coupon_service.cc
@@ -4,7 +4,6 @@ #include "chrome/browser/commerce/coupons/coupon_service.h" #include "base/observer_list.h" -#include "chrome/browser/cart/cart_features.h" #include "chrome/browser/commerce/coupons/coupon_db_content.pb.h" #include "components/commerce/core/commerce_feature_list.h" @@ -139,8 +138,8 @@ } void CouponService::MaybeFeatureStatusChanged(bool enabled) { - enabled &= (commerce::IsCouponWithCodeEnabled() || - cart_features::IsFakeDataEnabled()); + enabled &= + (commerce::IsCouponWithCodeEnabled() || commerce::IsFakeDataEnabled()); if (enabled == features_enabled_) return; features_enabled_ = enabled;
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc index c371379..02e56c1 100644 --- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc +++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -281,6 +281,9 @@ } bool CrOSComponentInstaller::Unload(const std::string& name) { + DispatchFailedLoads(std::move(load_cache_[name].callbacks)); + load_cache_.erase(name); + const ComponentConfig* config = FindConfig(name); if (!config) { // Component |name| does not exist. @@ -306,6 +309,8 @@ } void CrOSComponentInstaller::UnregisterCompatiblePath(const std::string& name) { + DispatchFailedLoads(std::move(load_cache_[name].callbacks)); + load_cache_.erase(name); compatible_components_.erase(name); } @@ -320,6 +325,13 @@ delegate_->EmitInstalledSignal(component); } +CrOSComponentInstaller::LoadInfo::LoadInfo() = default; +CrOSComponentInstaller::LoadInfo::~LoadInfo() = default; +std::map<std::string, CrOSComponentInstaller::LoadInfo>& +CrOSComponentInstaller::GetLoadCacheForTesting() { + return load_cache_; +} + bool CrOSComponentInstaller::IsRegisteredMayBlock(const std::string& name) { base::FilePath root; if (!base::PathService::Get(DIR_COMPONENT_USER, &root)) @@ -420,6 +432,23 @@ void CrOSComponentInstaller::LoadInternal(const std::string& name, LoadCallback load_callback) { + // Use the cached value if it exists. + auto it = load_cache_.find(name); + if (it != load_cache_.end()) { + // If the request is ongoing, queue up a callback. + if (!it->second.success.has_value()) { + it->second.callbacks.push_back(std::move(load_callback)); + return; + } + // Otherwise immediately dispatch. + DispatchLoadCallback(std::move(load_callback), it->second.path, + it->second.success.value()); + return; + } + + // Update the cache to indicate the request is being queued. + load_cache_[name].success = absl::nullopt; + const base::FilePath path = GetCompatiblePath(name); DCHECK(!path.empty()); chromeos::DBusThreadManager::Get() @@ -438,16 +467,25 @@ // Report component image mount time. UMA_HISTOGRAM_LONG_TIMES("ComponentUpdater.ChromeOS.MountTime", base::TimeTicks::Now() - start_time); - if (!result.has_value()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(std::move(load_callback), - ReportError(Error::MOUNT_FAILURE), base::FilePath())); - } else { - metadata_table_->AddComponentForCurrentUser(name); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(std::move(load_callback), - ReportError(Error::NONE), result.value())); + + bool success = result.has_value(); + base::FilePath path; + if (success) + path = result.value(); + + DispatchLoadCallback(std::move(load_callback), path, success); + + // Update the cache. + auto it = load_cache_.find(name); + if (it != load_cache_.end()) { + it->second.success = success; + it->second.path = path; + + // Dispatch queued up callbacks. + for (LoadCallback& queued_callback : it->second.callbacks) { + DispatchLoadCallback(std::move(queued_callback), path, success); + } + it->second.callbacks.clear(); } } @@ -463,4 +501,21 @@ return compatible_components_.count(name) > 0; } +void CrOSComponentInstaller::DispatchLoadCallback(LoadCallback callback, + base::FilePath path, + bool success) { + Error error = success ? Error::NONE : Error::MOUNT_FAILURE; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(callback), ReportError(error), std::move(path))); +} + +void CrOSComponentInstaller::DispatchFailedLoads( + std::vector<LoadCallback> callbacks) { + for (LoadCallback& callback : callbacks) { + DispatchLoadCallback(std::move(callback), base::FilePath(), + /*success=*/false); + } +} + } // namespace component_updater
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.h b/chrome/browser/component_updater/cros_component_installer_chromeos.h index 6c2381f..5e971ecf 100644 --- a/chrome/browser/component_updater/cros_component_installer_chromeos.h +++ b/chrome/browser/component_updater/cros_component_installer_chromeos.h
@@ -157,6 +157,28 @@ // Broadcasts a D-Bus signal for a successful component installation. void EmitInstalledSignal(const std::string& component); + // The load cache contains three pieces of information: + // (1) For a given component, whether the load request was successful, a + // failure, or in-progress. + // (2) If the load request was successful, the file path to the loaded + // image. + // (3) If the load request is in progress, the callbacks to invoke after the + // load request finishes. + struct LoadInfo { + LoadInfo(); + ~LoadInfo(); + // If null, then the request is pending. + absl::optional<bool> success; + // Only populated on success. + base::FilePath path; + // Only populated if request is pending. Includes all subsequent callbacks + // after the first. + std::vector<LoadCallback> callbacks; + }; + + // Test-only method for introspection. + std::map<std::string, LoadInfo>& GetLoadCacheForTesting(); + protected: ~CrOSComponentInstaller() override; @@ -211,6 +233,13 @@ // |name|. bool IsCompatible(const std::string& name) const; + // Posts a task with the response information for |callback|. + void DispatchLoadCallback(LoadCallback callback, + base::FilePath path, + bool success); + // Repeatedly calls DispatchLoadCallback with failure parameters. + void DispatchFailedLoads(std::vector<LoadCallback> callbacks); + // Maps from a compatible component name to its installed path. base::flat_map<std::string, base::FilePath> compatible_components_; @@ -220,6 +249,10 @@ // Table storing metadata (installs, usage, etc.). std::unique_ptr<MetadataTable> metadata_table_; + // The load cache stores ongoing load requests, as well as the finished + // results. + std::map<std::string, LoadInfo> load_cache_; + ComponentUpdateService* const component_updater_; };
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc b/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc index d7ffa9fb..524fa49 100644 --- a/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc +++ b/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc
@@ -924,4 +924,54 @@ EXPECT_EQ(base::FilePath(kTestComponentMountPath), mount_path); } +TEST_F(CrOSComponentInstallerTest, LoadCache) { + absl::optional<base::FilePath> install_path = CreateInstalledComponent( + kTestComponentName, "1.0", kTestComponentValidMinEnvVersion); + ASSERT_TRUE(install_path.has_value()); + + image_loader_client()->SetMountPathForComponent( + kTestComponentName, base::FilePath(kTestComponentMountPath)); + + TestUpdater updater; + std::unique_ptr<MockComponentUpdateService> update_service = + CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater); + scoped_refptr<CrOSComponentInstaller> cros_component_manager = + base::MakeRefCounted<CrOSComponentInstaller>(nullptr, + update_service.get()); + + cros_component_manager->RegisterInstalled(); + RunUntilIdle(); + EXPECT_FALSE(updater.HasPendingUpdate(kTestComponentName)); + EXPECT_EQ(install_path.value(), + cros_component_manager->GetCompatiblePath(kTestComponentName)); + + absl::optional<CrOSComponentManager::Error> load_result1; + base::FilePath mount_path1; + absl::optional<CrOSComponentManager::Error> load_result2; + base::FilePath mount_path2; + cros_component_manager->Load( + kTestComponentName, CrOSComponentManager::MountPolicy::kMount, + CrOSComponentManager::UpdatePolicy::kDontForce, + base::BindOnce(&RecordLoadResult, &load_result1, &mount_path1)); + cros_component_manager->Load( + kTestComponentName, CrOSComponentManager::MountPolicy::kMount, + CrOSComponentManager::UpdatePolicy::kDontForce, + base::BindOnce(&RecordLoadResult, &load_result2, &mount_path2)); + + auto& load_cache = cros_component_manager->GetLoadCacheForTesting(); + ASSERT_EQ(load_cache.size(), 1u); + ASSERT_EQ(load_cache.begin()->second.callbacks.size(), 1u); + RunUntilIdle(); + + ASSERT_TRUE(load_result1.has_value()); + ASSERT_TRUE(load_result2.has_value()); + ASSERT_EQ(load_result1.value(), load_result2.value()); + ASSERT_EQ(mount_path1, mount_path2); + ASSERT_EQ(load_cache.size(), 1u); + ASSERT_EQ(load_cache.begin()->second.callbacks.size(), 0u); + ASSERT_TRUE(load_cache.begin()->second.success.has_value()); + ASSERT_TRUE(load_cache.begin()->second.success.value()); + ASSERT_EQ(mount_path1, load_cache.begin()->second.path); +} + } // namespace component_updater
diff --git a/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.cc b/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.cc new file mode 100644 index 0000000..b5b855b --- /dev/null +++ b/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.cc
@@ -0,0 +1,119 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/component_updater/desktop_screenshot_editor_component_installer.h" + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/path_service.h" +#include "base/task/post_task.h" +#include "base/task/thread_pool.h" +#include "base/version.h" +#include "chrome/browser/share/share_features.h" +#include "components/component_updater/component_updater_paths.h" + +namespace { + +constexpr base::FilePath::CharType kDesktopScreenshotEditorBinaryPbFileName[] = + FILE_PATH_LITERAL("image_editor_app"); + +// The SHA256 of the SubjectPublicKeyInfo used to sign the extension. +// The extension id is: kdbdaidmledpgkihpopchgmjikgkjclh +constexpr uint8_t kDesktopScreenshotEditorPublicKeySHA256[32] = { + 0x82, 0x30, 0x22, 0x02, 0x0d, 0x30, 0x09, 0x06, 0x86, 0x2a, 0x86, + 0x48, 0x0d, 0xf7, 0x01, 0x01, 0x05, 0x01, 0x03, 0x00, 0x02, 0x82, + 0x00, 0x0f, 0x82, 0x30, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x02}; + +const char kDesktopScreenshotEditorManifestName[] = + "Desktop Screenshot Editor App"; + +} // namespace + +namespace component_updater { + +bool DesktopScreenshotEditorComponentInstallerPolicy:: + SupportsGroupPolicyEnabledComponentUpdates() const { + return true; +} + +bool DesktopScreenshotEditorComponentInstallerPolicy:: + RequiresNetworkEncryption() const { + return false; +} + +update_client::CrxInstaller::Result +DesktopScreenshotEditorComponentInstallerPolicy::OnCustomInstall( + const base::Value& manifest, + const base::FilePath& install_dir) { + return update_client::CrxInstaller::Result(0); // Nothing custom added. +} + +void DesktopScreenshotEditorComponentInstallerPolicy::OnCustomUninstall() {} + +base::FilePath +DesktopScreenshotEditorComponentInstallerPolicy::GetInstalledPath( + const base::FilePath& base) { + return base.Append(kDesktopScreenshotEditorBinaryPbFileName); +} + +void DesktopScreenshotEditorComponentInstallerPolicy::ComponentReady( + const base::Version& version, + const base::FilePath& install_dir, + base::Value manifest) { + VLOG(1) << "Component ready, version " << version.GetString() << " in " + << install_dir.value(); +} + +// Called during startup and installation before ComponentReady(). +bool DesktopScreenshotEditorComponentInstallerPolicy::VerifyInstallation( + const base::Value& manifest, + const base::FilePath& install_dir) const { + // TODO(skare): We could enforce some sanity checks that files are present. + // Otherwise, this is a bundle of files that web contents load. + return base::PathExists(GetInstalledPath(install_dir)); +} + +base::FilePath +DesktopScreenshotEditorComponentInstallerPolicy::GetRelativeInstallDir() const { + return base::FilePath(FILE_PATH_LITERAL("DesktopScreenshotEditor")); +} + +void DesktopScreenshotEditorComponentInstallerPolicy::GetHash( + std::vector<uint8_t>* hash) const { + hash->assign(std::begin(kDesktopScreenshotEditorPublicKeySHA256), + std::end(kDesktopScreenshotEditorPublicKeySHA256)); +} + +std::string DesktopScreenshotEditorComponentInstallerPolicy::GetName() const { + return kDesktopScreenshotEditorManifestName; +} + +update_client::InstallerAttributes +DesktopScreenshotEditorComponentInstallerPolicy::GetInstallerAttributes() + const { + return update_client::InstallerAttributes(); +} + +void RegisterDesktopScreenshotEditorComponent( + component_updater::ComponentUpdateService* cus) { + // Require either Upcoming Sharing Features or DesktopScreenshotsEdit. + if (!share::AreUpcomingSharingFeaturesEnabled() && + !base::FeatureList::IsEnabled(share::kSharingDesktopScreenshotsEdit)) { + return; + } + VLOG(1) << "Registering Screenshot Editor component."; + auto installer = base::MakeRefCounted<ComponentInstaller>( + std::make_unique<DesktopScreenshotEditorComponentInstallerPolicy>()); + installer->Register(cus, base::OnceClosure()); +} + +} // namespace component_updater
diff --git a/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.h b/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.h new file mode 100644 index 0000000..d87f505 --- /dev/null +++ b/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.h
@@ -0,0 +1,63 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_COMPONENT_UPDATER_DESKTOP_SCREENSHOT_EDITOR_COMPONENT_INSTALLER_H_ +#define CHROME_BROWSER_COMPONENT_UPDATER_DESKTOP_SCREENSHOT_EDITOR_COMPONENT_INSTALLER_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/values.h" +#include "components/component_updater/component_installer.h" + +namespace base { +class FilePath; +} // namespace base + +namespace component_updater { + +class ComponentUpdateService; + +class DesktopScreenshotEditorComponentInstallerPolicy + : public ComponentInstallerPolicy { + public: + DesktopScreenshotEditorComponentInstallerPolicy() = default; + DesktopScreenshotEditorComponentInstallerPolicy( + const DesktopScreenshotEditorComponentInstallerPolicy&) = delete; + DesktopScreenshotEditorComponentInstallerPolicy& operator=( + const DesktopScreenshotEditorComponentInstallerPolicy&) = delete; + ~DesktopScreenshotEditorComponentInstallerPolicy() override = default; + + private: + // The following methods override ComponentInstallerPolicy. + bool SupportsGroupPolicyEnabledComponentUpdates() const override; + bool RequiresNetworkEncryption() const override; + update_client::CrxInstaller::Result OnCustomInstall( + const base::Value& manifest, + const base::FilePath& install_dir) override; + void OnCustomUninstall() override; + bool VerifyInstallation(const base::Value& manifest, + const base::FilePath& install_dir) const override; + void ComponentReady(const base::Version& version, + const base::FilePath& install_dir, + base::Value manifest) override; + base::FilePath GetRelativeInstallDir() const override; + void GetHash(std::vector<uint8_t>* hash) const override; + std::string GetName() const override; + update_client::InstallerAttributes GetInstallerAttributes() const override; + + static base::FilePath GetInstalledPath(const base::FilePath& base); +}; + +// Call once during startup to make the component update service aware of +// the Desktop Screenshot Editor component. +void RegisterDesktopScreenshotEditorComponent(ComponentUpdateService* cus); + +} // namespace component_updater + +#endif // CHROME_BROWSER_COMPONENT_UPDATER_DESKTOP_SCREENSHOT_EDITOR_COMPONENT_INSTALLER_H_
diff --git a/chrome/browser/component_updater/desktop_sharing_hub_component_installer.h b/chrome/browser/component_updater/desktop_sharing_hub_component_installer.h index 4ac49a7..ccacbd0c 100644 --- a/chrome/browser/component_updater/desktop_sharing_hub_component_installer.h +++ b/chrome/browser/component_updater/desktop_sharing_hub_component_installer.h
@@ -55,7 +55,7 @@ }; // Call once during startup to make the component update service aware of -// the File Type Policies component. +// the Desktop Sharing Hub component. void RegisterDesktopSharingHubComponent(ComponentUpdateService* cus); } // namespace component_updater
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer.cc b/chrome/browser/component_updater/first_party_sets_component_installer.cc index 3d2734d..00c39e0 100644 --- a/chrome/browser/component_updater/first_party_sets_component_installer.cc +++ b/chrome/browser/component_updater/first_party_sets_component_installer.cc
@@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/feature_list.h" #include "base/files/file.h" +#include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/ref_counted.h" @@ -24,6 +25,7 @@ #include "content/public/browser/first_party_sets_handler.h" #include "content/public/common/content_features.h" #include "net/cookies/cookie_util.h" +#include "third_party/abseil-cpp/absl/types/optional.h" using component_updater::ComponentUpdateService; @@ -51,8 +53,11 @@ return base::File(pb_path, base::File::FLAG_OPEN | base::File::FLAG_READ); } -base::FilePath& GetConfigPathInstance() { - static base::NoDestructor<base::FilePath> instance; +absl::optional<base::FilePath>& GetConfigPathInstance() { + // Contains nullopt until registration is complete. Afterward, contains the + // FilePath for the component file, or an empty FilePath if no component was + // installed at startup. + static base::NoDestructor<absl::optional<base::FilePath>> instance; return *instance; } @@ -74,8 +79,15 @@ return; } - const base::FilePath instance_path = GetConfigPathInstance(); - if (instance_path.empty()) { + const absl::optional<base::FilePath>& instance_path = GetConfigPathInstance(); + if (!instance_path.has_value()) { + // Registration not is complete yet. The policy's `on_sets_ready_` callback + // will still be invoked once registration is done, so we don't bother to + // save or invoke `on_sets_ready`. + return; + } + + if (instance_path->empty()) { // Registration is complete, but no component version exists on disk. std::move(on_sets_ready).Run(base::File()); return; @@ -85,7 +97,7 @@ // network navigations at startup. base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock(), GetTaskPriority()}, - base::BindOnce(&OpenFile, instance_path), std::move(on_sets_ready)); + base::BindOnce(&OpenFile, *instance_path), std::move(on_sets_ready)); } std::string BoolToString(bool b) { @@ -103,6 +115,8 @@ } void FirstPartySetsComponentInstallerPolicy::OnRegistrationComplete() { + if (!GetConfigPathInstance().has_value()) + GetConfigPathInstance() = base::FilePath(); SetFirstPartySetsConfig(std::move(on_sets_ready_)); } @@ -149,7 +163,7 @@ const base::Version& version, const base::FilePath& install_dir, base::Value manifest) { - if (install_dir.empty() || !GetConfigPathInstance().empty()) + if (install_dir.empty() || GetConfigPathInstance().has_value()) return; VLOG(1) << "First-Party Sets Component ready, version " << version.GetString() @@ -202,7 +216,7 @@ // static void FirstPartySetsComponentInstallerPolicy::ResetForTesting() { - GetConfigPathInstance().clear(); + GetConfigPathInstance().reset(); } void RegisterFirstPartySetsComponent(ComponentUpdateService* cus) {
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer.h b/chrome/browser/component_updater/first_party_sets_component_installer.h index 72f4807f..4a352455 100644 --- a/chrome/browser/component_updater/first_party_sets_component_installer.h +++ b/chrome/browser/component_updater/first_party_sets_component_installer.h
@@ -46,6 +46,7 @@ static void ReconfigureAfterNetworkRestart( SetsReadyOnceCallback on_sets_ready); + // To be called once registration is complete. void OnRegistrationComplete(); // Resets static state. Should only be used to clear state during testing. @@ -69,6 +70,10 @@ FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest, LoadsSets_OnNetworkRestart); FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest, + ReconfigureNetworkBeforeComponentReady); + FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest, + ReconfigureNetworkBeforeRegistrationComplete); + FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest, IgnoreNewSets_NoInitialComponent); FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest, IgnoreNewSets_OnComponentReady);
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc b/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc index b19a6056..7053917a 100644 --- a/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc +++ b/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
@@ -292,6 +292,60 @@ } } +TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest, + ReconfigureNetworkBeforeComponentReady) { + SEQUENCE_CHECKER(sequence_checker); + const std::string expectation = "some first party sets"; + + base::RunLoop run_loop; + FirstPartySetsComponentInstallerPolicy policy( + base::BindLambdaForTesting([&](base::File file) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker); + EXPECT_TRUE(file.IsValid()); + EXPECT_EQ(ReadToString(std::move(file)), expectation); + run_loop.Quit(); + })); + + ASSERT_TRUE( + base::WriteFile(FirstPartySetsComponentInstallerPolicy::GetInstalledPath( + component_install_dir_.GetPath()), + expectation)); + + policy.ReconfigureAfterNetworkRestart( + base::BindLambdaForTesting([](base::File file) { + // Should not be called. + EXPECT_TRUE(false); + })); + + policy.ComponentReady(base::Version(), component_install_dir_.GetPath(), + base::Value(base::Value::Type::DICTIONARY)); + + run_loop.Run(); +} + +TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest, + ReconfigureNetworkBeforeRegistrationComplete) { + SEQUENCE_CHECKER(sequence_checker); + + base::RunLoop run_loop; + FirstPartySetsComponentInstallerPolicy policy( + base::BindLambdaForTesting([&](base::File file) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker); + EXPECT_FALSE(file.IsValid()); + run_loop.Quit(); + })); + + policy.ReconfigureAfterNetworkRestart( + base::BindLambdaForTesting([](base::File file) { + // Should not be called. + EXPECT_TRUE(false); + })); + + policy.OnRegistrationComplete(); + + run_loop.Run(); +} + // Test ReconfigureAfterNetworkRestart calls the callback with the correct // version, i.e. the first installed component, even if there are newer versions // installed after browser startup.
diff --git a/chrome/browser/component_updater/recovery_component_installer.cc b/chrome/browser/component_updater/recovery_component_installer.cc index 52efafe..839de739 100644 --- a/chrome/browser/component_updater/recovery_component_installer.cc +++ b/chrome/browser/component_updater/recovery_component_installer.cc
@@ -201,6 +201,7 @@ base::LaunchOptions options; options.start_hidden = true; + options.elevated = true; base::Process process = base::LaunchElevatedProcess(cmdline, options); #elif BUILDFLAG(IS_MAC) base::mac::ScopedAuthorizationRef authRef(
diff --git a/chrome/browser/component_updater/registration.cc b/chrome/browser/component_updater/registration.cc index 754679bb..d33dbde 100644 --- a/chrome/browser/component_updater/registration.cc +++ b/chrome/browser/component_updater/registration.cc
@@ -60,6 +60,7 @@ #endif // BUILDFLAG(IS_ANDROID) #if !BUILDFLAG(IS_ANDROID) +#include "chrome/browser/component_updater/desktop_screenshot_editor_component_installer.h" #include "chrome/browser/component_updater/desktop_sharing_hub_component_installer.h" #include "chrome/browser/component_updater/soda_component_installer.h" #include "chrome/browser/component_updater/zxcvbn_data_component_installer.h" @@ -195,6 +196,7 @@ #endif #if !BUILDFLAG(IS_ANDROID) + RegisterDesktopScreenshotEditorComponent(cus); RegisterDesktopSharingHubComponent(cus); RegisterZxcvbnDataComponent(cus); #endif // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/download/bubble/download_bubble_controller.cc b/chrome/browser/download/bubble/download_bubble_controller.cc new file mode 100644 index 0000000..2f4a01e0 --- /dev/null +++ b/chrome/browser/download/bubble/download_bubble_controller.cc
@@ -0,0 +1,281 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/download/bubble/download_bubble_controller.h" +#include "base/files/file_path.h" +#include "base/time/time.h" +#include "chrome/browser/content_index/content_index_provider_impl.h" +#include "chrome/browser/download/download_item_model.h" +#include "chrome/browser/download/offline_item_model_manager.h" +#include "chrome/browser/download/offline_item_model_manager_factory.h" +#include "chrome/browser/download/offline_item_utils.h" +#include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h" +#include "chrome/browser/profiles/profile_key.h" +#include "components/download/public/common/download_item.h" +#include "components/offline_items_collection/core/offline_content_aggregator.h" +#include "content/public/browser/download_manager.h" + +using DownloadCreationType = ::download::DownloadItem::DownloadCreationType; + +namespace { +constexpr int kShowDownloadsInBubbleForNumDays = 1; + +bool FindOfflineItemByContentId(const ContentId& to_find, + const OfflineItem& candidate) { + return candidate.id == to_find; +} + +bool DownloadUIModelIsRecent(const DownloadUIModelPtr& model, + base::Time cutoff_time) { + return ((model->GetStartTime().is_null() && !model->IsDone()) || + model->GetStartTime() > cutoff_time); +} + +using DownloadUIModelPtrList = std::list<DownloadUIModelPtr>; +struct StartTimeComparator { + bool operator()(const DownloadUIModelPtrList::iterator& a_iter, + const DownloadUIModelPtrList::iterator& b_iter) const { + DownloadUIModel* a = (*a_iter).get(); + DownloadUIModel* b = (*b_iter).get(); + // Offline items have start time not populated, so display only not finished + // ones. + bool is_a_active_offline = (a->GetStartTime().is_null() && !a->IsDone()); + bool is_b_active_offline = (b->GetStartTime().is_null() && !b->IsDone()); + // a definitely shown before b if, 1) b is not an active offline item, and + // 2) Either a is active offline item, or a is more recent + return (!is_b_active_offline && + (is_a_active_offline || (a->GetStartTime() > b->GetStartTime()))); + } +}; +using SortedDownloadUIModelSet = + std::multiset<DownloadUIModelPtrList::iterator, StartTimeComparator>; + +} // namespace + +DownloadBubbleUIController::DownloadBubbleUIController(Profile* profile) + : profile_(profile), + download_manager_(profile_->GetDownloadManager()), + download_notifier_(download_manager_, this), + aggregator_(OfflineContentAggregatorFactory::GetForKey( + profile_->GetProfileKey())), + offline_manager_( + OfflineItemModelManagerFactory::GetForBrowserContext(profile_)) { + observation_.Observe(aggregator_.get()); +} + +DownloadBubbleUIController::~DownloadBubbleUIController() = default; + +bool DownloadBubbleUIController::MaybeAddOfflineItem(const OfflineItem& item, + bool is_new) { + if (profile_->IsOffTheRecord() != item.is_off_the_record) + return false; + + if (OfflineItemUtils::IsDownload(item.id)) + return false; + + if (item.state == OfflineItemState::CANCELLED) + return false; + + if (item.id.name_space == ContentIndexProviderImpl::kProviderNamespace) + return false; + + if (!OfflineItemModel::Wrap(offline_manager_, item)->ShouldShowInBubble()) + return false; + + offline_items_.push_back(item); + if (is_new) { + partial_view_ids_.insert(item.id); + } + return true; +} + +void DownloadBubbleUIController::MaybeAddOfflineItems( + base::OnceCallback<void()> callback, + bool is_new, + const OfflineItemList& offline_items) { + for (const OfflineItem& item : offline_items) { + MaybeAddOfflineItem(item, is_new); + } + std::move(callback).Run(); +} + +void DownloadBubbleUIController::InitOfflineItems( + DownloadDisplayController* display_controller, + base::OnceCallback<void()> callback) { + display_controller_ = display_controller; + aggregator_->GetAllItems(base::BindOnce( + &DownloadBubbleUIController::MaybeAddOfflineItems, + weak_factory_.GetWeakPtr(), std::move(callback), /*is_new=*/false)); +} + +const OfflineItemList& DownloadBubbleUIController::GetOfflineItems() { + PruneOfflineItems(); + return offline_items_; +} + +void DownloadBubbleUIController::OnManagerGoingDown( + content::DownloadManager* manager) { + if (manager == download_manager_) { + download_manager_ = nullptr; + } +} + +void DownloadBubbleUIController::OnContentProviderGoingDown() { + observation_.Reset(); +} + +void DownloadBubbleUIController::OnItemsAdded( + const OfflineContentProvider::OfflineItemList& items) { + bool any_new = false; + bool any_in_progress = false; + for (const OfflineItem& item : items) { + if (MaybeAddOfflineItem(item, /*is_new=*/true)) { + if (item.state == OfflineItemState::IN_PROGRESS) { + any_in_progress = true; + } + any_new = true; + } + } + if (any_new) { + display_controller_->OnNewItem(any_in_progress); + } +} + +void DownloadBubbleUIController::OnDownloadCreated( + content::DownloadManager* manager, + download::DownloadItem* item) { + DownloadUIModelPtr model = DownloadItemModel::Wrap( + item, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()); + if (item && model->ShouldShowInBubble() && + model->download()->GetDownloadCreationType() != + DownloadCreationType::TYPE_HISTORY_IMPORT) { + partial_view_ids_.insert(model->GetContentId()); + display_controller_->OnNewItem(item->GetState() == + download::DownloadItem::IN_PROGRESS); + } +} + +void DownloadBubbleUIController::OnItemRemoved(const ContentId& id) { + offline_items_.erase( + std::remove_if(offline_items_.begin(), offline_items_.end(), + [&id](const OfflineItem& candidate) { + return FindOfflineItemByContentId(id, candidate); + }), + offline_items_.end()); + partial_view_ids_.erase(id); + display_controller_->OnRemovedItem(); +} + +void DownloadBubbleUIController::OnDownloadRemoved( + content::DownloadManager* manager, + download::DownloadItem* item) { + partial_view_ids_.erase( + DownloadItemModel::Wrap( + item, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()) + ->GetContentId()); + display_controller_->OnRemovedItem(); +} + +void DownloadBubbleUIController::OnItemUpdated( + const OfflineItem& item, + const absl::optional<UpdateDelta>& update_delta) { + // Update item + offline_items_.erase( + std::remove_if(offline_items_.begin(), offline_items_.end(), + [&item](const OfflineItem& candidate) { + return FindOfflineItemByContentId(item.id, candidate); + }), + offline_items_.end()); + MaybeAddOfflineItem(item, /*is_new=*/false); + display_controller_->OnUpdatedItem( + OfflineItemModel::Wrap(offline_manager_, item)->IsDone()); +} + +void DownloadBubbleUIController::OnDownloadUpdated( + content::DownloadManager* manager, + download::DownloadItem* item) { + display_controller_->OnUpdatedItem( + DownloadItemModel::Wrap( + item, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()) + ->IsDone()); +} + +void DownloadBubbleUIController::RemoveContentIdFromPartialView( + const ContentId& id) { + partial_view_ids_.erase(id); +} + +void DownloadBubbleUIController::PruneOfflineItems() { + base::Time cutoff_time = + base::Time::Now() - base::Days(kShowDownloadsInBubbleForNumDays); + + for (auto item_iter = offline_items_.begin(); + item_iter != offline_items_.end();) { + if (!DownloadUIModelIsRecent( + OfflineItemModel::Wrap(offline_manager_, *item_iter), + cutoff_time)) { + partial_view_ids_.erase(item_iter->id); + item_iter = offline_items_.erase(item_iter); + } else { + item_iter++; + } + } +} + +std::vector<DownloadUIModelPtr> DownloadBubbleUIController::GetDownloadUIModels( + bool is_main_view) { + // Prune just to keep the list of offline entries small. + PruneOfflineItems(); + + // Aggregate downloads and offline items + std::vector<DownloadUIModelPtr> models_aggregate; + for (const OfflineItem& item : offline_items_) { + models_aggregate.push_back(OfflineItemModel::Wrap( + offline_manager_, item, + std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>())); + } + std::vector<download::DownloadItem*> download_items; + download_manager_->GetAllDownloads(&download_items); + for (download::DownloadItem* item : download_items) { + models_aggregate.push_back(DownloadItemModel::Wrap( + item, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>())); + } + + // Store list of DownloadUIModelPtrs. Sort list iterators in a set, as a set + // does not allow move semantics over unique_ptr, preventing us from putting + // DownloadUIModelPtr directly in the set. + DownloadUIModelPtrList filtered_models_list; + SortedDownloadUIModelSet sorted_ui_model_iters; + base::Time cutoff_time = + base::Time::Now() - base::Days(kShowDownloadsInBubbleForNumDays); + for (auto& model : models_aggregate) { + // Partial view consists of only the entries in partial_view_ids_, which are + // also removed if viewed on the main view. + if (model->ShouldShowInBubble() && + DownloadUIModelIsRecent(model, cutoff_time) && + (is_main_view || partial_view_ids_.find(model->GetContentId()) != + partial_view_ids_.end())) { + if (is_main_view) { + partial_view_ids_.erase(model->GetContentId()); + } + filtered_models_list.push_front(std::move(model)); + sorted_ui_model_iters.insert(filtered_models_list.begin()); + } + } + + // Convert set iterators to sorted vector. + std::vector<DownloadUIModelPtr> models_return_arr; + for (const auto& model_iter : sorted_ui_model_iters) { + models_return_arr.push_back(std::move((*model_iter))); + } + return models_return_arr; +} + +std::vector<DownloadUIModelPtr> DownloadBubbleUIController::GetMainView() { + return GetDownloadUIModels(/*is_main_view=*/true); +} + +std::vector<DownloadUIModelPtr> DownloadBubbleUIController::GetPartialView() { + return GetDownloadUIModels(/*is_main_view=*/false); +}
diff --git a/chrome/browser/download/bubble/download_bubble_controller.h b/chrome/browser/download/bubble/download_bubble_controller.h new file mode 100644 index 0000000..6d5491a --- /dev/null +++ b/chrome/browser/download/bubble/download_bubble_controller.h
@@ -0,0 +1,128 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_ +#define CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_ + +#include "base/scoped_observation.h" +#include "chrome/browser/download/bubble/download_display_controller.h" +#include "chrome/browser/download/offline_item_model.h" +#include "components/download/content/public/all_download_item_notifier.h" +#include "components/offline_items_collection/core/offline_content_aggregator.h" +#include "components/offline_items_collection/core/offline_content_provider.h" +#include "content/public/browser/download_manager.h" + +class Profile; + +using OfflineItemState = ::offline_items_collection::OfflineItemState; +using ContentId = ::offline_items_collection::ContentId; +using OfflineContentProvider = + ::offline_items_collection::OfflineContentProvider; +using OfflineContentAggregator = + ::offline_items_collection::OfflineContentAggregator; +using OfflineItem = ::offline_items_collection::OfflineItem; +using UpdateDelta = ::offline_items_collection::UpdateDelta; +using DownloadUIModelPtr = ::OfflineItemModel::DownloadUIModelPtr; +using OfflineItemList = + ::offline_items_collection::OfflineContentAggregator::OfflineItemList; + +class DownloadBubbleUIController + : public OfflineContentProvider::Observer, + public download::AllDownloadItemNotifier::Observer { + public: + explicit DownloadBubbleUIController(Profile* profile); + DownloadBubbleUIController(const DownloadBubbleUIController&) = delete; + DownloadBubbleUIController& operator=(const DownloadBubbleUIController&) = + delete; + ~DownloadBubbleUIController() override; + + // Get the entries for the main view of the Download Bubble. The main view + // contains all the recent downloads (finished within the last 24 hours). + std::vector<DownloadUIModelPtr> GetMainView(); + + // Get the entries for the partial view of the Download Bubble. The partial + // view contains in-progress and uninteracted downloads, meant to capture the + // user's recent tasks. This can only be opened by the browser in the event of + // new downloads, and user action only creates a main view. + std::vector<DownloadUIModelPtr> GetPartialView(); + + // The list is needed by DownloadDisplayController to check a few things, + // for example in progress download count, last completed time, and getting + // progress for animation. + virtual const OfflineItemList& GetOfflineItems(); + + // This function makes sure that the offline items field is + // populated, and then calls the given callback. After this, GetOfflineItems + // will return a populated list. + virtual void InitOfflineItems(DownloadDisplayController* display_controller, + base::OnceCallback<void()> callback); + + // Remove the entry from Partial view candidates. + void RemoveContentIdFromPartialView(const ContentId& id); + + download::AllDownloadItemNotifier& get_download_notifier_for_testing() { + return download_notifier_; + } + + void set_manager_for_testing(content::DownloadManager* manager) { + download_manager_ = manager; + } + + private: + friend class DownloadBubbleUIControllerTest; + // AllDownloadItemNotifier::Observer + void OnDownloadCreated(content::DownloadManager* manager, + download::DownloadItem* item) override; + void OnDownloadUpdated(content::DownloadManager* manager, + download::DownloadItem* item) override; + void OnDownloadRemoved(content::DownloadManager* manager, + download::DownloadItem* item) override; + void OnManagerGoingDown(content::DownloadManager* manager) override; + + // OfflineContentProvider::Observer + void OnItemsAdded( + const OfflineContentProvider::OfflineItemList& items) override; + void OnItemRemoved(const ContentId& id) override; + void OnItemUpdated(const OfflineItem& item, + const absl::optional<UpdateDelta>& update_delta) override; + void OnContentProviderGoingDown() override; + + // Try to add the items to the set/list(s) and calling callback on completion. + void MaybeAddOfflineItems(base::OnceCallback<void()> callback, + bool is_new, + const OfflineItemList& offline_items); + + // Try to add the new item to the list, returning success status. + bool MaybeAddOfflineItem(const OfflineItem& item, bool is_new); + + // Prune OfflineItems to recent items to in-progress offline items, or + // downloads started in the last day. + void PruneOfflineItems(); + + // Common method for getting main and partial views. + std::vector<DownloadUIModelPtr> GetDownloadUIModels(bool is_main_view); + + raw_ptr<Profile> profile_; + raw_ptr<content::DownloadManager> download_manager_; + download::AllDownloadItemNotifier download_notifier_; + raw_ptr<OfflineContentAggregator> aggregator_; + raw_ptr<OfflineItemModelManager> offline_manager_; + base::ScopedObservation<OfflineContentProvider, + OfflineContentProvider::Observer> + observation_{this}; + // DownloadDisplayController and DownloadBubbleUIController have the same + // lifetime. Both are owned, constructed together, and destructed together by + // DownloadToolbarButtonView. If one is valid, so is the other. + raw_ptr<DownloadDisplayController> display_controller_; + + // Pruned list of offline items. + OfflineItemList offline_items_; + + // set of ids to be shown in partial_view. + std::set<ContentId> partial_view_ids_; + + base::WeakPtrFactory<DownloadBubbleUIController> weak_factory_{this}; +}; + +#endif // CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_
diff --git a/chrome/browser/download/bubble/download_bubble_controller_unittest.cc b/chrome/browser/download/bubble/download_bubble_controller_unittest.cc new file mode 100644 index 0000000..89866e1c --- /dev/null +++ b/chrome/browser/download/bubble/download_bubble_controller_unittest.cc
@@ -0,0 +1,290 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/download/bubble/download_bubble_controller.h" +#include "chrome/browser/download/bubble/download_display.h" +#include "chrome/browser/download/bubble/download_display_controller.h" +#include "chrome/browser/download/bubble/download_icon_state.h" +#include "chrome/browser/download/chrome_download_manager_delegate.h" +#include "chrome/browser/download/download_core_service.h" +#include "chrome/browser/download/download_core_service_factory.h" +#include "chrome/browser/download/download_prefs.h" +#include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h" +#include "chrome/browser/profiles/profile_key.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "components/download/public/common/mock_download_item.h" +#include "components/offline_items_collection/core/offline_item.h" +#include "components/offline_items_collection/core/test_support/mock_offline_content_provider.h" +#include "content/public/browser/download_item_utils.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/mock_download_manager.h" +#include "content/public/test/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::NiceMock; +using testing::Return; +using testing::SetArgPointee; + +namespace { +using StrictMockDownloadItem = testing::StrictMock<download::MockDownloadItem>; +using DownloadIconState = download::DownloadIconState; +using DownloadState = download::DownloadItem::DownloadState; +const char kProviderNamespace[] = "mock_namespace"; + +class MockDownloadDisplayController : public DownloadDisplayController { + public: + MockDownloadDisplayController(Profile* profile, + DownloadBubbleUIController* bubble_controller) + : DownloadDisplayController(nullptr, profile, bubble_controller) {} + void MaybeShowButtonWhenCreated() override {} + MOCK_METHOD1(OnNewItem, void(bool)); + MOCK_METHOD1(OnUpdatedItem, void(bool)); + MOCK_METHOD0(OnRemovedItem, void()); +}; + +} // namespace + +class DownloadBubbleUIControllerTest : public testing::Test { + public: + DownloadBubbleUIControllerTest() + : manager_(std::make_unique<NiceMock<content::MockDownloadManager>>()), + testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {} + DownloadBubbleUIControllerTest(const DownloadBubbleUIControllerTest&) = + delete; + DownloadBubbleUIControllerTest& operator=( + const DownloadBubbleUIControllerTest&) = delete; + + void SetUp() override { + ASSERT_TRUE(testing_profile_manager_.SetUp()); + + profile_ = testing_profile_manager_.CreateTestingProfile("testing_profile"); + EXPECT_CALL(*manager_.get(), GetBrowserContext()) + .WillRepeatedly(Return(profile_)); + + // Set test delegate to get the corresponding download prefs. + auto delegate = std::make_unique<ChromeDownloadManagerDelegate>(profile_); + DownloadCoreServiceFactory::GetForBrowserContext(profile_) + ->SetDownloadManagerDelegateForTesting(std::move(delegate)); + + content_provider_ = std::make_unique< + NiceMock<offline_items_collection::MockOfflineContentProvider>>(); + OfflineContentAggregatorFactory::GetForKey(profile_->GetProfileKey()) + ->RegisterProvider(kProviderNamespace, content_provider_.get()); + + controller_ = std::make_unique<DownloadBubbleUIController>(profile_); + display_controller_ = + std::make_unique<NiceMock<MockDownloadDisplayController>>( + profile_, controller_.get()); + controller_->set_manager_for_testing(manager_.get()); + } + + void TearDown() override { + for (auto& item : items_) { + item->RemoveObserver(&controller_->get_download_notifier_for_testing()); + } + // The controller needs to be reset before download manager, because the + // download_notifier_ will unregister itself from the manager. + controller_.reset(); + } + + protected: + NiceMock<content::MockDownloadManager>& manager() { return *manager_.get(); } + download::MockDownloadItem& item(size_t index) { return *items_[index]; } + NiceMock<MockDownloadDisplayController>& display_controller() { + return *display_controller_; + } + DownloadBubbleUIController& controller() { return *controller_; } + NiceMock<offline_items_collection::MockOfflineContentProvider>& + content_provider() { + return *content_provider_; + } + + void InitDownloadItem(const base::FilePath::CharType* path, + DownloadState state, + std::string& id, + bool is_transient = false, + base::Time start_time = base::Time::Now()) { + size_t index = items_.size(); + items_.push_back(std::make_unique<StrictMockDownloadItem>()); + EXPECT_CALL(item(index), GetId()) + .WillRepeatedly(Return(static_cast<uint32_t>(items_.size() + 1))); + EXPECT_CALL(item(index), GetGuid()).WillRepeatedly(testing::ReturnRef(id)); + EXPECT_CALL(item(index), GetState()).WillRepeatedly(Return(state)); + EXPECT_CALL(item(index), GetStartTime()).WillRepeatedly(Return(start_time)); + int received_bytes = + state == download::DownloadItem::IN_PROGRESS ? 50 : 100; + EXPECT_CALL(item(index), GetReceivedBytes()) + .WillRepeatedly(Return(received_bytes)); + EXPECT_CALL(item(index), GetTotalBytes()).WillRepeatedly(Return(100)); + EXPECT_CALL(item(index), IsDone()).WillRepeatedly(Return(false)); + EXPECT_CALL(item(index), IsTransient()) + .WillRepeatedly(Return(is_transient)); + EXPECT_CALL(item(index), GetDownloadCreationType()) + .WillRepeatedly(Return(download::DownloadItem::DownloadCreationType:: + TYPE_ACTIVE_DOWNLOAD)); + std::vector<download::DownloadItem*> items; + for (size_t i = 0; i < items_.size(); ++i) { + items.push_back(&item(i)); + } + EXPECT_CALL(*manager_.get(), GetAllDownloads(_)) + .WillRepeatedly(SetArgPointee<0>(items)); + item(index).AddObserver(&controller().get_download_notifier_for_testing()); + content::DownloadItemUtils::AttachInfoForTesting(&(item(index)), profile_, + nullptr); + controller().OnDownloadCreated(&manager(), &item(index)); + } + + void UpdateDownloadItem(int item_index, DownloadState state) { + DCHECK_GT(items_.size(), static_cast<size_t>(item_index)); + + EXPECT_CALL(item(item_index), GetState()).WillRepeatedly(Return(state)); + if (state == DownloadState::COMPLETE) { + EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(true)); + DownloadPrefs::FromDownloadManager(&manager()) + ->SetLastCompleteTime(base::Time::Now()); + } else { + EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(false)); + } + item(item_index).NotifyObserversDownloadUpdated(); + } + + void InitOfflineItem(OfflineItemState state, std::string id) { + OfflineItem item; + item.state = state; + item.id.id = id; + offline_items_.push_back(item); + content_provider().NotifyOnItemsAdded({item}); + } + + void UpdateOfflineItem(int item_index, OfflineItemState state) { + offline_items_[item_index].state = state; + UpdateDelta delta; + delta.state_changed = true; + content_provider().NotifyOnItemUpdated(offline_items_[item_index], delta); + } + + content::BrowserTaskEnvironment task_environment_{ + base::test::TaskEnvironment::TimeSource::MOCK_TIME}; + + private: + std::unique_ptr<DownloadBubbleUIController> controller_; + std::unique_ptr<NiceMock<MockDownloadDisplayController>> display_controller_; + std::vector<std::unique_ptr<StrictMockDownloadItem>> items_; + OfflineItemList offline_items_; + std::unique_ptr<NiceMock<content::MockDownloadManager>> manager_; + TestingProfileManager testing_profile_manager_; + std::unique_ptr< + NiceMock<offline_items_collection::MockOfflineContentProvider>> + content_provider_; + Profile* profile_; +}; + +TEST_F(DownloadBubbleUIControllerTest, ProcessesNewItems) { + std::vector<std::string> ids = {"Download 1", "Download 2", "Offline 1"}; + EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), + download::DownloadItem::IN_PROGRESS, ids[0]); + EXPECT_CALL(display_controller(), OnNewItem(false)).Times(1); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"), + download::DownloadItem::COMPLETE, ids[1]); + EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); + InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[2]); +} + +TEST_F(DownloadBubbleUIControllerTest, ProcessesUpdatedItems) { + std::vector<std::string> ids = {"Download 1", "Offline 1"}; + EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), + download::DownloadItem::IN_PROGRESS, ids[0]); + EXPECT_CALL(display_controller(), OnUpdatedItem(false)).Times(1); + UpdateDownloadItem(/*item_index=*/0, DownloadState::IN_PROGRESS); + EXPECT_CALL(display_controller(), OnUpdatedItem(true)).Times(1); + UpdateDownloadItem(/*item_index=*/0, DownloadState::COMPLETE); + + EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); + InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[1]); + EXPECT_CALL(display_controller(), OnUpdatedItem(true)).Times(1); + UpdateOfflineItem(/*item_index=*/0, OfflineItemState::COMPLETE); +} + +TEST_F(DownloadBubbleUIControllerTest, TransientDownloadShouldNotShow) { + std::vector<std::string> ids = {"Download 1", "Download 2"}; + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), + download::DownloadItem::IN_PROGRESS, ids[0], + /*is_transient=*/true); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"), + download::DownloadItem::IN_PROGRESS, ids[1], + /*is_transient=*/false); + std::vector<DownloadUIModelPtr> models = controller().GetMainView(); + EXPECT_EQ(models.size(), 1ul); + EXPECT_EQ(models[0]->GetContentId().id, ids[1]); +} + +TEST_F(DownloadBubbleUIControllerTest, ListIsSorted) { + std::vector<std::string> ids = {"Download 1", "Download 2", "Download 3", + "Offline 1"}; + std::vector<base::TimeDelta> start_time_offsets = { + base::Hours(1), base::Hours(4), base::Hours(2)}; + std::vector<std::string> sorted_ids = {"Offline 1", "Download 1", + "Download 3", "Download 2"}; + base::Time now = base::Time::Now(); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), + download::DownloadItem::IN_PROGRESS, ids[0], + /*is_transient=*/false, now - start_time_offsets[0]); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"), + download::DownloadItem::IN_PROGRESS, ids[1], + /*is_transient=*/false, now - start_time_offsets[1]); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar3.pdf"), + download::DownloadItem::IN_PROGRESS, ids[2], + /*is_transient=*/false, now - start_time_offsets[2]); + InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[3]); + std::vector<DownloadUIModelPtr> models = controller().GetMainView(); + EXPECT_EQ(models.size(), 4ul); + for (unsigned long i = 0; i < models.size(); i++) { + EXPECT_EQ(models[i]->GetContentId().id, sorted_ids[i]); + } +} + +TEST_F(DownloadBubbleUIControllerTest, ListIsRecent) { + std::vector<std::string> ids = {"Download 1", "Download 2", "Download 3", + "Offline 1"}; + std::vector<base::TimeDelta> start_time_offsets = { + base::Hours(1), base::Hours(25), base::Hours(2)}; + std::vector<std::string> sorted_ids = {"Offline 1", "Download 1", + "Download 3"}; + base::Time now = base::Time::Now(); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), + download::DownloadItem::IN_PROGRESS, ids[0], + /*is_transient=*/false, now - start_time_offsets[0]); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"), + download::DownloadItem::IN_PROGRESS, ids[1], + /*is_transient=*/false, now - start_time_offsets[1]); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar3.pdf"), + download::DownloadItem::IN_PROGRESS, ids[2], + /*is_transient=*/false, now - start_time_offsets[2]); + InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[3]); + std::vector<DownloadUIModelPtr> models = controller().GetMainView(); + EXPECT_EQ(models.size(), 3ul); + for (unsigned long i = 0; i < models.size(); i++) { + EXPECT_EQ(models[i]->GetContentId().id, sorted_ids[i]); + } +} + +TEST_F(DownloadBubbleUIControllerTest, + OpeningMainViewRemovesEntryFromPartialView) { + std::vector<std::string> ids = {"Download 1", "Offline 1"}; + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), + download::DownloadItem::IN_PROGRESS, ids[0]); + InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[1]); + std::vector<DownloadUIModelPtr> partial_view = controller().GetPartialView(); + EXPECT_EQ(partial_view.size(), 2ul); + std::vector<DownloadUIModelPtr> main_view = controller().GetMainView(); + EXPECT_EQ(main_view.size(), 2ul); + std::vector<DownloadUIModelPtr> partial_view_empty = + controller().GetPartialView(); + EXPECT_EQ(partial_view_empty.size(), 0ul); +}
diff --git a/chrome/browser/download/bubble/download_bubble_prefs.cc b/chrome/browser/download/bubble/download_bubble_prefs.cc new file mode 100644 index 0000000..055b1eb3 --- /dev/null +++ b/chrome/browser/download/bubble/download_bubble_prefs.cc
@@ -0,0 +1,50 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/download/bubble/download_bubble_prefs.h" + +#include "base/feature_list.h" +#include "chrome/browser/enterprise/connectors/connectors_service.h" +#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h" +#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h" +#include "components/safe_browsing/core/common/features.h" +#include "components/safe_browsing/core/common/safe_browsing_prefs.h" + +namespace download { + +bool IsDownloadBubbleEnabled(Profile* profile) { + if (!base::FeatureList::IsEnabled(safe_browsing::kDownloadBubble)) { + return false; + } + + // TODO(crbug.com/1307021): Enable download bubble for enhanced protection + // users, advanced protection users and enterprise connector users once it + // supports deep scanning. + if (safe_browsing::IsEnhancedProtectionEnabled(*profile->GetPrefs())) { + return false; + } + + auto* advanced_protection_manager = + safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile( + profile); + if (advanced_protection_manager && + advanced_protection_manager->IsUnderAdvancedProtection()) { + return false; + } + + auto* connector_service = + enterprise_connectors::ConnectorsServiceFactory::GetForBrowserContext( + profile); + if (connector_service && + connector_service->IsConnectorEnabled( + enterprise_connectors::AnalysisConnector::FILE_DOWNLOADED)) { + return false; + } + + // TODO(crbug.com/1307021): Create an enterprise policy DownloadBubbleEnabled + // and check here. + return true; +} + +} // namespace download
diff --git a/chrome/browser/download/bubble/download_bubble_prefs.h b/chrome/browser/download/bubble/download_bubble_prefs.h new file mode 100644 index 0000000..f424d5d --- /dev/null +++ b/chrome/browser/download/bubble/download_bubble_prefs.h
@@ -0,0 +1,16 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_PREFS_H_ +#define CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_PREFS_H_ + +#include "chrome/browser/profiles/profile.h" + +namespace download { + +bool IsDownloadBubbleEnabled(Profile* profile); + +} // namespace download + +#endif // CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_PREFS_H_
diff --git a/chrome/browser/download/bubble/download_bubble_prefs_unittest.cc b/chrome/browser/download/bubble/download_bubble_prefs_unittest.cc new file mode 100644 index 0000000..e76f9ef --- /dev/null +++ b/chrome/browser/download/bubble/download_bubble_prefs_unittest.cc
@@ -0,0 +1,68 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/download/bubble/download_bubble_prefs.h" + +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h" +#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "components/safe_browsing/core/common/features.h" +#include "components/safe_browsing/core/common/safe_browsing_prefs.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace download { + +class DownloadBubblePrefsTest : public testing::Test { + public: + DownloadBubblePrefsTest() + : testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {} + DownloadBubblePrefsTest(const DownloadBubblePrefsTest&) = delete; + DownloadBubblePrefsTest& operator=(const DownloadBubblePrefsTest&) = delete; + + void SetUp() override { + ASSERT_TRUE(testing_profile_manager_.SetUp()); + + profile_ = testing_profile_manager_.CreateTestingProfile("testing_profile"); + } + + protected: + Profile* profile_; + base::test::ScopedFeatureList feature_list_; + + private: + content::BrowserTaskEnvironment task_environment_; + TestingProfileManager testing_profile_manager_; +}; + +TEST_F(DownloadBubblePrefsTest, FeatureFlagEnabled) { + feature_list_.InitAndEnableFeature(safe_browsing::kDownloadBubble); + profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false); + EXPECT_TRUE(IsDownloadBubbleEnabled(profile_)); +} + +TEST_F(DownloadBubblePrefsTest, FeatureFlagDisabled) { + feature_list_.InitAndDisableFeature(safe_browsing::kDownloadBubble); + profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false); + EXPECT_FALSE(IsDownloadBubbleEnabled(profile_)); +} + +TEST_F(DownloadBubblePrefsTest, EnhancedProtectionEnabled) { + feature_list_.InitAndEnableFeature(safe_browsing::kDownloadBubble); + profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, true); + EXPECT_FALSE(IsDownloadBubbleEnabled(profile_)); +} + +TEST_F(DownloadBubblePrefsTest, AdvancedProtectionEnabled) { + feature_list_.InitAndEnableFeature(safe_browsing::kDownloadBubble); + profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false); + safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(profile_) + ->SetAdvancedProtectionStatusForTesting(true); + EXPECT_FALSE(IsDownloadBubbleEnabled(profile_)); +} + +} // namespace download
diff --git a/chrome/browser/download/bubble/download_display_controller.cc b/chrome/browser/download/bubble/download_display_controller.cc index 46d16ffa..d59fa8b 100644 --- a/chrome/browser/download/bubble/download_display_controller.cc +++ b/chrome/browser/download/bubble/download_display_controller.cc
@@ -6,10 +6,13 @@ #include "base/numerics/safe_conversions.h" #include "base/time/time.h" #include "base/timer/timer.h" +#include "chrome/browser/download/bubble/download_bubble_controller.h" #include "chrome/browser/download/bubble/download_display.h" #include "chrome/browser/download/bubble/download_icon_state.h" #include "chrome/browser/download/download_item_model.h" #include "chrome/browser/download/download_prefs.h" +#include "components/offline_items_collection/core/offline_item.h" +#include "components/offline_items_collection/core/offline_item_state.h" namespace { @@ -26,36 +29,39 @@ DownloadDisplayController::DownloadDisplayController( DownloadDisplay* display, - content::DownloadManager* download_manager) + Profile* profile, + DownloadBubbleUIController* bubble_controller) : display_(display), - download_manager_(download_manager), - download_notifier_(download_manager, this) { - MaybeShowButtonWhenCreated(); + download_manager_(profile->GetDownloadManager()), + download_notifier_(download_manager_, this), + bubble_controller_(bubble_controller) { + bubble_controller_->InitOfflineItems( + this, + base::BindOnce(&DownloadDisplayController::MaybeShowButtonWhenCreated, + weak_factory_.GetWeakPtr())); } DownloadDisplayController::~DownloadDisplayController() = default; -void DownloadDisplayController::OnDownloadCreated( - content::DownloadManager* manager, - download::DownloadItem* item) { +void DownloadDisplayController::OnNewItem(bool in_progress) { UpdateToolbarButtonState(); // Only show details if the created download is in progress. - if (item->GetState() == download::DownloadItem::IN_PROGRESS) { + if (in_progress) { display_->ShowDetails(); } } -void DownloadDisplayController::OnDownloadUpdated( - content::DownloadManager* manager, - download::DownloadItem* item) { - DownloadItemModel item_model(item); - - if (item_model.IsDone()) { +void DownloadDisplayController::OnUpdatedItem(bool is_done) { + if (is_done) { ScheduleToolbarDisappearance(kToolbarIconVisibilityTimeInterval); } UpdateToolbarButtonState(); } +void DownloadDisplayController::OnRemovedItem() { + UpdateToolbarButtonState(); +} + void DownloadDisplayController::OnManagerGoingDown( content::DownloadManager* manager) { if (download_manager_ == manager) { @@ -86,21 +92,27 @@ } void DownloadDisplayController::UpdateToolbarButtonState() { - if (download_manager_->InProgressCount() > 0) { + const auto& offline_items = bubble_controller_->GetOfflineItems(); + int in_progress_count = download_manager_->InProgressCount(); + for (const auto& offline_item : offline_items) { + in_progress_count += (offline_item.state == OfflineItemState::IN_PROGRESS); + } + if (in_progress_count > 0) { ShowToolbarButton(); icon_info_.icon_state = DownloadIconState::kProgress; icon_info_.is_active = true; + display_->UpdateDownloadIcon(); } else { icon_info_.icon_state = DownloadIconState::kComplete; - if (HasRecentCompleteDownload(kToolbarIconActiveTimeInterval)) { + if (HasRecentCompleteDownload(kToolbarIconActiveTimeInterval, + GetLastCompleteTime(offline_items))) { icon_info_.is_active = true; ScheduleToolbarInactive(kToolbarIconActiveTimeInterval); } else { icon_info_.is_active = false; } + display_->UpdateDownloadIcon(); } - - display_->UpdateDownloadIcon(); } void DownloadDisplayController::UpdateDownloadIconToInactive() { @@ -123,31 +135,41 @@ &DownloadDisplayController::UpdateDownloadIconToInactive); } +base::Time DownloadDisplayController::GetLastCompleteTime( + const offline_items_collection::OfflineContentAggregator::OfflineItemList& + offline_items) { + base::Time last_time = DownloadPrefs::FromDownloadManager(download_manager_) + ->GetLastCompleteTime(); + for (const auto& offline_item : offline_items) { + if (last_time < offline_item.completion_time) + last_time = offline_item.completion_time; + } + return last_time; +} + void DownloadDisplayController::MaybeShowButtonWhenCreated() { - if (!HasRecentCompleteDownload(kToolbarIconVisibilityTimeInterval)) { + base::Time last_complete_time = + GetLastCompleteTime(bubble_controller_->GetOfflineItems()); + if (!HasRecentCompleteDownload(kToolbarIconVisibilityTimeInterval, + last_complete_time)) { return; } // If the last download complete time is less than - // `kToolbarIconVisibilityTimeInterval` ago, show the button immediately. + // `kToolbarIconVisibilityTimeInterval` ago, show the button + // immediately. ShowToolbarButton(); icon_info_.icon_state = DownloadIconState::kComplete; // The initial state should be inactive, because there is no active // download. icon_info_.is_active = false; display_->UpdateDownloadIcon(); - - base::TimeDelta time_since_last_completion = - base::Time::Now() - DownloadPrefs::FromDownloadManager(download_manager_) - ->GetLastCompleteTime(); ScheduleToolbarDisappearance(kToolbarIconVisibilityTimeInterval - - time_since_last_completion); + (base::Time::Now() - last_complete_time)); } bool DownloadDisplayController::HasRecentCompleteDownload( - base::TimeDelta interval) { - base::Time last_complete_time = - DownloadPrefs::FromDownloadManager(download_manager_) - ->GetLastCompleteTime(); + base::TimeDelta interval, + base::Time last_complete_time) { base::Time current_time = base::Time::Now(); base::TimeDelta time_since_last_completion = current_time - last_complete_time; @@ -164,7 +186,6 @@ DownloadDisplayController::ProgressInfo DownloadDisplayController::GetProgress() { DownloadDisplayController::ProgressInfo progress_info; - int64_t received_bytes = 0; int64_t total_bytes = 0; @@ -183,6 +204,19 @@ } } + for (const auto& item : bubble_controller_->GetOfflineItems()) { + if (item.state == OfflineItemState::IN_PROGRESS) { + ++progress_info.download_count; + if (item.total_size_bytes <= 0) { + // There may or may not be more data coming down this pipe. + progress_info.progress_certain = false; + } else { + received_bytes += item.received_bytes; + total_bytes += item.total_size_bytes; + } + } + } + if (total_bytes > 0) { progress_info.progress_percentage = base::ClampFloor(received_bytes * 100.0 / total_bytes);
diff --git a/chrome/browser/download/bubble/download_display_controller.h b/chrome/browser/download/bubble/download_display_controller.h index 53c535b..92cbd16 100644 --- a/chrome/browser/download/bubble/download_display_controller.h +++ b/chrome/browser/download/bubble/download_display_controller.h
@@ -7,12 +7,19 @@ #include "base/timer/timer.h" #include "chrome/browser/download/bubble/download_icon_state.h" +#include "chrome/browser/download/offline_item_model.h" #include "components/download/content/public/all_download_item_notifier.h" +#include "components/offline_items_collection/core/offline_content_aggregator.h" +#include "components/offline_items_collection/core/offline_content_provider.h" namespace content { class DownloadManager; } // namespace content +class Profile; +class DownloadBubbleUIController; +using DownloadUIModelPtr = ::OfflineItemModel::DownloadUIModelPtr; + namespace base { class TimeDelta; class OneShotTimer; @@ -20,11 +27,16 @@ class DownloadDisplay; +// Used to control the DownloadToolbar Button, through the DownloadDisplay +// interface. Supports both regular Download and Offline items. When in the +// future OfflineItems include regular Download on Desktop platforms, +// we can remove AllDownloadItemNotifier::Observer. class DownloadDisplayController : public download::AllDownloadItemNotifier::Observer { public: DownloadDisplayController(DownloadDisplay* display, - content::DownloadManager* download_manager); + Profile* profile, + DownloadBubbleUIController* bubble_controller); DownloadDisplayController(const DownloadDisplayController&) = delete; DownloadDisplayController& operator=(const DownloadDisplayController&) = delete; @@ -49,16 +61,33 @@ // // This implementation will match the one in download_status_updater.cc ProgressInfo GetProgress(); + // Returns an IconInfo that contains current state of the icon. IconInfo GetIconInfo(); // Notifies the controller that the button is pressed. Called by `display_`. void OnButtonPressed(); + // Common methods for new downloads or new offline items. + // Called from bubble controller when new item(s) are added, with + // |in_progress| as argument for if any was in progress. + // These methods are virtual so that they can be overridden for fake + // controllers in testing. + virtual void OnNewItem(bool in_progress); + // Called from bubble controller when an item is updated, with |is_done| + // indicating if it was marked done. + virtual void OnUpdatedItem(bool is_done); + // Called from bubble controller when an item is deleted. + virtual void OnRemovedItem(); + download::AllDownloadItemNotifier& get_download_notifier_for_testing() { return download_notifier_; } + void set_manager_for_testing(content::DownloadManager* manager) { + download_manager_ = manager; + } + private: friend class DownloadDisplayControllerTest; @@ -83,24 +112,31 @@ void UpdateDownloadIconToInactive(); // Decides whether the toolbar button should be shown when it is created. - void MaybeShowButtonWhenCreated(); + virtual void MaybeShowButtonWhenCreated(); // Whether the last download complete time is less than `interval` ago. - bool HasRecentCompleteDownload(base::TimeDelta interval); + bool HasRecentCompleteDownload(base::TimeDelta interval, + base::Time last_complete_time); // AllDownloadItemNotifier::Observer - void OnDownloadCreated(content::DownloadManager* manager, - download::DownloadItem* item) override; - void OnDownloadUpdated(content::DownloadManager* manager, - download::DownloadItem* item) override; void OnManagerGoingDown(content::DownloadManager* manager) override; + base::Time GetLastCompleteTime( + const offline_items_collection::OfflineContentAggregator::OfflineItemList& + offline_items); + // The pointer is created in ToolbarView and owned by ToolbarView. - DownloadDisplay* const display_; - content::DownloadManager* download_manager_; + raw_ptr<DownloadDisplay> const display_; + raw_ptr<content::DownloadManager> download_manager_; download::AllDownloadItemNotifier download_notifier_; base::OneShotTimer icon_disappearance_timer_; base::OneShotTimer icon_inactive_timer_; IconInfo icon_info_; + // DownloadDisplayController and DownloadBubbleUIController have the same + // lifetime. Both are owned, constructed together, and destructed together by + // DownloadToolbarButtonView. If one is valid, so is the other. + raw_ptr<DownloadBubbleUIController> bubble_controller_; + + base::WeakPtrFactory<DownloadDisplayController> weak_factory_{this}; }; #endif // CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_DISPLAY_CONTROLLER_H_
diff --git a/chrome/browser/download/bubble/download_display_controller_unittest.cc b/chrome/browser/download/bubble/download_display_controller_unittest.cc index 707e34e9..e4e3545 100644 --- a/chrome/browser/download/bubble/download_display_controller_unittest.cc +++ b/chrome/browser/download/bubble/download_display_controller_unittest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "chrome/browser/download/bubble/download_display_controller.h" +#include "chrome/browser/download/bubble/download_bubble_controller.h" #include "chrome/browser/download/bubble/download_display.h" #include "chrome/browser/download/bubble/download_icon_state.h" #include "chrome/browser/download/chrome_download_manager_delegate.h" @@ -13,6 +14,7 @@ #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h" #include "components/download/public/common/mock_download_item.h" +#include "components/offline_items_collection/core/offline_item.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/mock_download_manager.h" #include "content/public/test/test_utils.h" @@ -27,6 +29,7 @@ using StrictMockDownloadItem = testing::StrictMock<download::MockDownloadItem>; using DownloadIconState = download::DownloadIconState; using DownloadState = download::DownloadItem::DownloadState; +using OfflineItemState = offline_items_collection::OfflineItemState; class FakeDownloadDisplay : public DownloadDisplay { public: @@ -73,6 +76,25 @@ DownloadDisplayController* controller_ = nullptr; }; +class FakeDownloadBubbleUIController : public DownloadBubbleUIController { + public: + explicit FakeDownloadBubbleUIController(Profile* profile) + : DownloadBubbleUIController(profile) {} + ~FakeDownloadBubbleUIController() override = default; + const OfflineItemList& GetOfflineItems() override { return offline_items_; } + void InitOfflineItems(DownloadDisplayController* display_controller, + base::OnceCallback<void()> callback) override { + std::move(callback).Run(); + } + void AddOfflineItem(OfflineItem& item) { offline_items_.push_back(item); } + void UpdateOfflineItem(int index, OfflineItemState state) { + offline_items_[index].state = state; + } + + protected: + OfflineItemList offline_items_; +}; + } // namespace class DownloadDisplayControllerTest : public testing::Test { @@ -87,19 +109,21 @@ void SetUp() override { ASSERT_TRUE(testing_profile_manager_.SetUp()); - Profile* profile = - testing_profile_manager_.CreateTestingProfile("testing_profile"); + profile_ = testing_profile_manager_.CreateTestingProfile("testing_profile"); EXPECT_CALL(*manager_.get(), GetBrowserContext()) - .WillRepeatedly(Return(profile)); + .WillRepeatedly(Return(profile_)); // Set test delegate to get the corresponding download prefs. - auto delegate = std::make_unique<ChromeDownloadManagerDelegate>(profile); - DownloadCoreServiceFactory::GetForBrowserContext(profile) + auto delegate = std::make_unique<ChromeDownloadManagerDelegate>(profile_); + DownloadCoreServiceFactory::GetForBrowserContext(profile_) ->SetDownloadManagerDelegateForTesting(std::move(delegate)); display_ = std::make_unique<FakeDownloadDisplay>(); - controller_ = std::make_unique<DownloadDisplayController>(display_.get(), - manager_.get()); + bubble_controller_ = + std::make_unique<FakeDownloadBubbleUIController>(profile_); + controller_ = std::make_unique<DownloadDisplayController>( + display_.get(), profile_, bubble_controller_.get()); + controller_->set_manager_for_testing(manager_.get()); display_->SetController(controller_.get()); } @@ -117,6 +141,10 @@ download::MockDownloadItem& item(size_t index) { return *items_[index]; } FakeDownloadDisplay& display() { return *display_; } DownloadDisplayController& controller() { return *controller_; } + FakeDownloadBubbleUIController& bubble_controller() { + return *bubble_controller_; + } + Profile* profile() { return profile_; } void InitDownloadItem(const base::FilePath::CharType* path, DownloadState state) { @@ -144,7 +172,21 @@ EXPECT_CALL(*manager_.get(), GetAllDownloads(_)) .WillRepeatedly(SetArgPointee<0>(items)); item(index).AddObserver(&controller().get_download_notifier_for_testing()); - controller().OnDownloadCreated(&manager(), &item(index)); + controller().OnNewItem(state == download::DownloadItem::IN_PROGRESS); + } + + void InitOfflineItem(OfflineItemState state) { + OfflineItem item; + item.state = state; + bubble_controller().AddOfflineItem(item); + controller().OnNewItem(state == OfflineItemState::IN_PROGRESS); + } + + void UpdateOfflineItem(int item_index, OfflineItemState state) { + if (state == OfflineItemState::COMPLETE) { + bubble_controller().UpdateOfflineItem(item_index, state); + } + controller().OnUpdatedItem(state == OfflineItemState::COMPLETE); } void UpdateDownloadItem(int item_index, DownloadState state) { @@ -161,7 +203,7 @@ } else { EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(false)); } - item(item_index).NotifyObserversDownloadUpdated(); + controller().OnUpdatedItem(state == DownloadState::COMPLETE); } bool VerifyDisplayState(bool shown, @@ -203,8 +245,11 @@ std::unique_ptr<DownloadDisplayController> controller_; std::unique_ptr<FakeDownloadDisplay> display_; std::vector<std::unique_ptr<StrictMockDownloadItem>> items_; + std::unique_ptr<NiceMock<content::MockDownloadManager>> manager_; + std::unique_ptr<FakeDownloadBubbleUIController> bubble_controller_; TestingProfileManager testing_profile_manager_; + Profile* profile_; }; TEST_F(DownloadDisplayControllerTest, GetProgressItemsInProgress) { @@ -220,6 +265,22 @@ EXPECT_EQ(progress.progress_percentage, 50); } +TEST_F(DownloadDisplayControllerTest, OfflineItemsUncertainProgress) { + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), + download::DownloadItem::IN_PROGRESS); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"), + download::DownloadItem::COMPLETE); + InitDownloadItem(FILE_PATH_LITERAL("/foo/bar4.pdf"), + download::DownloadItem::IN_PROGRESS); + // This offline item has uncertain progress + InitOfflineItem(OfflineItemState::IN_PROGRESS); + DownloadDisplayController::ProgressInfo progress = controller().GetProgress(); + + EXPECT_EQ(progress.download_count, 3); + EXPECT_EQ(progress.progress_percentage, 50); + EXPECT_FALSE(progress.progress_certain); +} + TEST_F(DownloadDisplayControllerTest, GetProgressItemsAllComplete) { InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), download::DownloadItem::COMPLETE); @@ -306,6 +367,17 @@ EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/false, /*icon_state=*/DownloadIconState::kComplete, /*is_active=*/true)); + + InitOfflineItem(OfflineItemState::IN_PROGRESS); + EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/true, + /*icon_state=*/DownloadIconState::kProgress, + /*is_active=*/true)); + display().SetDetailsShown(false); + + UpdateOfflineItem(/*item_index=*/0, OfflineItemState::COMPLETE); + EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/false, + /*icon_state=*/DownloadIconState::kComplete, + /*is_active=*/true)); } TEST_F(DownloadDisplayControllerTest, @@ -328,7 +400,8 @@ DownloadPrefs::FromDownloadManager(&manager()) ->SetLastCompleteTime(current_time - base::Hours(25)); - DownloadDisplayController controller(&display(), &manager()); + DownloadDisplayController controller(&display(), profile(), + &bubble_controller()); EXPECT_TRUE(VerifyDisplayState(/*shown=*/false, /*detail_shown=*/false, /*icon_state=*/DownloadIconState::kComplete, /*is_active=*/false)); @@ -340,7 +413,8 @@ DownloadPrefs::FromDownloadManager(&manager()) ->SetLastCompleteTime(current_time - base::Hours(23)); - DownloadDisplayController controller(&display(), &manager()); + DownloadDisplayController controller(&display(), profile(), + &bubble_controller()); // The initial state should not display details. EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/false, /*icon_state=*/DownloadIconState::kComplete, @@ -355,7 +429,8 @@ } TEST_F(DownloadDisplayControllerTest, InitialState_NoLastDownload) { - DownloadDisplayController controller(&display(), &manager()); + DownloadDisplayController controller(&display(), profile(), + &bubble_controller()); EXPECT_TRUE(VerifyDisplayState(/*shown=*/false, /*detail_shown=*/false, /*icon_state=*/DownloadIconState::kComplete, /*is_active=*/false));
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc index a53f074..0006dda 100644 --- a/chrome/browser/download/chrome_download_manager_delegate.cc +++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -299,8 +299,8 @@ std::move(callback).Run( target_info->target_path, target_info->target_disposition, target_info->danger_type, target_info->mixed_content_status, - target_info->intermediate_path, std::move(target_info->download_schedule), - target_info->result); + target_info->intermediate_path, target_info->display_name, + std::move(target_info->download_schedule), target_info->result); } #if BUILDFLAG(IS_ANDROID) @@ -313,13 +313,13 @@ bool should_download) { // If the download should be blocked, we can call the callback directly. if (!should_download) { - std::move(callback).Run(target_info->target_path, - target_info->target_disposition, - download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, - DownloadItem::MixedContentStatus::SILENT_BLOCK, - target_info->intermediate_path, - std::move(target_info->download_schedule), - download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED); + std::move(callback).Run( + target_info->target_path, target_info->target_disposition, + download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DownloadItem::MixedContentStatus::SILENT_BLOCK, + target_info->intermediate_path, target_info->display_name, + std::move(target_info->download_schedule), + download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED); return; } target_info->mixed_content_status = @@ -1241,9 +1241,9 @@ void ChromeDownloadManagerDelegate::DetermineLocalPath( DownloadItem* download, const base::FilePath& virtual_path, - DownloadTargetDeterminerDelegate::LocalPathCallback callback) { + download::LocalPathCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - std::move(callback).Run(virtual_path); + download::DetermineLocalPath(download, virtual_path, std::move(callback)); } void ChromeDownloadManagerDelegate::CheckDownloadUrl(
diff --git a/chrome/browser/download/chrome_download_manager_delegate.h b/chrome/browser/download/chrome_download_manager_delegate.h index dffd12e..b4871ec 100644 --- a/chrome/browser/download/chrome_download_manager_delegate.h +++ b/chrome/browser/download/chrome_download_manager_delegate.h
@@ -215,7 +215,7 @@ ConfirmationCallback callback) override; void DetermineLocalPath(download::DownloadItem* download, const base::FilePath& virtual_path, - LocalPathCallback callback) override; + download::LocalPathCallback callback) override; void CheckDownloadUrl(download::DownloadItem* download, const base::FilePath& suggested_virtual_path, CheckDownloadUrlCallback callback) override;
diff --git a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc index 4309fcca..0734db4 100644 --- a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc +++ b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
@@ -112,23 +112,20 @@ // Struct for holding the result of calling DetermineDownloadTarget. struct DetermineDownloadTargetResult { - DetermineDownloadTargetResult(); - base::FilePath target_path; - download::DownloadItem::TargetDisposition disposition; - download::DownloadDangerType danger_type; - download::DownloadItem::MixedContentStatus mixed_content_status; + download::DownloadItem::TargetDisposition disposition = + download::DownloadItem::TARGET_DISPOSITION_OVERWRITE; + download::DownloadDangerType danger_type = + download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS; + download::DownloadItem::MixedContentStatus mixed_content_status = + download::DownloadItem::MixedContentStatus::UNKNOWN; base::FilePath intermediate_path; - download::DownloadInterruptReason interrupt_reason; + base::FilePath display_name; + download::DownloadInterruptReason interrupt_reason = + download::DOWNLOAD_INTERRUPT_REASON_NONE; absl::optional<download::DownloadSchedule> download_schedule; }; -DetermineDownloadTargetResult::DetermineDownloadTargetResult() - : disposition(download::DownloadItem::TARGET_DISPOSITION_OVERWRITE), - danger_type(download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS), - mixed_content_status(download::DownloadItem::MixedContentStatus::UNKNOWN), - interrupt_reason(download::DOWNLOAD_INTERRUPT_REASON_NONE) {} - // Subclass of the ChromeDownloadManagerDelegate that replaces a few interaction // points for ease of testing. class TestChromeDownloadManagerDelegate : public ChromeDownloadManagerDelegate { @@ -236,6 +233,12 @@ const base::FilePath& path, DownloadTargetDeterminerDelegate::ConfirmationCallback) override {} + void DetermineLocalPath(download::DownloadItem* download, + const base::FilePath& virtual_path, + download::LocalPathCallback callback) override { + std::move(callback).Run(virtual_path, virtual_path.BaseName()); + } + private: friend class ChromeDownloadManagerDelegateTest; }; @@ -407,6 +410,7 @@ download::DownloadDangerType danger_type, download::DownloadItem::MixedContentStatus mixed_content_status, const base::FilePath& intermediate_path, + const base::FilePath& display_name, absl::optional<download::DownloadSchedule> download_schedule, download::DownloadInterruptReason interrupt_reason) { result->target_path = target_path; @@ -414,6 +418,7 @@ result->danger_type = danger_type; result->mixed_content_status = mixed_content_status; result->intermediate_path = intermediate_path; + result->display_name = display_name; result->interrupt_reason = interrupt_reason; result->download_schedule = std::move(download_schedule); quit_runloop.Run();
diff --git a/chrome/browser/download/download_item_model.cc b/chrome/browser/download/download_item_model.cc index 98fc8a5..de2ab5d 100644 --- a/chrome/browser/download/download_item_model.cc +++ b/chrome/browser/download/download_item_model.cc
@@ -154,9 +154,7 @@ } DownloadItemModel::DownloadItemModel(DownloadItem* download) - : download_(download) { - download_->AddObserver(this); -} + : DownloadItemModel(download, std::make_unique<StatusTextBuilder>()) {} DownloadItemModel::DownloadItemModel( download::DownloadItem* download, @@ -511,6 +509,10 @@ return download_->TimeRemaining(remaining); } +base::Time DownloadItemModel::GetStartTime() const { + return download_->GetStartTime(); +} + base::Time DownloadItemModel::GetEndTime() const { return download_->GetEndTime(); }
diff --git a/chrome/browser/download/download_item_model.h b/chrome/browser/download/download_item_model.h index 6de605e..d7bdc6b 100644 --- a/chrome/browser/download/download_item_model.h +++ b/chrome/browser/download/download_item_model.h
@@ -84,6 +84,7 @@ bool GetOpenWhenComplete() const override; bool IsOpenWhenCompleteByPolicy() const override; bool TimeRemaining(base::TimeDelta* remaining) const override; + base::Time GetStartTime() const override; base::Time GetEndTime() const override; bool GetOpened() const override; void SetOpened(bool opened) override;
diff --git a/chrome/browser/download/download_shelf_controller.cc b/chrome/browser/download/download_shelf_controller.cc index 99825e5..042cebe 100644 --- a/chrome/browser/download/download_shelf_controller.cc +++ b/chrome/browser/download/download_shelf_controller.cc
@@ -80,7 +80,7 @@ DownloadUIModel::DownloadUIModelPtr model) { Browser* browser = chrome::FindLastActiveWithProfile(profile_); - if (browser && browser->window()) { + if (browser && browser->window() && browser->window()->GetDownloadShelf()) { // Add the offline item to DownloadShelf in the browser window. browser->window()->GetDownloadShelf()->AddDownload(std::move(model)); }
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc index ec2bca8..0d144ef2 100644 --- a/chrome/browser/download/download_target_determiner.cc +++ b/chrome/browser/download/download_target_determiner.cc
@@ -603,7 +603,8 @@ } void DownloadTargetDeterminer::DetermineLocalPathDone( - const base::FilePath& local_path) { + const base::FilePath& local_path, + const base::FilePath& file_name) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe(); if (local_path.empty()) { @@ -619,6 +620,14 @@ DCHECK_EQ(STATE_DETERMINE_MIME_TYPE, next_state_); local_path_ = local_path; +#if BUILDFLAG(IS_ANDROID) + // If the |local path_| is a content Uri while the |virtual_path_| is a + // canonical path, replace the file name with the new name we got from + // the system so safebrowsing can check file extensions properly. + if (local_path_.IsContentUri() && !virtual_path_.IsContentUri()) { + virtual_path_ = virtual_path_.DirName().Append(file_name); + } +#endif // BUILDFLAG(IS_ANDROID) DoLoop(); } @@ -905,7 +914,6 @@ // If the local path is a content URI, the download should be from resumption // and we can just use the current path. if (local_path_.IsContentUri()) { - DCHECK(is_resumption_); intermediate_path_ = local_path_; return COMPLETE; } @@ -1004,6 +1012,12 @@ target_info->is_filetype_handled_safely = is_filetype_handled_safely_; target_info->mixed_content_status = mixed_content_status_; target_info->download_schedule = std::move(download_schedule_); +#if BUILDFLAG(IS_ANDROID) + // If |virtual_path_| is content URI, there is no need to prompt the user. + if (local_path_.IsContentUri() && !virtual_path_.IsContentUri()) { + target_info->display_name = virtual_path_.BaseName(); + } +#endif base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE,
diff --git a/chrome/browser/download/download_target_determiner.h b/chrome/browser/download/download_target_determiner.h index a983719..eb7b04c 100644 --- a/chrome/browser/download/download_target_determiner.h +++ b/chrome/browser/download/download_target_determiner.h
@@ -223,8 +223,12 @@ // - STATE_DETERMINE_MIME_TYPE. Result DoDetermineLocalPath(); - // Callback invoked when the delegate has determined local path. - void DetermineLocalPathDone(const base::FilePath& local_path); + // Callback invoked when the delegate has determined local path. |file_name| + // is supplied in case it cannot be determined from local_path (e.g. local + // path is a content Uri: content://media/12345). |file_name| could be empty + // if it is the last component of |local_path|. + void DetermineLocalPathDone(const base::FilePath& local_path, + const base::FilePath& file_name); // Determine the MIME type corresponding to the local file path. This is only // done if the local path and the virtual path was the same. I.e. The file is
diff --git a/chrome/browser/download/download_target_determiner_delegate.h b/chrome/browser/download/download_target_determiner_delegate.h index dc1f526..e9eae91 100644 --- a/chrome/browser/download/download_target_determiner_delegate.h +++ b/chrome/browser/download/download_target_determiner_delegate.h
@@ -14,6 +14,7 @@ #include "components/download/public/common/download_item.h" #include "components/download/public/common/download_path_reservation_tracker.h" #include "components/download/public/common/download_schedule.h" +#include "components/download/public/common/download_utils.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace base { @@ -55,12 +56,6 @@ const base::FilePath& virtual_path, absl::optional<download::DownloadSchedule> download_schedule)>; - // Callback to be invoked when DetermineLocalPath() completes. The argument - // should be the determined local path. It should be non-empty on success. If - // |virtual_path| is already a local path, then |virtual_path| should be - // returned as-is. - using LocalPathCallback = base::OnceCallback<void(const base::FilePath&)>; - // Callback to be invoked after CheckDownloadUrl() completes. The parameter to // the callback should indicate the danger type of the download based on the // results of the URL check. @@ -120,7 +115,7 @@ // invoked to return the path. virtual void DetermineLocalPath(download::DownloadItem* download, const base::FilePath& virtual_path, - LocalPathCallback callback) = 0; + download::LocalPathCallback callback) = 0; // Check whether the download URL is malicious and invoke |callback| with a // suggested danger type for the download.
diff --git a/chrome/browser/download/download_target_determiner_unittest.cc b/chrome/browser/download/download_target_determiner_unittest.cc index 6468749d..bd5498f 100644 --- a/chrome/browser/download/download_target_determiner_unittest.cc +++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -226,11 +226,13 @@ ConfirmationCallback&)); void DetermineLocalPath(DownloadItem* item, const base::FilePath& path, - LocalPathCallback cb) override { + download::LocalPathCallback cb) override { DetermineLocalPath_(item, path, cb); } MOCK_METHOD3(DetermineLocalPath_, - void(DownloadItem*, const base::FilePath&, LocalPathCallback&)); + void(DownloadItem*, + const base::FilePath&, + download::LocalPathCallback&)); void ReserveVirtualPath( DownloadItem* download, const base::FilePath& virtual_path, @@ -287,7 +289,7 @@ ConfirmationCallback& callback); static void NullDetermineLocalPath(DownloadItem* download, const base::FilePath& virtual_path, - LocalPathCallback& callback); + download::LocalPathCallback& callback); }; class DownloadTargetDeterminerTest : public ChromeRenderViewHostTestHarness { @@ -624,8 +626,8 @@ void MockDownloadTargetDeterminerDelegate::NullDetermineLocalPath( DownloadItem* download, const base::FilePath& virtual_path, - LocalPathCallback& callback) { - std::move(callback).Run(virtual_path); + download::LocalPathCallback& callback) { + std::move(callback).Run(virtual_path, virtual_path.BaseName()); } // NotifyExtensions implementation that overrides the path so that the target @@ -926,8 +928,9 @@ _, last_selected_dir.AppendASCII("foo.txt"), DownloadConfirmationReason::SAVE_AS, _)); EXPECT_CALL(*delegate(), DetermineLocalPath_(_, virtual_path, _)) - .WillOnce(WithArg<2>(ScheduleCallback( - GetPathInDownloadDir(FILE_PATH_LITERAL("bar.txt"))))); + .WillOnce(WithArg<2>(ScheduleCallback2( + GetPathInDownloadDir(FILE_PATH_LITERAL("bar.txt")), + base::FilePath()))); RunTestCasesWithActiveItem(kLastSavePathTestCasesVirtual, std::size(kLastSavePathTestCasesVirtual)); } @@ -954,8 +957,9 @@ EXPECT_LOCAL_PATH}; EXPECT_CALL(*delegate(), DetermineLocalPath_(_, _, _)) - .WillOnce(WithArg<2>(ScheduleCallback( - GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt"))))); + .WillOnce(WithArg<2>(ScheduleCallback2( + GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")), + base::FilePath()))); RunTestCasesWithActiveItem(&kAutomaticDownloadToVirtualDir, 1); } @@ -974,8 +978,9 @@ EXPECT_LOCAL_PATH}; EXPECT_CALL(*delegate(), DetermineLocalPath_(_, _, _)) - .WillOnce(WithArg<2>(ScheduleCallback( - GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt"))))); + .WillOnce(WithArg<2>(ScheduleCallback2( + GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")), + base::FilePath()))); EXPECT_CALL(*delegate(), RequestConfirmation_( _, test_virtual_dir().AppendASCII("bar.txt"), DownloadConfirmationReason::SAVE_AS, _)) @@ -1145,7 +1150,8 @@ *delegate(), DetermineLocalPath_( _, GetPathInDownloadDir(FILE_PATH_LITERAL("virtual/foo.txt")), _)) - .WillOnce(WithArg<2>(ScheduleCallback(base::FilePath()))); + .WillOnce( + WithArg<2>(ScheduleCallback2(base::FilePath(), base::FilePath()))); RunTestCasesWithActiveItem(kLocalPathFailedCases, std::size(kLocalPathFailedCases)); } @@ -2680,4 +2686,38 @@ #endif // BUILDFLAG(ENABLE_PLUGINS) +#if BUILDFLAG(IS_ANDROID) +// If a content URI is returned when determining local path, virtual path +// is updated. +TEST_F(DownloadTargetDeterminerTest, DetermineLocalPathReturnsContentUri) { + const DownloadTestCase kLocalPathContentUriCase = { + AUTOMATIC, + download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DownloadFileType::NOT_DANGEROUS, + "http://example.com/foo.txt", + "text/plain", + FILE_PATH_LITERAL(""), + FILE_PATH_LITERAL("content://media/123"), + DownloadItem::TARGET_DISPOSITION_OVERWRITE, + EXPECT_LOCAL_PATH}; + + std::unique_ptr<download::MockDownloadItem> item = + CreateActiveDownloadItem(0, kLocalPathContentUriCase); + // The default download directory is the virtual path. + download_prefs()->SetDownloadPath(test_virtual_dir()); + + EXPECT_CALL( + *delegate(), + DetermineLocalPath_( + _, GetPathInDownloadDir(FILE_PATH_LITERAL("virtual/foo.txt")), _)) + .WillOnce(WithArg<2>(ScheduleCallback2( + base::FilePath("content://media/123"), base::FilePath("foor.txt")))); + std::unique_ptr<DownloadTargetInfo> target_info = + RunDownloadTargetDeterminer(base::FilePath(), item.get()); + + EXPECT_EQ(target_info->display_name.value(), "foor.txt"); + EXPECT_EQ(target_info->target_path.value(), "content://media/123"); +} +#endif // BUILDFLAG(IS_ANDROID) + } // namespace
diff --git a/chrome/browser/download/download_target_info.h b/chrome/browser/download/download_target_info.h index 3a6e884..fb3330e 100644 --- a/chrome/browser/download/download_target_info.h +++ b/chrome/browser/download/download_target_info.h
@@ -85,6 +85,9 @@ // Defines when to start the download, used by download later feature. absl::optional<download::DownloadSchedule> download_schedule; + + // Display name of the file. + base::FilePath display_name; }; #endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_TARGET_INFO_H_
diff --git a/chrome/browser/download/download_ui_controller.cc b/chrome/browser/download/download_ui_controller.cc index 2bfb093..013b925 100644 --- a/chrome/browser/download/download_ui_controller.cc +++ b/chrome/browser/download/download_ui_controller.cc
@@ -100,7 +100,7 @@ if (browser == nullptr) browser = chrome::FindLastActiveWithProfile(profile_); - if (browser && browser->window() && + if (browser && browser->window() && browser->window()->GetDownloadShelf() && DownloadItemModel(item).ShouldShowInShelf()) { DownloadUIModel::DownloadUIModelPtr model = DownloadItemModel::Wrap(item);
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc index 7339f2f..9f07bdb 100644 --- a/chrome/browser/download/download_ui_model.cc +++ b/chrome/browser/download/download_ui_model.cc
@@ -421,6 +421,10 @@ void DownloadUIModel::SetShouldShowInShelf(bool should_show) {} +bool DownloadUIModel::ShouldShowInBubble() const { + return ShouldShowInShelf(); +} + bool DownloadUIModel::ShouldNotifyUI() const { return true; } @@ -505,6 +509,10 @@ return false; } +base::Time DownloadUIModel::GetStartTime() const { + return base::Time(); +} + base::Time DownloadUIModel::GetEndTime() const { return base::Time(); } @@ -867,14 +875,19 @@ if (!status_text.empty()) return status_text; - std::u16string total_text = ui::FormatBytes(model_->GetTotalBytes()); - std::u16string delta_str = ui::TimeFormat::Simple( - ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT, - base::Time::Now() - model_->GetEndTime()); - return base::StrCat( - {total_text, - l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR), - delta_str}); + if (model_->GetEndTime().is_null()) { + // Offline items have these null. + return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_DONE); + } else { + std::u16string total_text = ui::FormatBytes(model_->GetTotalBytes()); + std::u16string delta_str = ui::TimeFormat::Simple( + ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT, + base::Time::Now() - model_->GetEndTime()); + return base::StrCat( + {total_text, + l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR), + delta_str}); + } } // To clarify variable / method names in methods below that help form failure
diff --git a/chrome/browser/download/download_ui_model.h b/chrome/browser/download/download_ui_model.h index a8e5fabc..3b3983ee 100644 --- a/chrome/browser/download/download_ui_model.h +++ b/chrome/browser/download/download_ui_model.h
@@ -208,6 +208,9 @@ // Returns |true| if this download should be displayed in the downloads shelf. virtual bool ShouldShowInShelf() const; + // Returns |true| if this download should be displayed in the download bubble. + virtual bool ShouldShowInBubble() const; + // Change whether the download should be displayed on the downloads // shelf. Setting this is only effective if the download hasn't already been // displayed in the shelf. @@ -298,6 +301,9 @@ // and so can't give an estimate. virtual bool TimeRemaining(base::TimeDelta* remaining) const; + // Returns the creation time for a download. + virtual base::Time GetStartTime() const; + // Returns the end/completion time for a completed download. base::Time() // if the download has not completed yet. virtual base::Time GetEndTime() const;
diff --git a/chrome/browser/download/offline_item_model.cc b/chrome/browser/download/offline_item_model.cc index 9bda738..ea50e3b 100644 --- a/chrome/browser/download/offline_item_model.cc +++ b/chrome/browser/download/offline_item_model.cc
@@ -31,9 +31,30 @@ return model; } +DownloadUIModel::DownloadUIModelPtr OfflineItemModel::Wrap( + OfflineItemModelManager* manager, + const OfflineItem& offline_item, + std::unique_ptr<DownloadUIModel::StatusTextBuilderBase> + status_text_builder) { + DownloadUIModel::DownloadUIModelPtr model( + new OfflineItemModel(manager, offline_item, + std::move(status_text_builder)), + base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get())); + return model; +} + OfflineItemModel::OfflineItemModel(OfflineItemModelManager* manager, const OfflineItem& offline_item) - : manager_(manager), + : OfflineItemModel(manager, + offline_item, + std::make_unique<StatusTextBuilder>()) {} + +OfflineItemModel::OfflineItemModel( + OfflineItemModelManager* manager, + const OfflineItem& offline_item, + std::unique_ptr<DownloadUIModel::StatusTextBuilderBase> status_text_builder) + : DownloadUIModel(std::move(status_text_builder)), + manager_(manager), offline_item_(std::make_unique<OfflineItem>(offline_item)) { Profile* profile = Profile::FromBrowserContext(manager_->browser_context()); offline_items_collection::OfflineContentAggregator* aggregator = @@ -169,6 +190,10 @@ return true; } +base::Time OfflineItemModel::GetStartTime() const { + return offline_item_->creation_time; +} + base::Time OfflineItemModel::GetEndTime() const { return offline_item_->completion_time; }
diff --git a/chrome/browser/download/offline_item_model.h b/chrome/browser/download/offline_item_model.h index 1acb8a5..f52c02e 100644 --- a/chrome/browser/download/offline_item_model.h +++ b/chrome/browser/download/offline_item_model.h
@@ -28,10 +28,19 @@ public: static DownloadUIModelPtr Wrap(OfflineItemModelManager* manager, const OfflineItem& offline_item); + static DownloadUIModelPtr Wrap( + OfflineItemModelManager* manager, + const OfflineItem& offline_item, + std::unique_ptr<DownloadUIModel::StatusTextBuilderBase> + status_text_builder); // Constructs a OfflineItemModel. OfflineItemModel(OfflineItemModelManager* manager, const OfflineItem& offline_item); + OfflineItemModel(OfflineItemModelManager* manager, + const OfflineItem& offline_item, + std::unique_ptr<DownloadUIModel::StatusTextBuilderBase> + status_text_builder); OfflineItemModel(const OfflineItemModel&) = delete; OfflineItemModel& operator=(const OfflineItemModel&) = delete; @@ -56,6 +65,7 @@ download::DownloadItem::DownloadState GetState() const override; bool IsPaused() const override; bool TimeRemaining(base::TimeDelta* remaining) const override; + base::Time GetStartTime() const override; base::Time GetEndTime() const override; bool IsDone() const override; base::FilePath GetFullPath() const override;
diff --git a/chrome/browser/enterprise/browser_management/browser_management_status_provider.h b/chrome/browser/enterprise/browser_management/browser_management_status_provider.h index fd6d6187..6f842720 100644 --- a/chrome/browser/enterprise/browser_management/browser_management_status_provider.h +++ b/chrome/browser/enterprise/browser_management/browser_management_status_provider.h
@@ -58,6 +58,7 @@ }; #if BUILDFLAG(IS_CHROMEOS_ASH) +// This is both a device and browser management status provider for ChromeOS. class DeviceManagementStatusProvider final : public policy::ManagementStatusProvider { public:
diff --git a/chrome/browser/enterprise/browser_management/management_service_factory.cc b/chrome/browser/enterprise/browser_management/management_service_factory.cc index c628ed0d..175f2ba 100644 --- a/chrome/browser/enterprise/browser_management/management_service_factory.cc +++ b/chrome/browser/enterprise/browser_management/management_service_factory.cc
@@ -6,6 +6,7 @@ #include "base/memory/singleton.h" #include "base/no_destructor.h" +#include "build/chromeos_buildflags.h" #include "chrome/browser/enterprise/browser_management/browser_management_service.h" #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h" @@ -14,6 +15,12 @@ #include "components/policy/core/common/management/platform_management_service.h" #include "content/public/browser/browser_context.h" #include "extensions/buildflags/buildflags.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_process_platform_part.h" +#include "chrome/browser/enterprise/browser_management/browser_management_status_provider.h" +#endif namespace policy { @@ -25,7 +32,20 @@ // static ManagementService* ManagementServiceFactory::GetForPlatform() { - return PlatformManagementService::GetInstance(); + auto* instance = PlatformManagementService::GetInstance(); + // This has to be done here since `DeviceManagementStatusProvider` cannot be + // defined in `components/policy/`, also we need we need the + // `g_browser_process->platform_part()`. +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (!instance->has_cros_status_provider() && g_browser_process && + g_browser_process->platform_part()) { + instance->AddChromeOsStatusProvider( + std::make_unique<DeviceManagementStatusProvider>( + g_browser_process->platform_part() + ->browser_policy_connector_ash())); + } +#endif + return instance; } // static
diff --git a/chrome/browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc b/chrome/browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc index da1e4f8..2a6b65c 100644 --- a/chrome/browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc +++ b/chrome/browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc
@@ -131,7 +131,7 @@ auto* value_set_in_map = policy_map.GetValue(kPolicyName); if (value_set_in_map) { - ASSERT_TRUE(value_set_in_map->Equals(value_set_in_pref)); + ASSERT_EQ(*value_set_in_map, *value_set_in_pref); if (policy_scope()) ASSERT_EQ(policy::POLICY_SCOPE_MACHINE, pref_scope); } else {
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.cc b/chrome/browser/extensions/api/chrome_extensions_api_client.cc index 38c4681..c5a6b1a 100644 --- a/chrome/browser/extensions/api/chrome_extensions_api_client.cc +++ b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
@@ -99,16 +99,15 @@ void ChromeExtensionsAPIClient::AddAdditionalValueStoreCaches( content::BrowserContext* context, const scoped_refptr<value_store::ValueStoreFactory>& factory, - const scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>>& - observers, + SettingsChangedCallback observer, std::map<settings_namespace::Namespace, ValueStoreCache*>* caches) { // Add support for chrome.storage.sync. (*caches)[settings_namespace::SYNC] = - new SyncValueStoreCache(factory, observers, context->GetPath()); + new SyncValueStoreCache(factory, observer, context->GetPath()); // Add support for chrome.storage.managed. (*caches)[settings_namespace::MANAGED] = - new ManagedValueStoreCache(context, factory, observers); + new ManagedValueStoreCache(context, factory, observer); } void ChromeExtensionsAPIClient::AttachWebContentsHelpers(
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.h b/chrome/browser/extensions/api/chrome_extensions_api_client.h index 2cca3e8e..adf68a63 100644 --- a/chrome/browser/extensions/api/chrome_extensions_api_client.h +++ b/chrome/browser/extensions/api/chrome_extensions_api_client.h
@@ -29,8 +29,7 @@ void AddAdditionalValueStoreCaches( content::BrowserContext* context, const scoped_refptr<value_store::ValueStoreFactory>& factory, - const scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>>& - observers, + SettingsChangedCallback observer, std::map<settings_namespace::Namespace, ValueStoreCache*>* caches) override; void AttachWebContentsHelpers(content::WebContents* web_contents) const
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.cc b/chrome/browser/extensions/api/downloads/downloads_api.cc index 4126629f..d26484d 100644 --- a/chrome/browser/extensions/api/downloads/downloads_api.cc +++ b/chrome/browser/extensions/api/downloads/downloads_api.cc
@@ -1140,7 +1140,7 @@ *it, off_record ? profile->GetPrimaryOTRProfile(/*create_if_needed=*/true) : profile->GetOriginalProfile())); - json_results->Append(std::move(json_item)); + json_results->Append(base::Value::FromUniquePtrValue(std::move(json_item))); } RecordApiFunctions(DOWNLOADS_FUNCTION_SEARCH); return RespondNow( @@ -1928,10 +1928,10 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!EventRouter::Get(profile_)) return; - std::unique_ptr<base::ListValue> args(new base::ListValue()); - args->Append(std::move(arg)); + base::ListValue args; + args.Append(base::Value::FromUniquePtrValue(std::move(arg))); std::string json_args; - base::JSONWriter::Write(*args, &json_args); + base::JSONWriter::Write(args, &json_args); // The downloads system wants to share on-record events with off-record // extension renderers even in incognito_split_mode because that's how // chrome://downloads works. The "restrict_to_profile" mechanism does not @@ -1945,7 +1945,7 @@ (include_incognito && !profile_->IsOffTheRecord()) ? nullptr : profile_.get(); auto event = std::make_unique<Event>(histogram_value, event_name, - std::move(*args).TakeListDeprecated(), + std::move(args).TakeListDeprecated(), restrict_to_browser_context); event->will_dispatch_callback = std::move(will_dispatch_callback); EventRouter::Get(profile_)->BroadcastEvent(std::move(event));
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc index ff8f886..60cdfc7 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc
@@ -245,13 +245,13 @@ } DCHECK(result->is_certificates()); - auto client_certs = std::make_unique<base::ListValue>(); + base::Value::List client_certs; for (std::vector<uint8_t>& cert : result->get_certificates()) { - client_certs->Append(std::make_unique<base::Value>(std::move(cert))); + client_certs.Append(base::Value(std::move(cert))); } auto results = std::make_unique<base::ListValue>(); - results->Append(std::move(client_certs)); + results->Append(base::Value(std::move(client_certs))); Respond(ArgumentList(std::move(results))); }
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api.cc b/chrome/browser/extensions/api/input_ime/input_ime_api.cc index cc6eb41..24607aa 100644 --- a/chrome/browser/extensions/api/input_ime/input_ime_api.cc +++ b/chrome/browser/extensions/api/input_ime/input_ime_api.cc
@@ -166,7 +166,7 @@ segments, &error)) { std::unique_ptr<base::ListValue> results = std::make_unique<base::ListValue>(); - results->Append(std::make_unique<base::Value>(false)); + results->Append(false); return RespondNow(ErrorWithArguments( std::move(results), InformativeError(error, static_function_name()))); } @@ -187,7 +187,7 @@ &error)) { std::unique_ptr<base::ListValue> results = std::make_unique<base::ListValue>(); - results->Append(std::make_unique<base::Value>(false)); + results->Append(false); return RespondNow(ErrorWithArguments( std::move(results), InformativeError(error, static_function_name()))); }
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc index 8d09cba..9a0028bc 100644 --- a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc +++ b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
@@ -982,7 +982,7 @@ bool success = engine->ClearComposition(params.context_id, &error); std::unique_ptr<base::ListValue> results = std::make_unique<base::ListValue>(); - results->Append(std::make_unique<base::Value>(success)); + results->Append(success); return RespondNow(success ? ArgumentList(std::move(results)) : ErrorWithArguments( @@ -1076,7 +1076,7 @@ !engine->SetCandidateWindowVisible(*properties.visible, &error)) { std::unique_ptr<base::ListValue> results = std::make_unique<base::ListValue>(); - results->Append(std::make_unique<base::Value>(false)); + results->Append(false); return RespondNow(ErrorWithArguments( std::move(results), InformativeError(error, static_function_name()))); } @@ -1169,7 +1169,7 @@ engine->SetCandidates(params.context_id, candidates_out, &error); std::unique_ptr<base::ListValue> results = std::make_unique<base::ListValue>(); - results->Append(std::make_unique<base::Value>(success)); + results->Append(success); return RespondNow(success ? ArgumentList(std::move(results)) : ErrorWithArguments( @@ -1194,7 +1194,7 @@ engine->SetCursorPosition(params.context_id, params.candidate_id, &error); std::unique_ptr<base::ListValue> results = std::make_unique<base::ListValue>(); - results->Append(std::make_unique<base::Value>(success)); + results->Append(success); return RespondNow(success ? ArgumentList(std::move(results)) : ErrorWithArguments( @@ -1324,18 +1324,17 @@ if (!engine) return RespondNow(Error(InformativeError(error, static_function_name()))); - auto bounds_list = std::make_unique<base::ListValue>(); + base::Value::List bounds_list; for (const auto& bounds : engine->composition_bounds()) { - auto bounds_value = std::make_unique<base::DictionaryValue>(); - bounds_value->SetIntKey("x", bounds.x()); - bounds_value->SetIntKey("y", bounds.y()); - bounds_value->SetIntKey("w", bounds.width()); - bounds_value->SetIntKey("h", bounds.height()); - bounds_list->Append(std::move(bounds_value)); + base::Value::Dict bounds_value; + bounds_value.Set("x", bounds.x()); + bounds_value.Set("y", bounds.y()); + bounds_value.Set("w", bounds.width()); + bounds_value.Set("h", bounds.height()); + bounds_list.Append(std::move(bounds_value)); } - return RespondNow( - OneArgument(base::Value::FromUniquePtrValue(std::move(bounds_list)))); + return RespondNow(OneArgument(base::Value(std::move(bounds_list)))); } void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
diff --git a/chrome/browser/extensions/api/scripting/scripting_api.cc b/chrome/browser/extensions/api/scripting/scripting_api.cc index 4cdb29e..4343e0c 100644 --- a/chrome/browser/extensions/api/scripting/scripting_api.cc +++ b/chrome/browser/extensions/api/scripting/scripting_api.cc
@@ -305,6 +305,62 @@ return permissions.CanAccessPage(committed_url, tab_id, error); } +// Collects the frames for injection. Method will return false if an error is +// encountered. +bool CollectFramesForInjection(const api::scripting::InjectionTarget& target, + content::WebContents* tab, + std::set<int>& frame_ids, + std::set<content::RenderFrameHost*>& frames, + std::string* error_out) { + if (target.document_ids) { + for (const auto& id : *target.document_ids) { + ExtensionApiFrameIdMap::DocumentId document_id = + ExtensionApiFrameIdMap::DocumentIdFromString(id); + + if (!document_id) { + *error_out = base::StringPrintf("Invalid document id %s", id.c_str()); + return false; + } + + content::RenderFrameHost* frame = + ExtensionApiFrameIdMap::Get()->GetRenderFrameHostByDocumentId( + document_id); + + // If the frame was not found or it matched another tab reject this + // request. + if (!frame || content::WebContents::FromRenderFrameHost(frame) != tab) { + *error_out = + base::StringPrintf("No document with id %s in tab with id %d", + id.c_str(), target.tab_id); + return false; + } + + // Convert the documentId into a frameId since the content will be + // injected synchronously. + frame_ids.insert(ExtensionApiFrameIdMap::GetFrameId(frame)); + frames.insert(frame); + } + } else { + if (target.frame_ids) { + frame_ids.insert(target.frame_ids->begin(), target.frame_ids->end()); + } else { + frame_ids.insert(ExtensionApiFrameIdMap::kTopFrameId); + } + + for (int frame_id : frame_ids) { + content::RenderFrameHost* frame = + ExtensionApiFrameIdMap::GetRenderFrameHostById(tab, frame_id); + if (!frame) { + *error_out = base::StringPrintf("No frame with id %d in tab with id %d", + frame_id, target.tab_id); + return false; + } + frames.insert(frame); + } + } + return true; +} + // Returns true if the `target` can be accessed with the given `permissions`. // If the target can be accessed, populates `script_executor_out`, // `frame_scope_out`, and `frame_ids_out` with the appropriate values; @@ -327,8 +383,16 @@ return false; } - if ((target.all_frames && *target.all_frames == true) && target.frame_ids) { - *error_out = "Cannot specify both 'allFrames' and 'frameIds'."; + if ((target.all_frames && *target.all_frames == true) && + (target.frame_ids || target.document_ids)) { + *error_out = + "Cannot specify 'allFrames' if either 'frameIds' or 'documentIds' is " + "specified."; + return false; + } + + if (target.frame_ids && target.document_ids) { + *error_out = "Cannot specify both 'frameIds' and 'documentIds'."; return false; } @@ -341,25 +405,15 @@ : ScriptExecutor::SPECIFIED_FRAMES; std::set<int> frame_ids; - if (target.frame_ids) { - frame_ids.insert(target.frame_ids->begin(), target.frame_ids->end()); - } else { - frame_ids.insert(ExtensionApiFrameIdMap::kTopFrameId); - } + std::set<content::RenderFrameHost*> frames; + if (!CollectFramesForInjection(target, tab, frame_ids, frames, error_out)) + return false; // TODO(devlin): If `allFrames` is true, we error out if the extension // doesn't have access to the top frame (even if it may inject in child // frames). This is inconsistent with content scripts (which can execute on // child frames), but consistent with the old tabs.executeScript() API. - for (int frame_id : frame_ids) { - content::RenderFrameHost* frame = - ExtensionApiFrameIdMap::GetRenderFrameHostById(tab, frame_id); - if (!frame) { - *error_out = base::StringPrintf("No frame with id %d in tab with id %d", - frame_id, target.tab_id); - return false; - } - + for (content::RenderFrameHost* frame : frames) { DCHECK_EQ(content::WebContents::FromRenderFrameHost(frame), tab); if (!HasPermissionToInjectIntoFrame(permissions, target.tab_id, frame, error_out)) { @@ -627,6 +681,8 @@ injection_result.result = base::Value::ToUniquePtrValue(std::move(result.value)); injection_result.frame_id = result.frame_id; + if (result.document_id) + injection_result.document_id = result.document_id.ToString(); // Put the top frame first; otherwise, any order. if (result.frame_id == ExtensionApiFrameIdMap::kTopFrameId) {
diff --git a/chrome/browser/extensions/api/storage/managed_value_store_cache.cc b/chrome/browser/extensions/api/storage/managed_value_store_cache.cc index 02f22e0..6a075f54 100644 --- a/chrome/browser/extensions/api/storage/managed_value_store_cache.cc +++ b/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
@@ -231,12 +231,14 @@ ManagedValueStoreCache::ManagedValueStoreCache( BrowserContext* context, scoped_refptr<value_store::ValueStoreFactory> factory, - scoped_refptr<SettingsObserverList> observers) + SettingsChangedCallback observer) : profile_(Profile::FromBrowserContext(context)), policy_domain_(GetPolicyDomain(profile_)), policy_service_(profile_->GetProfilePolicyConnector()->policy_service()), storage_factory_(std::move(factory)), - observers_(std::move(observers)) { + observer_(GetSequenceBoundSettingsChangedCallback( + base::SequencedTaskRunnerHandle::Get(), + std::move(observer))) { DCHECK_CURRENTLY_ON(BrowserThread::UI); policy_service_->AddObserver(policy_domain_, this); @@ -363,7 +365,7 @@ // Create the store now, and serve the cached policy until the PolicyService // sends updated values. std::unique_ptr<PolicyValueStore> store(new PolicyValueStore( - extension_id, observers_, + extension_id, observer_, value_store_util::CreateSettingsStore(settings_namespace::MANAGED, kManagedModelType, extension_id, storage_factory_)));
diff --git a/chrome/browser/extensions/api/storage/managed_value_store_cache.h b/chrome/browser/extensions/api/storage/managed_value_store_cache.h index 2903cbb..4c273eb 100644 --- a/chrome/browser/extensions/api/storage/managed_value_store_cache.h +++ b/chrome/browser/extensions/api/storage/managed_value_store_cache.h
@@ -47,7 +47,7 @@ // changes. ManagedValueStoreCache(content::BrowserContext* context, scoped_refptr<value_store::ValueStoreFactory> factory, - scoped_refptr<SettingsObserverList> observers); + SettingsChangedCallback observer); ManagedValueStoreCache(const ManagedValueStoreCache&) = delete; ManagedValueStoreCache& operator=(const ManagedValueStoreCache&) = delete; @@ -101,7 +101,7 @@ // These live on the FILE thread. scoped_refptr<value_store::ValueStoreFactory> storage_factory_; - scoped_refptr<SettingsObserverList> observers_; + SequenceBoundSettingsChangedCallback observer_; // All the PolicyValueStores live on the FILE thread, and |store_map_| can be // accessed only on the FILE thread as well.
diff --git a/chrome/browser/extensions/api/storage/policy_value_store.cc b/chrome/browser/extensions/api/storage/policy_value_store.cc index a38d2be..7f42fba0 100644 --- a/chrome/browser/extensions/api/storage/policy_value_store.cc +++ b/chrome/browser/extensions/api/storage/policy_value_store.cc
@@ -29,10 +29,10 @@ PolicyValueStore::PolicyValueStore( const std::string& extension_id, - scoped_refptr<SettingsObserverList> observers, + SequenceBoundSettingsChangedCallback observer, std::unique_ptr<ValueStore> delegate) : extension_id_(extension_id), - observers_(std::move(observers)), + observer_(std::move(observer)), delegate_(std::move(delegate)) {} PolicyValueStore::~PolicyValueStore() {} @@ -99,10 +99,8 @@ } if (!changes.empty()) { - observers_->Notify( - FROM_HERE, &SettingsObserver::OnSettingsChanged, extension_id_, - StorageAreaNamespace::kManaged, - value_store::ValueStoreChange::ToValue(std::move(changes))); + observer_->Run(extension_id_, StorageAreaNamespace::kManaged, + value_store::ValueStoreChange::ToValue(std::move(changes))); } }
diff --git a/chrome/browser/extensions/api/storage/policy_value_store.h b/chrome/browser/extensions/api/storage/policy_value_store.h index 562f9c4..e202a7c 100644 --- a/chrome/browser/extensions/api/storage/policy_value_store.h +++ b/chrome/browser/extensions/api/storage/policy_value_store.h
@@ -29,7 +29,7 @@ class PolicyValueStore : public value_store::ValueStore { public: PolicyValueStore(const std::string& extension_id, - scoped_refptr<SettingsObserverList> observers, + SequenceBoundSettingsChangedCallback observer, std::unique_ptr<value_store::ValueStore> delegate); PolicyValueStore(const PolicyValueStore&) = delete; @@ -65,7 +65,7 @@ private: std::string extension_id_; - scoped_refptr<SettingsObserverList> observers_; + SequenceBoundSettingsChangedCallback observer_; std::unique_ptr<value_store::ValueStore> delegate_; };
diff --git a/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc b/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc index 4fc6e48..7c2b390 100644 --- a/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc +++ b/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc
@@ -50,7 +50,7 @@ value_store::ValueStoreChange::ToValue(std::move(changes))); } -class MockSettingsObserver : public SettingsObserver { +class MockSettingsObserver { public: MOCK_METHOD3(OnSettingsChangedJSON, void(const std::string& extension_id, @@ -59,8 +59,9 @@ void OnSettingsChanged(const std::string& extension_id, StorageAreaNamespace storage_area, - const base::Value& changes) override { - OnSettingsChangedJSON(extension_id, storage_area, ValueToJson(changes)); + base::Value changes) { + OnSettingsChangedJSON(extension_id, storage_area, + ValueToJson(std::move(changes))); } }; @@ -70,11 +71,12 @@ class MutablePolicyValueStore : public PolicyValueStore { public: explicit MutablePolicyValueStore(const base::FilePath& path) - : PolicyValueStore(kTestExtensionId, - base::MakeRefCounted<SettingsObserverList>(), - std::make_unique<value_store::LeveldbValueStore>( - kDatabaseUMAClientName, - path)) {} + : PolicyValueStore( + kTestExtensionId, + SequenceBoundSettingsChangedCallback(base::DoNothing()), + std::make_unique<value_store::LeveldbValueStore>( + kDatabaseUMAClientName, + path)) {} MutablePolicyValueStore(const MutablePolicyValueStore&) = delete; MutablePolicyValueStore& operator=(const MutablePolicyValueStore&) = delete; @@ -120,18 +122,16 @@ void SetUp() override { ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); - observers_ = new SettingsObserverList(); - observers_->AddObserver(&observer_); store_ = std::make_unique<PolicyValueStore>( - kTestExtensionId, observers_, + kTestExtensionId, + SequenceBoundSettingsChangedCallback( + base::BindRepeating(&MockSettingsObserver::OnSettingsChanged, + base::Unretained(&observer_))), std::make_unique<value_store::LeveldbValueStore>( kDatabaseUMAClientName, scoped_temp_dir_.GetPath())); } - void TearDown() override { - observers_->RemoveObserver(&observer_); - store_.reset(); - } + void TearDown() override { store_.reset(); } protected: void SetCurrentPolicy(const policy::PolicyMap& policies) { @@ -151,7 +151,6 @@ content::BrowserTaskEnvironment task_environment_; std::unique_ptr<PolicyValueStore> store_; NiceMock<MockSettingsObserver> observer_; - scoped_refptr<SettingsObserverList> observers_; }; TEST_F(PolicyValueStoreTest, DontProvideRecommendedPolicies) {
diff --git a/chrome/browser/extensions/api/storage/sync_storage_backend.cc b/chrome/browser/extensions/api/storage/sync_storage_backend.cc index 4919a65..961c263 100644 --- a/chrome/browser/extensions/api/storage/sync_storage_backend.cc +++ b/chrome/browser/extensions/api/storage/sync_storage_backend.cc
@@ -52,12 +52,12 @@ SyncStorageBackend::SyncStorageBackend( scoped_refptr<value_store::ValueStoreFactory> storage_factory, const SettingsStorageQuotaEnforcer::Limits& quota, - scoped_refptr<SettingsObserverList> observers, + SequenceBoundSettingsChangedCallback observer, syncer::ModelType sync_type, const syncer::SyncableService::StartSyncFlare& flare) : storage_factory_(std::move(storage_factory)), quota_(quota), - observers_(std::move(observers)), + observer_(std::move(observer)), sync_type_(sync_type), flare_(flare) { DCHECK(IsOnBackendSequence()); @@ -92,7 +92,7 @@ // It's fine to create the quota enforcer underneath the sync layer, since // sync will only go ahead if each underlying storage operation succeeds. std::unique_ptr<SyncableSettingsStorage> syncable_storage( - new SyncableSettingsStorage(observers_, extension_id, + new SyncableSettingsStorage(observer_, extension_id, settings_storage.release(), sync_type_, flare_)); SyncableSettingsStorage* raw_syncable_storage = syncable_storage.get();
diff --git a/chrome/browser/extensions/api/storage/sync_storage_backend.h b/chrome/browser/extensions/api/storage/sync_storage_backend.h index 9c8a4c5..0691443e 100644 --- a/chrome/browser/extensions/api/storage/sync_storage_backend.h +++ b/chrome/browser/extensions/api/storage/sync_storage_backend.h
@@ -39,7 +39,7 @@ SyncStorageBackend( scoped_refptr<value_store::ValueStoreFactory> storage_factory, const SettingsStorageQuotaEnforcer::Limits& quota, - scoped_refptr<SettingsObserverList> observers, + SequenceBoundSettingsChangedCallback observer, syncer::ModelType sync_type, const syncer::SyncableService::StartSyncFlare& flare); @@ -81,8 +81,8 @@ // Quota limits (see SettingsStorageQuotaEnforcer). const SettingsStorageQuotaEnforcer::Limits quota_; - // The list of observers to settings changes. - const scoped_refptr<SettingsObserverList> observers_; + // Observer to settings changes. + SequenceBoundSettingsChangedCallback observer_; // A cache of ValueStore objects that have already been created. // Ensure that there is only ever one created per extension.
diff --git a/chrome/browser/extensions/api/storage/sync_value_store_cache.cc b/chrome/browser/extensions/api/storage/sync_value_store_cache.cc index 6f2bf3bc..b4163012 100644 --- a/chrome/browser/extensions/api/storage/sync_value_store_cache.cc +++ b/chrome/browser/extensions/api/storage/sync_value_store_cache.cc
@@ -37,7 +37,7 @@ SyncValueStoreCache::SyncValueStoreCache( scoped_refptr<value_store::ValueStoreFactory> factory, - scoped_refptr<SettingsObserverList> observers, + SettingsChangedCallback observer, const base::FilePath& profile_path) : initialized_(false) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -48,7 +48,10 @@ GetBackendTaskRunner()->PostTask( FROM_HERE, base::BindOnce(&SyncValueStoreCache::InitOnBackend, base::Unretained(this), std::move(factory), - std::move(observers), profile_path)); + GetSequenceBoundSettingsChangedCallback( + base::SequencedTaskRunnerHandle::Get(), + std::move(observer)), + profile_path)); } SyncValueStoreCache::~SyncValueStoreCache() { @@ -93,15 +96,15 @@ void SyncValueStoreCache::InitOnBackend( scoped_refptr<value_store::ValueStoreFactory> factory, - scoped_refptr<SettingsObserverList> observers, + SequenceBoundSettingsChangedCallback observer, const base::FilePath& profile_path) { DCHECK(IsOnBackendSequence()); DCHECK(!initialized_); app_backend_ = std::make_unique<SyncStorageBackend>( - factory, GetSyncQuotaLimits(), observers, syncer::APP_SETTINGS, + factory, GetSyncQuotaLimits(), observer, syncer::APP_SETTINGS, sync_start_util::GetFlareForSyncableService(profile_path)); extension_backend_ = std::make_unique<SyncStorageBackend>( - std::move(factory), GetSyncQuotaLimits(), std::move(observers), + std::move(factory), GetSyncQuotaLimits(), std::move(observer), syncer::EXTENSION_SETTINGS, sync_start_util::GetFlareForSyncableService(profile_path)); initialized_ = true;
diff --git a/chrome/browser/extensions/api/storage/sync_value_store_cache.h b/chrome/browser/extensions/api/storage/sync_value_store_cache.h index 3e603e3..8b264e5 100644 --- a/chrome/browser/extensions/api/storage/sync_value_store_cache.h +++ b/chrome/browser/extensions/api/storage/sync_value_store_cache.h
@@ -35,7 +35,7 @@ class SyncValueStoreCache : public ValueStoreCache { public: SyncValueStoreCache(scoped_refptr<value_store::ValueStoreFactory> factory, - scoped_refptr<SettingsObserverList> observers, + SettingsChangedCallback observer, const base::FilePath& profile_path); SyncValueStoreCache(const SyncValueStoreCache&) = delete; @@ -54,7 +54,7 @@ private: void InitOnBackend(scoped_refptr<value_store::ValueStoreFactory> factory, - scoped_refptr<SettingsObserverList> observers, + SequenceBoundSettingsChangedCallback observer, const base::FilePath& profile_path); bool initialized_;
diff --git a/chrome/browser/extensions/api/storage/syncable_settings_storage.cc b/chrome/browser/extensions/api/storage/syncable_settings_storage.cc index 668018b..f43309e 100644 --- a/chrome/browser/extensions/api/storage/syncable_settings_storage.cc +++ b/chrome/browser/extensions/api/storage/syncable_settings_storage.cc
@@ -22,12 +22,12 @@ namespace extensions { SyncableSettingsStorage::SyncableSettingsStorage( - scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>> observers, + SequenceBoundSettingsChangedCallback observer, const std::string& extension_id, ValueStore* delegate, syncer::ModelType sync_type, const syncer::SyncableService::StartSyncFlare& flare) - : observers_(std::move(observers)), + : observer_(std::move(observer)), extension_id_(extension_id), delegate_(delegate), sync_type_(sync_type), @@ -332,10 +332,8 @@ sync_processor_->NotifyChanges(changes); - observers_->Notify( - FROM_HERE, &SettingsObserver::OnSettingsChanged, extension_id_, - StorageAreaNamespace::kSync, - value_store::ValueStoreChange::ToValue(std::move(changes))); + observer_->Run(extension_id_, StorageAreaNamespace::kSync, + value_store::ValueStoreChange::ToValue(std::move(changes))); // TODO(kalman): Something sensible with multiple errors. if (errors.empty())
diff --git a/chrome/browser/extensions/api/storage/syncable_settings_storage.h b/chrome/browser/extensions/api/storage/syncable_settings_storage.h index 4bf52b908..15d9abc 100644 --- a/chrome/browser/extensions/api/storage/syncable_settings_storage.h +++ b/chrome/browser/extensions/api/storage/syncable_settings_storage.h
@@ -31,7 +31,7 @@ // Decorates a ValueStore with sync behaviour. class SyncableSettingsStorage : public value_store::ValueStore { public: - SyncableSettingsStorage(scoped_refptr<SettingsObserverList> observers, + SyncableSettingsStorage(SequenceBoundSettingsChangedCallback observer, const std::string& extension_id, // Ownership taken. value_store::ValueStore* delegate, @@ -114,8 +114,8 @@ std::unique_ptr<base::Value> old_value, value_store::ValueStoreChangeList* changes); - // List of observers to settings changes. - const scoped_refptr<SettingsObserverList> observers_; + // Observer to settings changes. + SequenceBoundSettingsChangedCallback observer_; // Id of the extension these settings are for. std::string const extension_id_;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 15ce5b9..b797f4e8 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -56,7 +56,7 @@ { "name": "add-passwords-in-settings", "owners": [ "vidhanj", "mamir", "lizapopova" ], - "expiry_milestone": 100 + "expiry_milestone": 103 }, { "name": "add-to-homescreen-iph", @@ -212,7 +212,7 @@ { "name": "arc-file-picker-experiment", "owners": [ "youkichihosoi", "risan", "niwa" ], - "expiry_milestone": 100 + "expiry_milestone": 110 }, { "name": "arc-ghost-window", @@ -1139,7 +1139,7 @@ { "name": "default-wkwebview-context-menu", "owners": [ "gambard", "bling-flags@google.com" ], - "expiry_milestone": 100 + "expiry_milestone": 105 }, { "name": "deferred-font-shaping", @@ -1513,7 +1513,7 @@ { "name": "enable-app-discovery-for-oobe", "owners": [ "melzhang", "tsergeant", "chromeos-apps-foundation-team" ], - "expiry_milestone": 100 + "expiry_milestone": 110 }, { "name": "enable-app-provisioning-static-server", @@ -1623,6 +1623,11 @@ "expiry_milestone": 99 }, { + "name": "enable-cast-remoting-query-blocklist", + "owners": [ "rwkeane@google.com", "openscreen-eng@google.com" ], + "expiry_milestone": 115 + }, + { "name": "enable-cast-streaming-av1", "owners": [ "jophba@google.com", "openscreen-eng@google.com" ], "expiry_milestone": 105 @@ -2138,12 +2143,12 @@ { "name": "enable-fre-default-browser-screen-testing", "owners": [ "alionadangla", "gambard" ], - "expiry_milestone": 100 + "expiry_milestone": 105 }, { "name": "enable-fre-ui-module-ios-with-options", "owners": [ "gambard", "veronguyen", "vincb" ], - "expiry_milestone": 100 + "expiry_milestone": 102 }, { "name": "enable-fullscreen-api", @@ -2344,7 +2349,7 @@ "expiry_milestone": 90 }, { - "name": "enable-lens-region-search", + "name": "enable-lens-standalone", "owners": [ "stanfield@google.com", "benwgold@google.com", "juanmojica@google.com" ], "expiry_milestone": 103 }, @@ -3233,7 +3238,7 @@ { "name": "expanded-tab-strip", "owners": [ "rkgibson@google.com", "gambard", "bling-flags@google.com" ], - "expiry_milestone": 100 + "expiry_milestone": 105 }, { "name": "explore-sites", @@ -3413,7 +3418,7 @@ { "name": "force-control-face-ae", "owners": [ "chromeos-camera-eng@google.com" ], - "expiry_milestone": 100 + "expiry_milestone": 110 }, { "name": "force-disable-extended-sync-promos", @@ -3428,6 +3433,11 @@ "expiry_milestone": -1 }, { + "name": "force-enable-cast-remoting-query", + "owners": [ "rwkeane@google.com", "openscreen-eng@google.com" ], + "expiry_milestone": 115 + }, + { "name": "force-major-version-to-minor", "owners": [ "brgoldstein", "potassium-katabolism@google.com" ], "expiry_milestone": 110 @@ -3759,6 +3769,11 @@ "expiry_milestone": 95 }, { + "name": "intent-chip-skips-intent-picker", + "owners": ["tsergeant", "chromeos-apps-foundation-team@google.com"], + "expiry_milestone": 105 + }, + { "name": "interest-feed-notice-card-auto-dismiss", "owners": [ "//chrome/android/feed/OWNERS", "feed@chromium.org", "edchin@chromium.org" ], "expiry_milestone": 95 @@ -3979,6 +3994,11 @@ "expiry_milestone": 104 }, { + "name": "link-capturing-infobar", + "owners": ["tsergeant", "chromeos-apps-foundation-team@google.com"], + "expiry_milestone": 105 + }, + { "name": "link-capturing-ui-update", "owners": ["tsergeant", "chromeos-apps-foundation-team@google.com"], "expiry_milestone": 105 @@ -4003,6 +4023,11 @@ "expiry_milestone": -1 }, { + "name": "location-bar-model-optimizations", + "owners": [ "pnoland@google.com"], + "expiry_milestone": 105 + }, + { "name": "mac-syscall-sandbox", "owners": [ "kerrnel@google.com" ], "expiry_milestone": 88 @@ -4780,7 +4805,12 @@ { "name": "partitioned-cookies", "owners": [ "dylancutler" ], - "expiry_milestone": 100 + "expiry_milestone": 110 + }, + { + "name": "partitioned-cookies-bypass-origin-trial", + "owners": [ "dylancutler" ], + "expiry_milestone": 110 }, { "name": "password-change-in-settings", @@ -4817,7 +4847,7 @@ { "name": "passwords-account-storage-revised-opt-in-flow", "owners": [ "mamir", "treib" ], - "expiry_milestone": 100 + "expiry_milestone": 103 }, { "name": "pcie-billboard-notification", @@ -4851,28 +4881,28 @@ }, { "name": "permission-chip", - "owners": [ "bsep", "engedy", "olesiamarukhno@google.com" ], - "expiry_milestone": 100 + "owners": [ "elklm", "bsep", "engedy", "olesiamarukhno@google.com" ], + "expiry_milestone": 110 }, { "name": "permission-chip-gesture", - "owners": [ "bsep", "engedy", "olesiamarukhno@google.com" ], - "expiry_milestone": 100 + "owners": [ "elklm", "bsep", "engedy", "olesiamarukhno@google.com" ], + "expiry_milestone": 110 }, { "name": "permission-chip-request-type", - "owners": [ "bsep", "engedy", "olesiamarukhno@google.com" ], - "expiry_milestone": 100 + "owners": [ "elklm", "bsep", "engedy", "olesiamarukhno@google.com" ], + "expiry_milestone": 110 }, { "name": "permission-predictions", "owners": [ "andypaicu", "engedy", "ravjit", "mkwst", "hkamila", "elklm" ], - "expiry_milestone": 100 + "expiry_milestone": 110 }, { "name": "permission-quiet-chip", "owners": [ "elklm", "engedy"], - "expiry_milestone": 100 + "expiry_milestone": 110 }, { "name": "persist-share-hub-on-app-switch", @@ -5565,7 +5595,7 @@ { "name": "smart-lock-ui-revamp", "owners": [ "cclem", "better-together-dev@google.com" ], - "expiry_milestone": 101 + "expiry_milestone": 103 }, { "name": "smart-suggestion-for-large-downloads", @@ -6162,7 +6192,7 @@ { "name": "web-otp-backend", "owners": [ "yigu" ], - "expiry_milestone": 100 + "expiry_milestone": 105 }, { "name": "web-share",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 11592fe5..4d22ac78 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -1200,13 +1200,11 @@ const char kEnableAutomaticSnoozeDescription[] = "Enables automatic snoozing on In-Product Help with no snooze button."; -const char kEnableLensRegionSearchFlagId[] = "enable-lens-region-search"; -const char kEnableLensRegionSearchName[] = - "Search your screen with Google Lens"; -const char kEnableLensRegionSearchDescription[] = - "Right click and select \"Search images with Google Lens\" to " - "search any region of the site to learn more about the visual content you " - "see while you browse and shop on the web."; +const char kEnableLensStandaloneFlagId[] = "enable-lens-standalone"; +const char kEnableLensStandaloneName[] = "Enable Lens features in Chrome."; +const char kEnableLensStandaloneDescription[] = + "Enables Lens image and region search to learn about the visual content " + "you see while you browse and shop on the web."; const char kEnableLoginDetectionName[] = "Enable login detection"; const char kEnableLoginDetectionDescription[] = @@ -1439,6 +1437,19 @@ "Enables the inclusion of VP9 codec video encoding in Cast mirroring " "session negotiations."; +const char kCastUseBlocklistForRemotingQueryName[] = + "Use blocklist for controlling remoting capabilities queries"; +const char kCastUseBlocklistForRemotingQueryDescription[] = + "Enables the use of the hard-coded blocklist for controlling whether a " + "device should be queried for remoting capabilities when configuring a " + "mirroring session."; + +const char kCastForceEnableRemotingQueryName[] = + "Force enable remoting capabilities queries"; +const char kCastForceEnableRemotingQueryDescription[] = + "Enables querying for remoting capabilities for ALL devices, regardless of " + "the contents of the allowlist or blocklist."; + const char kGoogleLensSdkIntentName[] = "Enable the use of the Lens SDK when starting intent into Lens."; const char kGoogleLensSdkIntentDescription[] = @@ -1613,6 +1624,11 @@ "Enable an entry point to Google Lens to allow users to search what they " "see using their mobile camera."; +const char kLocationBarModelOptimizationsName[] = + "LocationBarModel optimizations"; +const char kLocationBarModelOptimizationsDescription[] = + "Cache commonly used data in LocationBarModel to improve performance"; + const char kLogJsConsoleMessagesName[] = "Log JS console messages in system logs"; const char kLogJsConsoleMessagesDescription[] = @@ -2244,6 +2260,12 @@ const char kPartitionedCookiesName[] = "Partitioned cookies"; const char kPartitionedCookiesDescription[] = "Controls if the Partitioned cookie attribute is enabled."; +const char kPartitionedCookiesBypassOriginTrialName[] = + "Partitioned cookies: bypass origin trial"; +const char kPartitionedCookiesBypassOriginTrialDescription[] = + "If this flag is enabled, Chrome will not require a site to opt into the " + "origin trial in order to send or receive cookies set with the Partitioned " + "attribute."; const char kScrollableTabStripFlagId[] = "scrollable-tabstrip"; const char kScrollableTabStripName[] = "Tab Scrolling"; @@ -4525,40 +4547,6 @@ const char kDriveFsBidirectionalNativeMessagingDescription[] = "Enable enhanced native messaging host to communicate with DriveFS."; -const char kCrOSDspBasedAecAllowedName[] = - "Allow CRAS to use a DSP-based AEC if available"; -const char kCrOSDspBasedAecAllowedDescription[] = - "Allows the system variant of the AEC in CRAS to be run on DSP "; - -const char kCrOSDspBasedNsAllowedName[] = - "Allow CRAS to use a DSP-based NS if available"; -const char kCrOSDspBasedNsAllowedDescription[] = - "Allows the system variant of the NS in CRAS to be run on DSP "; - -const char kCrOSDspBasedAgcAllowedName[] = - "Allow CRAS to use a DSP-based AGC if available"; -const char kCrOSDspBasedAgcAllowedDescription[] = - "Allows the system variant of the AGC in CRAS to be run on DSP "; - -const char kCrOSEnforceSystemAecName[] = "Enforce using the system AEC in CrAS"; -const char kCrOSEnforceSystemAecDescription[] = - "Enforces using the system variant in CrAS of the AEC"; - -const char kCrOSEnforceSystemAecAgcName[] = - "Enforce using the system AEC and AGC in CrAS"; -const char kCrOSEnforceSystemAecAgcDescription[] = - "Enforces using the system variants in CrAS of the AEC and AGC."; - -const char kCrOSEnforceSystemAecNsName[] = - "Enforce using the system AEC and NS in CrAS"; -const char kCrOSEnforceSystemAecNsDescription[] = - "Enforces using the system variants in CrAS of the AEC and NS."; - -const char kCrOSEnforceSystemAecNsAgcName[] = - "Enforce using the system AEC, NS and AGC in CrAS"; -const char kCrOSEnforceSystemAecNsAgcDescription[] = - "Enforces using the system variants in CrAS of the AEC, NS and AGC."; - const char kEnableAppReinstallZeroStateName[] = "Enable Zero State App Reinstall Suggestions."; const char kEnableAppReinstallZeroStateDescription[] = @@ -5560,6 +5548,40 @@ "scanners that filter low energy advertisements in a power-efficient " "manner."; +const char kCrOSDspBasedAecAllowedName[] = + "Allow CRAS to use a DSP-based AEC if available"; +const char kCrOSDspBasedAecAllowedDescription[] = + "Allows the system variant of the AEC in CRAS to be run on DSP "; + +const char kCrOSDspBasedNsAllowedName[] = + "Allow CRAS to use a DSP-based NS if available"; +const char kCrOSDspBasedNsAllowedDescription[] = + "Allows the system variant of the NS in CRAS to be run on DSP "; + +const char kCrOSDspBasedAgcAllowedName[] = + "Allow CRAS to use a DSP-based AGC if available"; +const char kCrOSDspBasedAgcAllowedDescription[] = + "Allows the system variant of the AGC in CRAS to be run on DSP "; + +const char kCrOSEnforceSystemAecName[] = "Enforce using the system AEC in CrAS"; +const char kCrOSEnforceSystemAecDescription[] = + "Enforces using the system variant in CrAS of the AEC"; + +const char kCrOSEnforceSystemAecAgcName[] = + "Enforce using the system AEC and AGC in CrAS"; +const char kCrOSEnforceSystemAecAgcDescription[] = + "Enforces using the system variants in CrAS of the AEC and AGC."; + +const char kCrOSEnforceSystemAecNsName[] = + "Enforce using the system AEC and NS in CrAS"; +const char kCrOSEnforceSystemAecNsDescription[] = + "Enforces using the system variants in CrAS of the AEC and NS."; + +const char kCrOSEnforceSystemAecNsAgcName[] = + "Enforce using the system AEC, NS and AGC in CrAS"; +const char kCrOSEnforceSystemAecNsAgcDescription[] = + "Enforces using the system variants in CrAS of the AEC, NS and AGC."; + const char kDefaultCalculatorWebAppName[] = "Default install Calculator PWA"; const char kDefaultCalculatorWebAppDescription[] = "Enable default installing of the calculator PWA instead of the deprecated " @@ -5580,6 +5602,20 @@ const char kEnableTtsLacrosSupportDescription[] = "Enable or disable lacros support for text to speech."; +const char kIntentChipSkipsPickerName[] = + "Link capturing intent chip skips the intent picker bubble"; +const char kIntentChipSkipsPickerDescription[] = + "When enabled, clicking the intent chip in the Omnibox will skip the " + "intent picker bubble and launch directly into the app. If multiple apps " + "are installed for a URL, the intent picker will still be shown for " + "disambigation."; + +const char kLinkCapturingInfoBarName[] = "Enable link capturing info bar"; +const char kLinkCapturingInfoBarDescription[] = + "Enables an info bar which appears when launching a web app through the " + "Omnibox intent chip, prompting to update the link capturing setting for " + "that app."; + const char kLinkCapturingUiUpdateName[] = "Enable updated link capturing UI"; const char kLinkCapturingUiUpdateDescription[] = "Enables updated UI for link capturing flows from the browser to apps, "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 6a4f2c7..0b782bd32 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -650,9 +650,9 @@ extern const char kEnableGamepadButtonAxisEventsName[]; extern const char kEnableGamepadButtonAxisEventsDescription[]; -extern const char kEnableLensRegionSearchFlagId[]; -extern const char kEnableLensRegionSearchName[]; -extern const char kEnableLensRegionSearchDescription[]; +extern const char kEnableLensStandaloneFlagId[]; +extern const char kEnableLensStandaloneName[]; +extern const char kEnableLensStandaloneDescription[]; extern const char kEnableLoginDetectionName[]; extern const char kEnableLoginDetectionDescription[]; @@ -809,6 +809,12 @@ extern const char kCastStreamingVp9Name[]; extern const char kCastStreamingVp9Description[]; +extern const char kCastUseBlocklistForRemotingQueryName[]; +extern const char kCastUseBlocklistForRemotingQueryDescription[]; + +extern const char kCastForceEnableRemotingQueryName[]; +extern const char kCastForceEnableRemotingQueryDescription[]; + extern const char kGoogleLensSdkIntentName[]; extern const char kGoogleLensSdkIntentDescription[]; @@ -918,6 +924,9 @@ extern const char kLensCameraAssistedSearchName[]; extern const char kLensCameraAssistedSearchDescription[]; +extern const char kLocationBarModelOptimizationsName[]; +extern const char kLocationBarModelOptimizationsDescription[]; + extern const char kLogJsConsoleMessagesName[]; extern const char kLogJsConsoleMessagesDescription[]; @@ -1268,6 +1277,9 @@ extern const char kPartitionedCookiesName[]; extern const char kPartitionedCookiesDescription[]; +// TODO(crbug.com/1296161): Remove this when the CHIPS OT ends. +extern const char kPartitionedCookiesBypassOriginTrialName[]; +extern const char kPartitionedCookiesBypassOriginTrialDescription[]; extern const char kScrollableTabStripFlagId[]; extern const char kScrollableTabStripName[]; @@ -2594,27 +2606,6 @@ extern const char kDriveFsBidirectionalNativeMessagingName[]; extern const char kDriveFsBidirectionalNativeMessagingDescription[]; -extern const char kCrOSDspBasedAecAllowedName[]; -extern const char kCrOSDspBasedAecAllowedDescription[]; - -extern const char kCrOSDspBasedNsAllowedName[]; -extern const char kCrOSDspBasedNsAllowedDescription[]; - -extern const char kCrOSDspBasedAgcAllowedName[]; -extern const char kCrOSDspBasedAgcAllowedDescription[]; - -extern const char kCrOSEnforceSystemAecName[]; -extern const char kCrOSEnforceSystemAecDescription[]; - -extern const char kCrOSEnforceSystemAecAgcName[]; -extern const char kCrOSEnforceSystemAecAgcDescription[]; - -extern const char kCrOSEnforceSystemAecNsName[]; -extern const char kCrOSEnforceSystemAecNsDescription[]; - -extern const char kCrOSEnforceSystemAecNsAgcName[]; -extern const char kCrOSEnforceSystemAecNsAgcDescription[]; - extern const char kEnableAppReinstallZeroStateName[]; extern const char kEnableAppReinstallZeroStateDescription[]; @@ -3200,6 +3191,27 @@ extern const char kBluetoothAdvertisementMonitoringName[]; extern const char kBluetoothAdvertisementMonitoringDescription[]; +extern const char kCrOSDspBasedAecAllowedName[]; +extern const char kCrOSDspBasedAecAllowedDescription[]; + +extern const char kCrOSDspBasedNsAllowedName[]; +extern const char kCrOSDspBasedNsAllowedDescription[]; + +extern const char kCrOSDspBasedAgcAllowedName[]; +extern const char kCrOSDspBasedAgcAllowedDescription[]; + +extern const char kCrOSEnforceSystemAecName[]; +extern const char kCrOSEnforceSystemAecDescription[]; + +extern const char kCrOSEnforceSystemAecAgcName[]; +extern const char kCrOSEnforceSystemAecAgcDescription[]; + +extern const char kCrOSEnforceSystemAecNsName[]; +extern const char kCrOSEnforceSystemAecNsDescription[]; + +extern const char kCrOSEnforceSystemAecNsAgcName[]; +extern const char kCrOSEnforceSystemAecNsAgcDescription[]; + extern const char kDefaultCalculatorWebAppName[]; extern const char kDefaultCalculatorWebAppDescription[]; #endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) @@ -3214,6 +3226,12 @@ extern const char kEnableTtsLacrosSupportName[]; extern const char kEnableTtsLacrosSupportDescription[]; +extern const char kIntentChipSkipsPickerName[]; +extern const char kIntentChipSkipsPickerDescription[]; + +extern const char kLinkCapturingInfoBarName[]; +extern const char kLinkCapturingInfoBarDescription[]; + extern const char kLinkCapturingUiUpdateName[]; extern const char kLinkCapturingUiUpdateDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index 2970736a..26dd0cc 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -244,6 +244,7 @@ &kInstantStart, &kKitKatSupported, &kLensCameraAssistedSearch, + &kLocationBarModelOptimizations, &kNewWindowAppMenu, &kNotificationPermissionVariant, &kOfflineIndicatorV2, @@ -349,10 +350,6 @@ &password_manager::features::kTouchToFillPasswordSubmission, &password_manager::features::kUnifiedCredentialManagerDryRun, &password_manager::features::kUnifiedPasswordManagerAndroid, - &password_manager::features::kUnifiedPasswordManagerMigration, - &password_manager::features::kUnifiedPasswordManagerShadowAndroid, - &password_manager::features:: - kUnifiedPasswordManagerShadowWriteOperationsAndroid, &performance_hints::features::kContextMenuPerformanceInfo, &policy::features::kChromeManagementPageAndroid, &privacy_sandbox::kPrivacySandboxSettings3, @@ -668,6 +665,9 @@ const base::Feature kKitKatSupported{"KitKatSupported", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kLocationBarModelOptimizations{ + "LocationBarModelOptimizations", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kSearchEnginePromoExistingDevice{ "SearchEnginePromo.ExistingDevice", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h index 2417ac1a..72b17bb0 100644 --- a/chrome/browser/flags/android/chrome_feature_list.h +++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -110,6 +110,7 @@ extern const base::Feature kKitKatSupported; extern const base::Feature kLanguagesPreference; extern const base::Feature kLensCameraAssistedSearch; +extern const base::Feature kLocationBarModelOptimizations; extern const base::Feature kNewWindowAppMenu; extern const base::Feature kNotificationPermissionVariant; extern const base::Feature kOfflineIndicatorV2;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java index 2cb9f9c..655df93 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java
@@ -312,6 +312,40 @@ // TODO(crbug.com/1217708): Assert cached flags values are false/false. } + @Test + public void testMultipleStartCheckpoints_normalMode() { + startRun(); + // Crash streak is 0. Do not engage Safe Mode. + // There are no safe values. + // There are no cached flag values, so the defaults false/false are used. + assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD, + CachedFeatureFlags.getSafeModeBehaviorForTesting()); + assertFalse(CachedFeatureFlags.isEnabled(CRASHY_FEATURE)); + assertFalse(CachedFeatureFlags.isEnabled(OK_FEATURE)); + endCleanRun(true, true); + // Safe values became false/false. + // Cached values became true(flaky)/true. + + startRun(); + startRun(); + startRun(); + startRun(); + // Crash streak is 0. Do not engage Safe Mode. + // Safe values are false/false. + // Cached flag values are true(flaky)/true. + assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD, + CachedFeatureFlags.getSafeModeBehaviorForTesting()); + assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE)); + assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE)); + endCrashyRun(); + // Cached values remain true(crashy)/true. + + startRun(); + // Crash streak is 1, despite the multiple startRun() calls above. Do not engage Safe Mode. + assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD, + CachedFeatureFlags.getSafeModeBehaviorForTesting()); + } + private void startRun() { CachedFeatureFlags.isEnabled(CRASHY_FEATURE); CachedFeatureFlags.onStartOrResumeCheckpoint();
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java index d2b2d79..f56ae529 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java
@@ -24,6 +24,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** @@ -63,6 +64,9 @@ private AtomicInteger mBehavior = new AtomicInteger(Behavior.UNKNOWN); + private AtomicBoolean mStartCheckpointWritten = new AtomicBoolean(false); + private AtomicBoolean mEndCheckpointWritten = new AtomicBoolean(false); + CachedFlagsSafeMode() {} /** @@ -98,11 +102,20 @@ } /** - * Call at an early point in the path that leads to caching flags. If onFinishedCachingFlags() + * Call at an early point in the path that leads to caching flags. If onEndCheckpoint() * does not get called before the next run, this run will be considered a crash for purposes of * counting the crash streak and entering Safe Mode. */ public void onStartOrResumeCheckpoint() { + if (mEndCheckpointWritten.get()) { + // Do not increment the streak if it was already reset. + return; + } + if (mStartCheckpointWritten.getAndSet(true)) { + // Limit to one increment per run. + return; + } + SharedPreferencesManager.getInstance().incrementInt( ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE); RecordHistogram.recordEnumeratedHistogram( @@ -114,6 +127,15 @@ * incremented in {@link #onStartOrResumeCheckpoint} but does not reset it. */ public void onPauseCheckpoint() { + if (mEndCheckpointWritten.get()) { + // Do not change the streak if it was already reset. + return; + } + if (!mStartCheckpointWritten.getAndSet(false)) { + // Do not change the streak if it hasn't been incremented yet. + return; + } + int currentStreak = SharedPreferencesManager.getInstance().readInt( ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE); assert currentStreak >= 0; @@ -128,6 +150,11 @@ * be saved to be used in Safe Mode. */ void onEndCheckpoint(ValuesReturned safeValuesReturned) { + if (mEndCheckpointWritten.getAndSet(true)) { + // Limit to one reset per run. + return; + } + SharedPreferencesManager.getInstance().writeInt( ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE, 0); @@ -218,6 +245,8 @@ void clearMemoryForTesting() { mBehavior.set(Behavior.UNKNOWN); + mStartCheckpointWritten.set(false); + mEndCheckpointWritten.set(false); } @SuppressLint({"ApplySharedPref"})
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java index f84f47f8..597dd53 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -381,6 +381,7 @@ public static final String KITKAT_SUPPORTED = "KitKatSupported"; public static final String LEAK_DETECTION_UNAUTHENTICATED = "LeakDetectionUnauthenticated"; public static final String LIGHTWEIGHT_REACTIONS = "LightweightReactions"; + public static final String LOCATION_BAR_MODEL_OPTIMIZATIONS = "LocationBarModelOptimizations"; public static final String LOOKALIKE_NAVIGATION_URL_SUGGESTIONS_UI = "LookalikeUrlNavigationSuggestionsUI"; public static final String MARK_HTTP_AS = "MarkHttpAs"; @@ -533,12 +534,6 @@ public static final String UNIFIED_CREDENTIAL_MANAGER_DRY_RUN = "UnifiedCredentialManagerDryRun"; public static final String UNIFIED_PASSWORD_MANAGER_ANDROID = "UnifiedPasswordManagerAndroid"; - public static final String UNIFIED_PASSWORD_MANAGER_MIGRATION = - "UnifiedPasswordManagerMigration"; - public static final String UNIFIED_PASSWORD_MANAGER_SHADOW_ANDROID = - "UnifiedPasswordManagerShadowAndroid"; - public static final String UNIFIED_PASSWORD_MANAGER_SHADOW_WRITE_OPERATIONS_ANDROID = - "UnifiedPasswordManagerShadowWriteOperationsAndroid"; public static final String UPCOMING_SHARING_FEATURES = "UpcomingSharingFeatures"; public static final String UPDATE_NOTIFICATION_IMMEDIATE_SHOW_OPTION = "UpdateNotificationScheduleServiceImmediateShowOption";
diff --git a/chrome/browser/lacros/launcher_search/search_controller_lacros.cc b/chrome/browser/lacros/launcher_search/search_controller_lacros.cc index c5caefb..7d1f7be0 100644 --- a/chrome/browser/lacros/launcher_search/search_controller_lacros.cc +++ b/chrome/browser/lacros/launcher_search/search_controller_lacros.cc
@@ -6,11 +6,42 @@ #include <utility> +#include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h" +#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/lacros/launcher_search/search_util.h" +#include "chrome/browser/profiles/profile_manager.h" #include "chromeos/lacros/lacros_service.h" +#include "components/omnibox/browser/autocomplete_classifier.h" namespace crosapi { +namespace { -SearchControllerLacros::SearchControllerLacros() { +int ProviderTypes() { + // We use all the default providers except for the document provider, which + // suggests Drive files on enterprise devices. This is disabled to avoid + // duplication with search results from DriveFS. + return AutocompleteClassifier::DefaultOmniboxProviders() & + ~AutocompleteProvider::TYPE_DOCUMENT; +} + +} // namespace + +SearchControllerLacros::SearchControllerLacros() + : profile_(g_browser_process->profile_manager()->GetProfileByPath( + ProfileManager::GetPrimaryUserProfilePath())) { + if (!profile_) { + // TODO(crbug.com/1228587): Log error metrics if the profile is unavailable. + return; + } + + profile_observation_.Observe(profile_); + + autocomplete_controller_ = std::make_unique<AutocompleteController>( + std::make_unique<ChromeAutocompleteProviderClient>(profile_), + ProviderTypes()); + autocomplete_controller_->AddObserver(this); + chromeos::LacrosService* service = chromeos::LacrosService::Get(); if (!service->IsAvailable<mojom::SearchControllerRegistry>()) return; @@ -20,13 +51,71 @@ SearchControllerLacros::~SearchControllerLacros() = default; +void SearchControllerLacros::OnProfileWillBeDestroyed(Profile* profile) { + DCHECK_EQ(profile, profile_); + DCHECK(profile_observation_.IsObservingSource(profile_)); + + // SearchControllerLacros must shut down before the Profile is destroyed, + // otherwise there will be a use-after-free. + weak_ptr_factory_.InvalidateWeakPtrs(); + autocomplete_controller_.reset(); + profile_observation_.Reset(); + profile_ = nullptr; +} + void SearchControllerLacros::Search(const std::u16string& query, SearchCallback callback) { + if (!autocomplete_controller_) { + // TODO(crbug.com/1228687): Log error metrics if the autocomplete controller + // is unavailable. + if (publisher_.is_bound() && publisher_.is_connected()) { + publisher_->OnSearchResultsReceived( + mojom::SearchStatus::kBackendUnavailable, absl::nullopt); + } + return; + } + + autocomplete_controller_->Stop(false); + // If there is an in-flight session, send a cancellation notification. + if (publisher_.is_bound() && publisher_.is_connected()) { + publisher_->OnSearchResultsReceived(mojom::SearchStatus::kCancelled, + absl::nullopt); + } + // Reset the remote and send a new pending receiver to ash. publisher_.reset(); std::move(callback).Run(publisher_.BindNewEndpointAndPassReceiver()); - // TODO(crbug/1228587): Fill the results here. + // Start the search. Results will be returned through the observer interface. + AutocompleteInput input = + AutocompleteInput(query, metrics::OmniboxEventProto::CHROMEOS_APP_LIST, + ChromeAutocompleteSchemeClassifier(profile_)); + if (input.text().empty()) + input.set_focus_type(OmniboxFocusType::ON_FOCUS); + + autocomplete_controller_->Start(input); +} + +void SearchControllerLacros::OnResultChanged(AutocompleteController* controller, + bool default_match_changed) { + DCHECK_EQ(controller, autocomplete_controller_.get()); + + std::vector<mojom::SearchResultPtr> results; + for (AutocompleteMatch match : autocomplete_controller_->result()) { + if (match.search_terms_args) { + match.search_terms_args->request_source = TemplateURLRef::CROS_APP_LIST; + autocomplete_controller_->SetMatchDestinationURL(&match); + } + + auto result = match.answer.has_value() ? CreateAnswerResult(match) + : CreateResult(match); + results.push_back(std::move(result)); + } + + const auto status = autocomplete_controller_->done() + ? mojom::SearchStatus::kDone + : mojom::SearchStatus::kInProgress; + publisher_->OnSearchResultsReceived(status, std::move(results)); } } // namespace crosapi
diff --git a/chrome/browser/lacros/launcher_search/search_controller_lacros.h b/chrome/browser/lacros/launcher_search/search_controller_lacros.h index f808429..7dce0063 100644 --- a/chrome/browser/lacros/launcher_search/search_controller_lacros.h +++ b/chrome/browser/lacros/launcher_search/search_controller_lacros.h
@@ -8,27 +8,46 @@ #include <string> #include "base/memory/weak_ptr.h" +#include "base/scoped_observation.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_observer.h" #include "chromeos/crosapi/mojom/launcher_search.mojom.h" +#include "components/omnibox/browser/autocomplete_controller.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/receiver.h" namespace crosapi { // Implements crosapi interface for launcher search controller. -class SearchControllerLacros : public mojom::SearchController { +class SearchControllerLacros : public mojom::SearchController, + public AutocompleteController::Observer, + public ProfileObserver { public: SearchControllerLacros(); SearchControllerLacros(const SearchControllerLacros&) = delete; SearchControllerLacros& operator=(const SearchControllerLacros&) = delete; ~SearchControllerLacros() override; + // ProfileObserver: + void OnProfileWillBeDestroyed(Profile* profile) override; + private: // mojom::SearchController: void Search(const std::u16string& query, SearchCallback callback) override; + // AutocompleteController::Observer: + void OnResultChanged(AutocompleteController* controller, + bool default_match_changed) override; + + Profile* profile_; + std::unique_ptr<AutocompleteController> autocomplete_controller_; + mojo::AssociatedRemote<mojom::SearchResultsPublisher> publisher_; mojo::Receiver<mojom::SearchController> receiver_{this}; + // Observes the profile destruction. + base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this}; + base::WeakPtrFactory<SearchControllerLacros> weak_ptr_factory_{this}; };
diff --git a/chrome/browser/lacros/launcher_search/search_util.cc b/chrome/browser/lacros/launcher_search/search_util.cc new file mode 100644 index 0000000..f2bc5faf --- /dev/null +++ b/chrome/browser/lacros/launcher_search/search_util.cc
@@ -0,0 +1,166 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/lacros/launcher_search/search_util.h" + +#include "components/omnibox/browser/autocomplete_match_type.h" +#include "components/omnibox/browser/suggestion_answer.h" + +namespace crosapi { +namespace { + +using AnswerType = mojom::SearchResult::AnswerType; +using OmniboxType = mojom::SearchResult::OmniboxType; + +AnswerType MatchTypeToAnswerType(const int type) { + switch (static_cast<SuggestionAnswer::AnswerType>(type)) { + case SuggestionAnswer::ANSWER_TYPE_WEATHER: + return AnswerType::kWeather; + case SuggestionAnswer::ANSWER_TYPE_CURRENCY: + return AnswerType::kCurrency; + case SuggestionAnswer::ANSWER_TYPE_DICTIONARY: + return AnswerType::kDictionary; + case SuggestionAnswer::ANSWER_TYPE_FINANCE: + return AnswerType::kFinance; + case SuggestionAnswer::ANSWER_TYPE_SUNRISE: + return AnswerType::kSunrise; + case SuggestionAnswer::ANSWER_TYPE_TRANSLATION: + return AnswerType::kTranslation; + case SuggestionAnswer::ANSWER_TYPE_WHEN_IS: + return AnswerType::kWhenIs; + default: + return AnswerType::kDefaultAnswer; + } +} + +OmniboxType MatchTypeToOmniboxType(const AutocompleteMatchType::Type type) { + switch (type) { + case AutocompleteMatchType::URL_WHAT_YOU_TYPED: + case AutocompleteMatchType::HISTORY_URL: + case AutocompleteMatchType::HISTORY_TITLE: + case AutocompleteMatchType::HISTORY_BODY: + case AutocompleteMatchType::HISTORY_KEYWORD: + case AutocompleteMatchType::NAVSUGGEST: + case AutocompleteMatchType::BOOKMARK_TITLE: + case AutocompleteMatchType::NAVSUGGEST_PERSONALIZED: + case AutocompleteMatchType::CLIPBOARD_URL: + case AutocompleteMatchType::PHYSICAL_WEB_DEPRECATED: + case AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW_DEPRECATED: + case AutocompleteMatchType::TAB_SEARCH_DEPRECATED: + case AutocompleteMatchType::DOCUMENT_SUGGESTION: + case AutocompleteMatchType::PEDAL_DEPRECATED: + return OmniboxType::kDomain; + + case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED: + case AutocompleteMatchType::SEARCH_SUGGEST: + case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY: + case AutocompleteMatchType::SEARCH_SUGGEST_TAIL: + case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE: + case AutocompleteMatchType::SEARCH_OTHER_ENGINE: + case AutocompleteMatchType::CONTACT_DEPRECATED: + case AutocompleteMatchType::VOICE_SUGGEST: + case AutocompleteMatchType::CLIPBOARD_TEXT: + case AutocompleteMatchType::CLIPBOARD_IMAGE: + return OmniboxType::kSearch; + + case AutocompleteMatchType::SEARCH_HISTORY: + case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED: + return OmniboxType::kHistory; + + case AutocompleteMatchType::CALCULATOR: + return OmniboxType::kCalculator; + + case AutocompleteMatchType::OPEN_TAB: + return OmniboxType::kOpenTab; + + case AutocompleteMatchType::EXTENSION_APP_DEPRECATED: + case AutocompleteMatchType::TILE_SUGGESTION: + case AutocompleteMatchType::TILE_NAVSUGGEST: + case AutocompleteMatchType::NUM_TYPES: + // Not reached. + return OmniboxType::kDomain; + } +} + +// Returns the first text field from the given ImageLine. +std::u16string GetFirstText(const SuggestionAnswer::ImageLine& line) { + return line.text_fields().empty() ? std::u16string() + : line.text_fields()[0].text(); +} + +std::u16string GetAdditionalText(const SuggestionAnswer::ImageLine& line) { + return line.additional_text() ? line.additional_text()->text() + : std::u16string(); +} + +mojom::SearchResult::TextType GetAdditionalTextType( + const SuggestionAnswer::ImageLine& line) { + if (!line.additional_text()) + return mojom::SearchResult::TextType::kUnset; + + switch (line.additional_text()->style()) { + case SuggestionAnswer::TextStyle::POSITIVE: + return mojom::SearchResult::TextType::kPositive; + case SuggestionAnswer::TextStyle::NEGATIVE: + return mojom::SearchResult::TextType::kNegative; + default: + return mojom::SearchResult::TextType::kUnset; + } +} + +mojom::SearchResultPtr CreateBaseResult(const AutocompleteMatch& match) { + mojom::SearchResultPtr result = mojom::SearchResult::New(); + + result->type = mojom::SearchResultType::kOmniboxResult; + result->relevance = match.relevance; + result->destination_url = match.destination_url; + result->is_omnibox_search = AutocompleteMatch::IsSearchType(match.type) + ? mojom::SearchResult::OptionalBool::kTrue + : mojom::SearchResult::OptionalBool::kFalse; + return result; +} + +} // namespace + +mojom::SearchResultPtr CreateAnswerResult(const AutocompleteMatch& match) { + mojom::SearchResultPtr result = CreateBaseResult(match); + + result->is_answer = mojom::SearchResult::OptionalBool::kTrue; + result->answer_type = MatchTypeToAnswerType(match.answer->type()); + + if (result->answer_type == AnswerType::kWeather) + result->image_url = match.answer->image_url(); + + result->contents = match.contents; + result->additional_contents = GetAdditionalText(match.answer->first_line()); + + const auto& second_line = match.answer->second_line(); + result->description = GetFirstText(second_line); + result->additional_description = GetAdditionalText(second_line); + result->additional_description_type = GetAdditionalTextType(second_line); + + return result; +} + +mojom::SearchResultPtr CreateResult(const AutocompleteMatch& match) { + mojom::SearchResultPtr result = CreateBaseResult(match); + + result->is_answer = mojom::SearchResult::OptionalBool::kFalse; + + if (match.type == AutocompleteMatchType::SEARCH_SUGGEST_ENTITY && + !match.image_url.is_empty()) { + result->omnibox_type = OmniboxType::kRichImage; + result->image_url = match.image_url; + } else { + // TODO(crbug.com/1228687): Implement favicon logic. + result->omnibox_type = MatchTypeToOmniboxType(match.type); + } + + result->contents = match.contents; + result->description = match.description; + + return result; +} + +} // namespace crosapi
diff --git a/chrome/browser/lacros/launcher_search/search_util.h b/chrome/browser/lacros/launcher_search/search_util.h new file mode 100644 index 0000000..bb4a788 --- /dev/null +++ b/chrome/browser/lacros/launcher_search/search_util.h
@@ -0,0 +1,21 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_LACROS_LAUNCHER_SEARCH_SEARCH_UTIL_H_ +#define CHROME_BROWSER_LACROS_LAUNCHER_SEARCH_SEARCH_UTIL_H_ + +#include "chromeos/crosapi/mojom/launcher_search.mojom.h" +#include "components/omnibox/browser/autocomplete_match.h" + +namespace crosapi { + +// Creates an Omnibox answer card result from the AutocompleteMatch. +mojom::SearchResultPtr CreateAnswerResult(const AutocompleteMatch& match); + +// Creates an Omnibox search result from the AutocompleteMatch. +mojom::SearchResultPtr CreateResult(const AutocompleteMatch& match); + +} // namespace crosapi + +#endif // CHROME_BROWSER_LACROS_LAUNCHER_SEARCH_SEARCH_UTIL_H_
diff --git a/chrome/browser/lacros/launcher_search/search_util_unittest.cc b/chrome/browser/lacros/launcher_search/search_util_unittest.cc new file mode 100644 index 0000000..4a582c6 --- /dev/null +++ b/chrome/browser/lacros/launcher_search/search_util_unittest.cc
@@ -0,0 +1,90 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/lacros/launcher_search/search_util.h" + +#include <string> + +#include "base/json/json_reader.h" +#include "base/values.h" +#include "components/omnibox/browser/autocomplete_match_type.h" +#include "components/omnibox/browser/suggestion_answer.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "url/gurl.h" + +namespace crosapi { +namespace { + +// Tests result conversion for a default answer result. +TEST(SearchUtilTest, CreateAnswerResult) { + AutocompleteMatch match; + match.relevance = 1248; + match.destination_url = GURL("http://www.example.com/"); + match.type = AutocompleteMatchType::Type::SEARCH_SUGGEST; + match.contents = u"contents"; + match.description = u"description"; + + SuggestionAnswer answer; + std::string json = + "{ \"l\": [" + " { \"il\": { \"t\": [{ \"t\": \"text one\", \"tt\": 8 }], " + " \"at\": { \"t\": \"additional one\", \"tt\": 42 } } }, " + " { \"il\": { \"t\": [{ \"t\": \"text two\", \"tt\": 5 }], " + " \"at\": { \"t\": \"additional two\", \"tt\": 6 } } } " + "] }"; + absl::optional<base::Value> value = base::JSONReader::Read(json); + ASSERT_TRUE(value && value->is_dict()); + ASSERT_TRUE(SuggestionAnswer::ParseAnswer(value->GetDict(), u"-1", &answer)); + match.answer = answer; + + const auto result = CreateAnswerResult(match); + EXPECT_EQ(result->type, mojom::SearchResultType::kOmniboxResult); + EXPECT_EQ(result->relevance, 1248); + ASSERT_TRUE(result->destination_url.has_value()); + EXPECT_EQ(result->destination_url.value(), GURL("http://www.example.com/")); + EXPECT_EQ(result->is_omnibox_search, + mojom::SearchResult::OptionalBool::kTrue); + EXPECT_EQ(result->is_answer, mojom::SearchResult::OptionalBool::kTrue); + + ASSERT_TRUE(result->contents.has_value()); + EXPECT_EQ(result->contents.value(), u"contents"); + ASSERT_TRUE(result->additional_contents.has_value()); + EXPECT_EQ(result->additional_contents.value(), u"additional one"); + ASSERT_TRUE(result->description.has_value()); + EXPECT_EQ(result->description.value(), u"text two"); + ASSERT_TRUE(result->additional_description.has_value()); + EXPECT_EQ(result->additional_description.value(), u"additional two"); +} + +// Tests result conversion for a rich entity Omnibox result. +TEST(SearchUtilTest, CreateResult) { + AutocompleteMatch match; + match.relevance = 300; + match.destination_url = GURL("http://www.example.com/"); + match.type = AutocompleteMatchType::Type::SEARCH_SUGGEST_ENTITY; + match.image_url = GURL("http://www.example.com/image.jpeg"); + match.contents = u"contents"; + match.description = u"description"; + + const auto result = CreateResult(match); + EXPECT_EQ(result->type, mojom::SearchResultType::kOmniboxResult); + EXPECT_EQ(result->relevance, 300); + ASSERT_TRUE(result->destination_url.has_value()); + EXPECT_EQ(result->destination_url.value(), GURL("http://www.example.com/")); + EXPECT_EQ(result->is_omnibox_search, + mojom::SearchResult::OptionalBool::kTrue); + EXPECT_EQ(result->is_answer, mojom::SearchResult::OptionalBool::kFalse); + EXPECT_EQ(result->omnibox_type, mojom::SearchResult::OmniboxType::kRichImage); + ASSERT_TRUE(result->image_url.has_value()); + EXPECT_EQ(result->image_url.value(), + GURL("http://www.example.com/image.jpeg")); + ASSERT_TRUE(result->contents.has_value()); + EXPECT_EQ(result->contents.value(), u"contents"); + ASSERT_TRUE(result->description.has_value()); + EXPECT_EQ(result->description.value(), u"description"); +} + +} // namespace +} // namespace crosapi
diff --git a/chrome/browser/lens/region_search/lens_region_search_controller.cc b/chrome/browser/lens/region_search/lens_region_search_controller.cc index 99ec5de6..fed827a 100644 --- a/chrome/browser/lens/region_search/lens_region_search_controller.cc +++ b/chrome/browser/lens/region_search/lens_region_search_controller.cc
@@ -192,7 +192,7 @@ core_tab_helper->SearchWithLensInNewTab( image, captured_image.Size(), lens::EntryPoint::CHROME_REGION_SEARCH_MENU_ITEM, - lens::features::kEnableSidePanelForLensRegionSearch.Get()); + lens::features::IsLensSidePanelEnabled()); } else { core_tab_helper->SearchByImageInNewTab(image, captured_image.Size()); }
diff --git a/chrome/browser/lifetime/browser_close_manager_browsertest.cc b/chrome/browser/lifetime/browser_close_manager_browsertest.cc index 5546ae3..7d36830 100644 --- a/chrome/browser/lifetime/browser_close_manager_browsertest.cc +++ b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
@@ -230,11 +230,12 @@ download::DownloadDangerType danger_type, download::DownloadItem::MixedContentStatus mcs, const base::FilePath& intermediate_path, + const base::FilePath& display_name, absl::optional<download::DownloadSchedule> download_schedule, download::DownloadInterruptReason reason) { - std::move(callback).Run(target_path, disp, - download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, mcs, - intermediate_path, download_schedule, reason); + std::move(callback).Run( + target_path, disp, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, mcs, + intermediate_path, display_name, download_schedule, reason); } };
diff --git a/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc b/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc index 98fea9d8..a68c2ec 100644 --- a/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc +++ b/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc
@@ -6,6 +6,7 @@ #include <string> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h" #include "base/bind.h" @@ -18,7 +19,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile_manager.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "chromeos/dbus/power/power_manager_client.h" #include "chromeos/login/login_state/login_state.h" #include "chromeos/system/fake_statistics_provider.h"
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc index de50846..72d1bd7 100644 --- a/chrome/browser/net/profile_network_context_service.cc +++ b/chrome/browser/net/profile_network_context_service.cc
@@ -345,12 +345,12 @@ registry->RegisterBooleanPref(prefs::kQuicAllowed, true); registry->RegisterBooleanPref(prefs::kGloballyScopeHTTPAuthCacheEnabled, false); + registry->RegisterListPref(prefs::kHSTSPolicyBypassList); } // static void ProfileNetworkContextService::RegisterLocalStatePrefs( PrefRegistrySimple* registry) { - registry->RegisterListPref(prefs::kHSTSPolicyBypassList); registry->RegisterIntegerPref( prefs::kAmbientAuthenticationInPrivateModesEnabled, static_cast<int>(net::AmbientAuthAllowedProfileTypes::REGULAR_ONLY)); @@ -794,7 +794,7 @@ base::FilePath(chrome::kSCTAuditingPendingReportsFileName); } const base::Value* hsts_policy_bypass_list = - g_browser_process->local_state()->GetList(prefs::kHSTSPolicyBypassList); + profile_->GetPrefs()->GetList(prefs::kHSTSPolicyBypassList); for (const auto& value : hsts_policy_bypass_list->GetListDeprecated()) { const std::string* string_value = value.GetIfString(); if (!string_value)
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java index efd5895..f654f35 100644 --- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java +++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java
@@ -126,13 +126,13 @@ * using notifications, false for invoking on startup. */ public void requestPermissionIfNeeded(boolean contextual) { - // Notifications only require permission starting at Android T. - if (!BuildInfo.isAtLeastT()) { + if (!BuildInfo.isAtLeastT() || !BuildInfo.targetsAtLeastT()) { return; } - // Record the state of the notification permission before trying to ask. - recordNotificationPermissionState(); + // Record the state of the notification permission before trying to ask but after verifying + // we are running on Android T. + recordCurrentNotificationPermissionStatus(); @PermissionRequestMode int requestMode = shouldRequestPermission(); @@ -156,7 +156,12 @@ @PermissionRequestMode int shouldRequestPermission() { - if (!BuildInfo.isAtLeastT()) return PermissionRequestMode.DO_NOT_REQUEST; + // Notifications only require permission starting at Android T. And apps targeting < T can't + // request permission as the OS prompts the user automatically. + if (!BuildInfo.isAtLeastT() || !BuildInfo.targetsAtLeastT()) { + return PermissionRequestMode.DO_NOT_REQUEST; + } + if (mAndroidPermissionDelegate.hasPermission(PermissionConstants.NOTIFICATION_PERMISSION)) { return PermissionRequestMode.DO_NOT_REQUEST; } @@ -188,7 +193,11 @@ : PermissionRequestMode.REQUEST_ANDROID_PERMISSION; } - private void recordNotificationPermissionState() { + /** + * Records the current status of the notification permission (Allowed/Denied) and if denied we + * include how many times we've asked or if the permission is denied by policy. + */ + private void recordCurrentNotificationPermissionStatus() { if (mAndroidPermissionDelegate.hasPermission(PermissionConstants.NOTIFICATION_PERMISSION)) { NotificationUmaTracker.getInstance().recordNotificationPermissionState( NotificationPermissionState.ALLOWED);
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java index 18c9ea1..2f1f5e59 100644 --- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java +++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java
@@ -69,6 +69,10 @@ .FIELD_TRIAL_ALWAYS_SHOW_RATIONALE_BEFORE_REQUESTING_PERMISSION, "false"); FeatureList.setTestValues(testValues); + + // These tests only apply on builds targeting T running on a T device. + ShadowBuildInfo.setTargetsAtLeastT(true); + ShadowBuildInfo.setIsAtLeastT(true); } @After @@ -160,8 +164,53 @@ } @Test + public void testNotificationPrompt_nothingHappensWhenNotTargetingT() { + // Build targeting a <T SDK. + ShadowBuildInfo.setTargetsAtLeastT(false); + + mActivityScenarios.getScenario().onActivity(activity -> { + TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate(); + TestAndroidPermissionDelegate permissionDelegate = + new TestAndroidPermissionDelegate(new WeakReference<>(activity)); + NotificationPermissionController notificationPermissionController = + createNotificationPermissionController(rationaleDelegate, permissionDelegate); + + notificationPermissionController.requestPermissionIfNeeded(); + + long permissionRequestTimestamp = + PermissionPrefs.getAndroidNotificationPermissionRequestTimestamp(); + + // We shouldn't have requested for permission or shown the rationale. + assertEquals(0, rationaleDelegate.getCallCount()); + assertEquals(0, permissionRequestTimestamp); + }); + } + + @Test + public void testNotificationPrompt_nothingHappensWhenNotRunningOnT() { + // Running on a <T device. + ShadowBuildInfo.setIsAtLeastT(false); + + mActivityScenarios.getScenario().onActivity(activity -> { + TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate(); + TestAndroidPermissionDelegate permissionDelegate = + new TestAndroidPermissionDelegate(new WeakReference<>(activity)); + NotificationPermissionController notificationPermissionController = + createNotificationPermissionController(rationaleDelegate, permissionDelegate); + + notificationPermissionController.requestPermissionIfNeeded(); + + long permissionRequestTimestamp = + PermissionPrefs.getAndroidNotificationPermissionRequestTimestamp(); + + // We shouldn't have requested for permission or shown the rationale. + assertEquals(0, rationaleDelegate.getCallCount()); + assertEquals(0, permissionRequestTimestamp); + }); + } + + @Test public void testNotificationPrompt_alreadyHasPermission() { - ShadowBuildInfo.setIsAtLeastT(true); mActivityScenarios.getScenario().onActivity(activity -> { TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate(); TestAndroidPermissionDelegate permissionDelegate = @@ -226,7 +275,6 @@ @Test public void testNotificationPromptShownOnStartup_noPermissionsYet_shouldShowOSPrompt() { - ShadowBuildInfo.setIsAtLeastT(true); mActivityScenarios.getScenario().onActivity(activity -> { NotificationPermissionController notificationPermissionController = createNotificationPermissionController(activity); @@ -248,7 +296,6 @@ @Test public void testNotificationPromptShownOnStartup_alwaysShowRationale() { - ShadowBuildInfo.setIsAtLeastT(true); FeatureList.TestValues testValues = new FeatureList.TestValues(); testValues.addFieldTrialParamOverride(ChromeFeatureList.NOTIFICATION_PERMISSION_VARIANT, NotificationPermissionController @@ -318,7 +365,6 @@ @Test public void testNotificationPrompt_showOSPromptAndAccept() { - ShadowBuildInfo.setIsAtLeastT(true); mActivityScenarios.getScenario().onActivity(activity -> { TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate(); TestAndroidPermissionDelegate permissionDelegate = @@ -363,7 +409,6 @@ @Test public void testNotificationPrompt_showOSPromptAndDismiss_tooSoonForSecondTime() { - ShadowBuildInfo.setIsAtLeastT(true); mActivityScenarios.getScenario().onActivity(activity -> { TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate(); TestAndroidPermissionDelegate permissionDelegate = @@ -404,7 +449,6 @@ @Test public void testNotificationPrompt_showOSPromptAndReject_tooSoonForSecondTime() { - ShadowBuildInfo.setIsAtLeastT(true); mActivityScenarios.getScenario().onActivity(activity -> { TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate(); TestAndroidPermissionDelegate permissionDelegate = @@ -445,7 +489,6 @@ @Test public void testNotificationPrompt_showOSPromptAndDismiss_showAgainAfterTimePasses() { - ShadowBuildInfo.setIsAtLeastT(true); mActivityScenarios.getScenario().onActivity(activity -> { TestAndroidPermissionDelegate permissionDelegate = new TestAndroidPermissionDelegate(new WeakReference<>(activity)); @@ -489,7 +532,6 @@ @Test public void testNotificationPrompt_showOSPromptAndReject_showRationaleAfterTimePasses() { - ShadowBuildInfo.setIsAtLeastT(true); mActivityScenarios.getScenario().onActivity(activity -> { TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate(); TestAndroidPermissionDelegate permissionDelegate = @@ -541,7 +583,6 @@ @Test public void testNotificationPrompt_showOSPromptAndReject_showRationaleAndAccept_approveSecondOSPrompt() { - ShadowBuildInfo.setIsAtLeastT(true); mActivityScenarios.getScenario().onActivity(activity -> { TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate(); TestAndroidPermissionDelegate permissionDelegate = @@ -599,7 +640,6 @@ @Test public void testNotificationPrompt_showOSPromptAndReject_showRationaleAndAccept_rejectSecondOSPrompt() { - ShadowBuildInfo.setIsAtLeastT(true); mActivityScenarios.getScenario().onActivity(activity -> { TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate(); TestAndroidPermissionDelegate permissionDelegate = @@ -674,7 +714,6 @@ @Test public void testNotificationPrompt_showOSPromptAndReject_showRationaleAndReject() { - ShadowBuildInfo.setIsAtLeastT(true); mActivityScenarios.getScenario().onActivity(activity -> { TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate(); NotificationPermissionController notificationPermissionController =
diff --git a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc index db08205..88cb72d 100644 --- a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc +++ b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
@@ -243,10 +243,10 @@ run_loop->Quit(); }, &run_loop, &results), - std::vector<GURL>{ - GURL("https://www.youtube.com/"), - GURL("https://www.chrome.com/"), - GURL("https://music.youtube.com/"), + std::vector<std::string>{ + "youtube.com", + "chrome.com", + "music.youtube.com", }); run_loop.Run(); @@ -497,7 +497,7 @@ run_loop->Quit(); }, &run_loop), - std::vector<GURL>{GURL("https://www.chromium.org")}); + std::vector<std::string>{"www.chromium.org"}); run_loop.Run(); }
diff --git a/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/BuyableProductPageAnnotation.java b/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/BuyableProductPageAnnotation.java index 7a06bfe..b89a358d 100644 --- a/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/BuyableProductPageAnnotation.java +++ b/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/BuyableProductPageAnnotation.java
@@ -8,18 +8,13 @@ import org.json.JSONObject; import org.chromium.base.Log; -import org.chromium.base.annotations.DoNotClassMerge; import org.chromium.chrome.browser.page_annotations.PageAnnotation.PageAnnotationType; import java.util.Locale; /** * {@link PageAnnotation} for products in a page. - * - * This class should not be merged because it is being used as a key in a Map - * in PageAnnotationUtils.java. */ -@DoNotClassMerge public class BuyableProductPageAnnotation extends PageAnnotation { private static final String TAG = "BPPA"; private static final String BUYABLE_PRODUCT_KEY = "buyableProduct";
diff --git a/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/ProductPriceUpdatePageAnnotation.java b/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/ProductPriceUpdatePageAnnotation.java index 36ed4d2..b14bcd3e 100644 --- a/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/ProductPriceUpdatePageAnnotation.java +++ b/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/ProductPriceUpdatePageAnnotation.java
@@ -8,18 +8,13 @@ import org.json.JSONObject; import org.chromium.base.Log; -import org.chromium.base.annotations.DoNotClassMerge; import org.chromium.chrome.browser.page_annotations.PageAnnotation.PageAnnotationType; import java.util.Locale; /** * {@link PageAnnotation} for product price updates in a page. - * - * This class should not be merged because it is being used as a key in a Map - * in PageAnnotationUtils.java. */ -@DoNotClassMerge public class ProductPriceUpdatePageAnnotation extends PageAnnotation { private static final String TAG = "PPUPA"; private static final String PRICE_UPDATE_KEY = "priceUpdate"; @@ -107,4 +102,4 @@ return json != null && json.has(CURRENCY_CODE_KEY) && !json.isNull(CURRENCY_CODE_KEY) && json.has(AMOUNT_MICROS_KEY) && !json.isNull(AMOUNT_MICROS_KEY); } -} +} \ No newline at end of file
diff --git a/chrome/browser/page_info/OWNERS b/chrome/browser/page_info/OWNERS index 7c601fd..535ff1d 100644 --- a/chrome/browser/page_info/OWNERS +++ b/chrome/browser/page_info/OWNERS
@@ -1 +1,3 @@ file://chrome/browser/ui/page_info/OWNERS + +per-file ...about_this_site*=dullweber@chromium.org
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc index 8a23c6c..6c7ef053 100644 --- a/chrome/browser/pdf/pdf_extension_test.cc +++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -62,6 +62,7 @@ #include "chrome/common/pref_names.h" #include "chrome/common/webui_url_constants.h" #include "chrome/test/base/ui_test_utils.h" +#include "chrome/test/base/web_ui_test_data_source.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/download/public/common/download_item.h" #include "components/guest_view/browser/guest_view_manager.h" @@ -1017,6 +1018,21 @@ class PDFExtensionJSTest : public PDFExtensionTest { protected: + void SetUpOnMainThread() override { + PDFExtensionTest::SetUpOnMainThread(); + + // Load the pak file holding the resources served from chrome://webui-test. + base::FilePath pak_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_MODULE, &pak_path)); + pak_path = pak_path.AppendASCII("browser_tests.pak"); + ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( + pak_path, ui::kScaleFactorNone); + + // Register the chrome://webui-test data source. + content::WebUIDataSource::Add(browser()->profile(), + webui::CreateWebUITestDataSource()); + } + void RunTestsInJsModule(const std::string& filename, const std::string& pdf_filename) { RunTestsInJsModuleHelper(filename, pdf_filename, /*new_tab=*/false); @@ -1050,12 +1066,16 @@ constexpr char kModuleLoaderTemplate[] = R"(var s = document.createElement('script'); s.type = 'module'; - s.src = '_test_resources/pdf/%s'; + s.src = 'chrome://%s/pdf/%s'; + s.onerror = function(e) { + console.error('Error while loading', e.target.src); + }; document.body.appendChild(s);)"; ASSERT_TRUE(content::ExecuteScript( guest_contents, - base::StringPrintf(kModuleLoaderTemplate, filename.c_str()))); + base::StringPrintf(kModuleLoaderTemplate, + chrome::kChromeUIWebUITestHost, filename.c_str()))); if (!catcher.GetNextResult()) FAIL() << catcher.message(); @@ -1332,6 +1352,8 @@ } IN_PROC_BROWSER_TEST_F(PDFExtensionContentSettingJSTest, BeepThenNoBeep) { + content::RenderProcessHost::SetMaxRendererProcessCount(1); + RunTestsInJsModule("beep_test.js", "test-beep.pdf"); SetPdfJavaScript(/*enabled=*/false); RunTestsInJsModuleNewTab("nobeep_test.js", "test-beep.pdf"); @@ -1343,6 +1365,8 @@ } IN_PROC_BROWSER_TEST_F(PDFExtensionContentSettingJSTest, NoBeepThenBeep) { + content::RenderProcessHost::SetMaxRendererProcessCount(1); + SetPdfJavaScript(/*enabled=*/false); RunTestsInJsModule("nobeep_test.js", "test-beep.pdf"); SetPdfJavaScript(/*enabled=*/true); @@ -2068,7 +2092,7 @@ protected: std::vector<base::Feature> GetEnabledFeatures() const override { auto enabled = PDFExtensionTest::GetEnabledFeatures(); - enabled.push_back(lens::features::kLensRegionSearch); + enabled.push_back(lens::features::kLensStandalone); return enabled; } };
diff --git a/chrome/browser/policy/default_geolocation_policy_handler_unittest.cc b/chrome/browser/policy/default_geolocation_policy_handler_unittest.cc index bef0640f..eb244251 100644 --- a/chrome/browser/policy/default_geolocation_policy_handler_unittest.cc +++ b/chrome/browser/policy/default_geolocation_policy_handler_unittest.cc
@@ -50,7 +50,7 @@ UpdateProviderPolicy(policy); const base::Value* value = nullptr; EXPECT_TRUE(store_->GetValue(arc::prefs::kArcLocationServiceEnabled, &value)); - EXPECT_TRUE(base::Value(false).Equals(value)); + EXPECT_EQ(base::Value(false), *value); } TEST_F(DefaultGeolocationPolicyHandlerTest, AskGeolocation) {
diff --git a/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc b/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc index 6de86b3..e389216 100644 --- a/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc +++ b/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
@@ -293,7 +293,7 @@ value, std::string(negation ? "isn't" : "is") + " equal to " + ValueToString(*value)) { - return value->Equals(&arg); + return *value == arg; } MATCHER(IsListEmpty, std::string(negation ? "isn't" : "is") + " empty.") {
diff --git a/chrome/browser/policy/profile_policy_connector_unittest.cc b/chrome/browser/policy/profile_policy_connector_unittest.cc index 6858e331..cd94f6b5 100644 --- a/chrome/browser/policy/profile_policy_connector_unittest.cc +++ b/chrome/browser/policy/profile_policy_connector_unittest.cc
@@ -287,7 +287,7 @@ connector.policy_service()->GetPolicies(chrome_ns).GetValue( key::kAutofillAddressEnabled, base::Value::Type::BOOLEAN); ASSERT_TRUE(value); - EXPECT_TRUE(base::Value(false).Equals(value)); + EXPECT_EQ(base::Value(false), *value); // Now test with a higher-priority provider also setting the policy. UpdateChromePolicyToMockProviderAndVerify(&mock_platform_provider, connector);
diff --git a/chrome/browser/profiles/incognito_mode_policy_handler_unittest.cc b/chrome/browser/profiles/incognito_mode_policy_handler_unittest.cc index fb9baea..955882e 100644 --- a/chrome/browser/profiles/incognito_mode_policy_handler_unittest.cc +++ b/chrome/browser/profiles/incognito_mode_policy_handler_unittest.cc
@@ -51,7 +51,7 @@ void VerifyValues(IncognitoModePrefs::Availability availability) { const base::Value* value = NULL; EXPECT_TRUE(store_->GetValue(prefs::kIncognitoModeAvailability, &value)); - EXPECT_TRUE(base::Value(static_cast<int>(availability)).Equals(value)); + EXPECT_EQ(base::Value(static_cast<int>(availability)), *value); } };
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc index 67d7beb..5e4b07c 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -3037,7 +3037,7 @@ const bool provider_supports_image_search = provider && !provider->image_url().empty() && provider->image_url_ref().IsValid(service->search_terms_data()); - return base::FeatureList::IsEnabled(lens::features::kLensRegionSearch) && + return base::FeatureList::IsEnabled(lens::features::kLensStandalone) && !IsFrameInPdfViewer(GetRenderFrameHost()) && provider_supports_image_search && !GetDocumentURL(params_).SchemeIs(content::kChromeUIScheme) && @@ -3290,7 +3290,7 @@ core_tab_helper->SearchWithLensInNewTab( render_frame_host, params().src_url, lens::EntryPoint::CHROME_SEARCH_WITH_GOOGLE_LENS_CONTEXT_MENU_ITEM, - lens::features::kEnableSidePanelForLensImageSearch.Get()); + lens::features::IsLensSidePanelEnabled()); } void RenderViewContextMenu::ExecRegionSearch(
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc index 763bbc91..3eea68e 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -1565,10 +1565,9 @@ void SetUp() override { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeatureWithParameters( - lens::features::kLensRegionSearch, + lens::features::kLensStandalone, std::map<std::string, std::string>{ - {lens::features::kEnableSidePanelForLensRegionSearch.name, - "false"}}); + {lens::features::kEnableSidePanelForLens.name, "false"}}); InProcessBrowserTest::SetUp(); } @@ -1614,7 +1613,7 @@ GURL GetLensRegionSearchURL() { static const std::string kLensRegionSearchURL = - lens::features::GetHomepageURLForRegionSearch() + "upload?ep=crs"; + lens::features::GetHomepageURLForLens() + "upload?ep=crs"; return GURL(kLensRegionSearchURL); }
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc index 707dba6..d5a0428f 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -743,7 +743,7 @@ // is enabled. TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearch) { base::test::ScopedFeatureList features; - features.InitAndEnableFeature(lens::features::kLensRegionSearch); + features.InitAndEnableFeature(lens::features::kLensStandalone); SetUserSelectedDefaultSearchProvider("https://www.google.com", /*supports_image_search=*/true); content::ContextMenuParams params = CreateParams(MenuItem::PAGE); @@ -758,7 +758,7 @@ TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchEnterprisePoicyDisabled) { base::test::ScopedFeatureList features; - features.InitAndEnableFeature(lens::features::kLensRegionSearch); + features.InitAndEnableFeature(lens::features::kLensStandalone); SetUserSelectedDefaultSearchProvider("https://www.google.com", /*supports_image_search=*/true); // Set enterprise policy to false. @@ -774,7 +774,7 @@ // clicks on an image. TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchDisabledOnImage) { base::test::ScopedFeatureList features; - features.InitAndEnableFeature(lens::features::kLensRegionSearch); + features.InitAndEnableFeature(lens::features::kLensStandalone); SetUserSelectedDefaultSearchProvider("https://www.google.com", /*supports_image_search=*/true); content::ContextMenuParams params = CreateParams(MenuItem::IMAGE); @@ -791,7 +791,7 @@ TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchNonGoogleDefaultSearchEngineSupportsImageSearch) { base::test::ScopedFeatureList features; - features.InitAndEnableFeature(lens::features::kLensRegionSearch); + features.InitAndEnableFeature(lens::features::kLensStandalone); SetUserSelectedDefaultSearchProvider("https://www.search.com", /*supports_image_search=*/true); content::ContextMenuParams params = CreateParams(MenuItem::PAGE); @@ -807,7 +807,7 @@ TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchDefaultSearchEngineDoesNotSupportImageSearch) { base::test::ScopedFeatureList features; - features.InitAndEnableFeature(lens::features::kLensRegionSearch); + features.InitAndEnableFeature(lens::features::kLensStandalone); SetUserSelectedDefaultSearchProvider("https://www.search.com", /*supports_image_search=*/false); content::ContextMenuParams params = CreateParams(MenuItem::PAGE); @@ -822,7 +822,7 @@ // is disabled. TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchExperimentDisabled) { base::test::ScopedFeatureList features; - features.InitAndDisableFeature(lens::features::kLensRegionSearch); + features.InitAndDisableFeature(lens::features::kLensStandalone); SetUserSelectedDefaultSearchProvider("https://www.google.com", /*supports_image_search=*/true); content::ContextMenuParams params = CreateParams(MenuItem::PAGE); @@ -836,7 +836,7 @@ // Chrome UI Scheme. TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchChromeUIScheme) { base::test::ScopedFeatureList features; - features.InitAndEnableFeature(lens::features::kLensRegionSearch); + features.InitAndEnableFeature(lens::features::kLensStandalone); SetUserSelectedDefaultSearchProvider("https://www.google.com", /*supports_image_search=*/true); content::ContextMenuParams params = CreateParams(MenuItem::PAGE);
diff --git a/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc index 1beaf9b..e3c904a 100644 --- a/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc +++ b/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc
@@ -75,8 +75,9 @@ for (const std::string& dict : dictionaries) { dictionaries_value.Append(dict); } - EXPECT_TRUE(dictionaries_value.Equals(menu()->GetPrefs()->GetList( - spellcheck::prefs::kSpellCheckDictionaries))); + EXPECT_EQ(dictionaries_value, + *menu()->GetPrefs()->GetList( + spellcheck::prefs::kSpellCheckDictionaries)); } MockRenderViewContextMenu* menu() { return menu_.get(); }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn index e5896369..58b6b162 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -39,7 +39,6 @@ "background/command_handler_interface.js", "background/custom_automation_event.js", "background/editing/editable_line.js", - "background/editing/intent_handler.js", "background/event_source.js", "background/gesture_command_data.js", "background/keyboard_handler.js", @@ -116,6 +115,7 @@ "background/earcon_engine.js", "background/earcons.js", "background/editing/editing.js", + "background/editing/intent_handler.js", "background/es6_loader.js", "background/find_handler.js", "background/focus_automation_handler.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js index 2a12407..3c8efb7 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
@@ -6,7 +6,9 @@ * @fileoverview Processes events related to editing text and emits the * appropriate spoken and braille feedback. */ + import {Color} from '../color.js'; +import {IntentHandler} from './intent_handler.js'; const AutomationEvent = chrome.automation.AutomationEvent; const AutomationIntent = chrome.automation.AutomationIntent;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js index b8c35f8..98aa6e0 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
@@ -7,14 +7,6 @@ * Braille is *not* handled in this module. */ -goog.provide('IntentHandler'); - -goog.require('constants'); -goog.require('editing.EditableLine'); -goog.require('Msgs'); -goog.require('Output'); - -goog.scope(function() { const AutomationIntent = chrome.automation.AutomationIntent; const Cursor = cursors.Cursor; const Dir = constants.Dir; @@ -28,7 +20,7 @@ /** * A stateless class that turns intents into speech. */ -IntentHandler = class { +export class IntentHandler { /** * Called when intents are received from an AutomationEvent. * @param {!Array<AutomationIntent>} intents @@ -203,5 +195,4 @@ return false; } -}; -}); // goog.scope +}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js index cbecb180..eaa5ff25 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js
@@ -15,11 +15,14 @@ */ ChromeVoxIntentHandlerTest = class extends ChromeVoxNextE2ETest { /** @override */ - setUp() { + async setUpDeferred() { + await super.setUpDeferred(); window.Dir = constants.Dir; window.IntentTextBoundaryType = chrome.automation.IntentTextBoundaryType; window.Movement = cursors.Movement; window.Unit = cursors.Unit; + await importModule( + 'IntentHandler', '/chromevox/background/editing/intent_handler.js'); } };
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js index 91b61055..7eba11d7 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -35,7 +35,6 @@ goog.require('ExtensionBridge'); goog.require('GestureCommandData'); goog.require('GestureGranularity'); -goog.require('IntentHandler'); goog.require('JaPhoneticMap'); goog.require('KeyCode'); goog.require('LibLouis.FormType');
diff --git a/chrome/browser/resources/chromeos/drive_internals.html b/chrome/browser/resources/chromeos/drive_internals.html index 277adc3..48b35de 100644 --- a/chrome/browser/resources/chromeos/drive_internals.html +++ b/chrome/browser/resources/chromeos/drive_internals.html
@@ -34,12 +34,6 @@ </label> </div> <div> - <label> - Mirroring - <input type="checkbox" id="mirroring-toggle"> - </label> - </div> - <div> Tracing <button id="button-enable-tracing">Enable</button> <button id="button-disable-tracing">Disable</button> @@ -97,6 +91,23 @@ </ul> </section> + <section id="mirror-sync-section" hidden> + <h2>MirrorSync</h2> + <label> + Enable Mirroring + <input type="checkbox" id="mirroring-toggle"> + </label> + <form id="mirror-path-form" hidden> + <input type="text" id="mirror-path-input"> + <button type="submit">Add Sync Path</button> + <span id="mirroring-path-status"></span> + </form> + <table> + <tbody id="mirror-sync-paths" hidden> + </tbody> + </table> + </section> + <section id="delta-update-status-section" hidden> <h2>Delta Update Status</h2> <table>
diff --git a/chrome/browser/resources/chromeos/drive_internals.js b/chrome/browser/resources/chromeos/drive_internals.js index 50a03da..81451cc 100644 --- a/chrome/browser/resources/chromeos/drive_internals.js +++ b/chrome/browser/resources/chromeos/drive_internals.js
@@ -217,6 +217,54 @@ } /** + * Adds a new row to the syncing paths table upon successful completion. + * @param {string} path The path that was synced. + * @param {string} status The drive::FileError as a string. + */ +function onAddSyncPath(path, status) { + $('mirroring-path-status').textContent = status; + if (status !== 'FILE_ERROR_OK') { + return; + } + + // Avoid adding paths to the table if they already exist. + if ($(`mirroring-${path}`)) { + return; + } + + const newRow = document.createElement('tr'); + newRow.id = `mirroring-${path}`; + const deleteButton = createElementFromText('button', 'Delete'); + deleteButton.addEventListener('click', function(e) { + e.preventDefault(); + chrome.send('removeSyncPath', [path]); + }); + const deleteCell = document.createElement('td'); + deleteCell.appendChild(deleteButton); + newRow.appendChild(deleteCell); + const pathCell = createElementFromText('td', path); + newRow.appendChild(pathCell); + $('mirror-sync-paths').appendChild(newRow); +} + +/** + * Remove a path from the syncing table. + * @param {string} path The path that was synced. + * @param {string} status The drive::FileError as a string. + */ +function onRemoveSyncPath(path, status) { + if (status !== 'FILE_ERROR_OK') { + return; + } + + if (!$(`mirroring-${path}`)) { + return; + } + + $(`mirroring-${path}`).remove(); +} + +/** * Creates an element named |elementName| containing the content |text|. * @param {string} elementName Name of the new element to be created. * @param {string} text Text to be contained in the new element. @@ -319,6 +367,12 @@ chrome.send('setStartupArguments', [$('startup-arguments-input').value]); }); + $('mirror-path-form').addEventListener('submit', function(e) { + e.preventDefault(); + $('mirroring-path-status').textContent = 'adding...'; + chrome.send('addSyncPath', [$('mirror-path-input').value]); + }); + $('button-enable-tracing').addEventListener('click', function() { chrome.send('enableTracing'); });
diff --git a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html index 87f310b..af94d252 100644 --- a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html +++ b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html
@@ -214,7 +214,8 @@ </div> <!-- Usage stats toggle row --> - <div id="usageStats" class="layout horizontal center oobe-optin-row"> + <div id="usageStats" class="layout horizontal center oobe-optin-row" + hidden="[[usageOptinHidden_]]"> <div class="oobe-optin-content flex"> <span id="usageTitle" class="oobe-optin-title"> [[i18nDynamic(locale, 'consolidatedConsentUsageOptInTitle')]]
diff --git a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js index 8fd3d3f..161a1be 100644 --- a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js +++ b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js
@@ -86,6 +86,11 @@ value: false, }, + usageOptinHidden_: { + type: Boolean, + value: false, + }, + backupManaged_: { type: Boolean, value: false, @@ -160,6 +165,7 @@ 'setBackupMode', 'setLocationMode', 'setIsDeviceOwner', + 'setUsageOptinHidden', ]; } // clang-format on @@ -337,7 +343,8 @@ var tosLoader = new WebViewLoader( webview, CONSOLIDATED_CONSENT_ONLINE_LOAD_TIMEOUT_IN_MS, - loadFailureCallback, false /* clear_anchors */, false /* inject_css */); + loadFailureCallback, this.isDemo_ /* clear_anchors */, + false /* inject_css */); tosLoader.setUrl(online_tos_url); } @@ -384,7 +391,8 @@ var tosLoader = new WebViewLoader( webview, CONSOLIDATED_CONSENT_ONLINE_LOAD_TIMEOUT_IN_MS, - loadFailureCallback, false /* clear_anchors */, false /* inject_css */); + loadFailureCallback, this.isDemo_ /* clear_anchors */, + false /* inject_css */); tosLoader.setUrl(online_tos_url); } @@ -601,6 +609,13 @@ } /** + * Hides the entire usage opt-in. + */ + setUsageOptinHidden() { + this.usageOptinHidden_ = true; + } + + /** * Sets current backup and restore mode. * @param {boolean} enabled Defines the state of backup opt in. * @param {boolean} managed Defines whether this setting is set by policy. @@ -622,7 +637,7 @@ /** * Sets isOwner_ property. - * @param {boolean} isOwner Defines whether the current user is the device + * @param {boolean} isOwner Defines whether the current user is the device * owner. */ setIsDeviceOwner(isOwner) {
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/OWNERS b/chrome/browser/resources/chromeos/multidevice_internals/OWNERS index 7027ab73..18377141 100644 --- a/chrome/browser/resources/chromeos/multidevice_internals/OWNERS +++ b/chrome/browser/resources/chromeos/multidevice_internals/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn b/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn index 6e60a75..ed68185a 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn +++ b/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
@@ -26,7 +26,7 @@ excludes = [ "chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js", "chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js", - "chrome://resources/mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js", + "chrome://resources/mojo/ash/components/multidevice/mojom/multidevice_types.mojom-lite.js", "chrome://resources/mojo/ash/services/device_sync/public/mojom/device_sync.mojom-lite.js", "chrome://resources/mojo/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom-lite.js", ]
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/OWNERS b/chrome/browser/resources/chromeos/multidevice_setup/OWNERS index 7027ab73..18377141 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/OWNERS +++ b/chrome/browser/resources/chromeos/multidevice_setup/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/resources/discards/BUILD.gn b/chrome/browser/resources/discards/BUILD.gn index c63b532..4d51020 100644 --- a/chrome/browser/resources/discards/BUILD.gn +++ b/chrome/browser/resources/discards/BUILD.gn
@@ -34,6 +34,7 @@ ts_files = [ "discards.ts", + "graph_doc.ts", "sorted_table_mixin.ts", ] + web_component_files @@ -47,15 +48,9 @@ out_folder = "$target_gen_dir/$preprocess_folder" } -# Intentionally excluding graph_tab_template.html.ts since it will be placed in -# the folder by generate_graph_tab. preprocess_if_expr("preprocess_gen") { in_folder = target_gen_dir - in_files = [ - "discards_main.html.ts", - "discards_tab.html.ts", - "database_tab.html.ts", - ] + in_files = html_wrapper_files out_folder = "$target_gen_dir/$preprocess_folder" deps = [ ":html_wrapper_files" ] } @@ -66,21 +61,20 @@ action("generate_graph_tab") { script = "generate_graph_tab.py" sources = [ - "graph_doc.js", + "graph_doc.ts", "graph_doc_template.html", "graph_tab_template.html", ] - outputs = [ "$target_gen_dir/$preprocess_folder/graph_tab_template.html.ts" ] + outputs = [ "$target_gen_dir/graph_tab/graph_tab_template.html.js" ] args = rebase_path(outputs, root_build_dir) + + rebase_path([ "graph_doc_template.html" ], root_build_dir) + rebase_path([ - "graph_doc_template.html", - "graph_doc.js", + "$target_gen_dir/tsc/graph_doc.js", + "$target_gen_dir/tsc/graph_tab_template.html.js", ], - root_build_dir) + - rebase_path([ "$target_gen_dir/graph_tab_template.html.ts" ], root_build_dir) - deps = [ ":html_wrapper_files" ] + deps = [ ":build_ts" ] } # Action with a transparent name. @@ -108,6 +102,10 @@ root_dir = "$target_gen_dir/$preprocess_folder" out_dir = "$target_gen_dir/$tsc_folder" tsconfig_base = "tsconfig_base.json" + manifest_excludes = [ + "graph_doc.ts", + "graph_tab_template.html.ts", + ] in_files = ts_files + html_wrapper_files + [ "discards.mojom-webui.js", "site_data.mojom-webui.js", @@ -121,17 +119,29 @@ extra_deps = [ ":copy_mojo", - ":generate_graph_tab", ":preprocess", ":preprocess_gen", ] } +generate_grd("build_graph_tab_grdp") { + input_files = [ "graph_tab_template.html.js" ] + input_files_base_dir = + rebase_path("$target_gen_dir/graph_tab", root_build_dir) + deps = [ ":generate_graph_tab" ] + grd_prefix = "discards" + out_grd = "$target_gen_dir/discards_graph_tab.grdp" +} + generate_grd("build_grd") { input_files = [ "discards.html" ] input_files_base_dir = rebase_path(".", "//") - deps = [ ":build_ts" ] + deps = [ + ":build_graph_tab_grdp", + ":build_ts", + ] manifest_files = [ "$target_gen_dir/tsconfig.manifest" ] + grdp_files = [ "$target_gen_dir/discards_graph_tab.grdp" ] grd_prefix = "discards" out_grd = "$target_gen_dir/${grd_prefix}_resources.grd" }
diff --git a/chrome/browser/resources/discards/generate_graph_tab.py b/chrome/browser/resources/discards/generate_graph_tab.py index 5c60ee4..026e495 100644 --- a/chrome/browser/resources/discards/generate_graph_tab.py +++ b/chrome/browser/resources/discards/generate_graph_tab.py
@@ -12,13 +12,17 @@ import sys -def strip_js_imports(js_contents): - """The input JS may use imports for Closure compilation. These must be - stripped from the output since the resulting data: URL cannot use imports - within its webview.""" - def not_an_import(line): - return not line.startswith('import ') - return '\n'.join(filter(not_an_import, js_contents.splitlines())) +def strip_js_exports(js_contents): + """The input JS may use imports for TS compilation, but these should be used + for types only and therefore should be stripped by TS compiler. + TS compiler also generates an "export" statement - remove this. """ + lines = js_contents.splitlines(); + for line in lines: + assert not line.startswith('import ') + + def not_an_export(line): + return not line.startswith('export '); + return '\n'.join(filter(not_an_export, lines)) def main(): @@ -33,18 +37,24 @@ # Slurp the input files. js_file_contents = open(args.javascript_file, 'r').read(); html_template = string.Template(open(args.html_template, 'r').read()); - output_template = string.Template(open(args.output_template, 'r').read()); + + # Note: Don't turn this into a template. It is a .html.js file output from the + # TS compiler. TS compiler complains about not finding values if it sees + # "${foo}" template syntax, so the input html file needs to use a different + # pattern (see DATA_URL_PLACEHOLDER below). + output_template = open(args.output_template, 'r').read(); # Stamp the javacript contents into the HTML template. html_doc = html_template.substitute( - {'javascript_file': strip_js_imports(js_file_contents)}) + {'javascript_file': strip_js_exports(js_file_contents)}) # Construct the data: URL that contains the combined doc. data_url = "data:text/html;base64,%s" % base64.b64encode( html_doc.encode()).decode() # And finally stamp the the data URL into the output template. - output = output_template.safe_substitute({'data_url': data_url}) + DATA_URL_PLACEHOLDER = '{__data_url__}'; + output = output_template.replace(DATA_URL_PLACEHOLDER, data_url); current_contents = '' if os.path.isfile(args.output_file):
diff --git a/chrome/browser/resources/discards/graph_doc.js b/chrome/browser/resources/discards/graph_doc.ts similarity index 61% rename from chrome/browser/resources/discards/graph_doc.js rename to chrome/browser/resources/discards/graph_doc.ts index ae6f0b1..18eb6dc7 100644 --- a/chrome/browser/resources/discards/graph_doc.js +++ b/chrome/browser/resources/discards/graph_doc.ts
@@ -7,66 +7,59 @@ // WebUI is through postMessage. // Note that these imports are stripped by a build step before being packaged. -// They're only present to help Closure compiler do type checks and must be -// referenced only within Closure annotations. -import {FavIconInfo, FrameInfo, GraphChangeStreamInterface, PageInfo, ProcessInfo, WorkerInfo} from './chrome/browser/ui/webui/discards/discards.mojom-webui.js'; +// These are used for types only. +import {FavIconInfo, FrameInfo, GraphChangeStreamInterface, PageInfo, ProcessInfo, WorkerInfo} from './discards.mojom-webui.js'; // Radius of a node circle. -const /** number */ kNodeRadius = 6; +const kNodeRadius: number = 6; // Target y position for page nodes. -const /** number */ kPageNodesTargetY = 20; +const kPageNodesTargetY: number = 20; // Range occupied by page nodes at the top of the graph view. -const /** number */ kPageNodesYRange = 100; +const kPageNodesYRange: number = 100; // Range occupied by process nodes at the bottom of the graph view. -const /** number */ kProcessNodesYRange = 100; +const kProcessNodesYRange: number = 100; // Range occupied by worker nodes at the bottom of the graph view, above // process nodes. -const /** number */ kWorkerNodesYRange = 200; +const kWorkerNodesYRange: number = 200; // Target y position for frame nodes. -const /** number */ kFrameNodesTargetY = kPageNodesYRange + 50; +const kFrameNodesTargetY: number = kPageNodesYRange + 50; // Range that frame nodes cannot enter at the top/bottom of the graph view. -const /** number */ kFrameNodesTopMargin = kPageNodesYRange; -const /** number */ kFrameNodesBottomMargin = kWorkerNodesYRange + 50; +const kFrameNodesTopMargin: number = kPageNodesYRange; +const kFrameNodesBottomMargin: number = kWorkerNodesYRange + 50; // The maximum strength of a boundary force. // According to https://github.com/d3/d3-force#positioning, strength values // outside the range [0,1] are "not recommended". -const /** number */ kMaxBoundaryStrength = 1; +const kMaxBoundaryStrength: number = 1; // The strength of a high Y-force. This is appropriate for forces that // strongly pull towards an attractor, but can still be overridden by the // strongest force. -const /** number */ kHighYStrength = 0.9; +const kHighYStrength: number = 0.9; // The strength of a weak Y-force. This is appropriate for forces that exert // some influence but can be easily overridden. -const /** number */ kWeakYStrength = 0.1; +const kWeakYStrength: number = 0.1; class ToolTip { - /** - * @param {Element} div - * @param {GraphNode} node - */ - constructor(div, node) { - /** @type {boolean} */ - this.floating = true; + floating: boolean = true; + x: number; + y: number; + node: GraphNode; + private div_: d3.Selection<HTMLDivElement, unknown, null, undefined>; + private description_json_: string = ''; - /** @type {number} */ + constructor(div: Element, node: GraphNode) { this.x = node.x; - - /** @type {number} */ this.y = node.y - 28; - - /** @type {GraphNode} */ this.node = node; - /** @private {d3.selection} */ this.div_ = d3.select(div) .append('div') .attr('class', 'tooltip') @@ -76,10 +69,9 @@ this.div_.append('table').append('tbody'); this.div_.transition().duration(200).style('opacity', .9); - /** @private {string} */ - this.description_json_ = ''; // Set up a drag behavior for this object's div. - const drag = d3.drag().subject(() => this); + const drag = d3.drag().subject(() => this) as unknown as + d3.DragBehavior<HTMLDivElement, unknown, unknown>; drag.on('start', this.onDragStart_.bind(this)); drag.on('drag', this.onDrag_.bind(this)); this.div_.call(drag); @@ -99,11 +91,10 @@ } /** - * @return {!Array<number>} The [x, y] center of the ToolTip's div - * element. + * @return The [x, y] center of the ToolTip's div element. */ - getCenter() { - const rect = this.div_.node().getBoundingClientRect(); + getCenter(): [number, number] { + const rect = this.div_.node()!.getBoundingClientRect(); return [rect.x + rect.width / 2, rect.y + rect.height / 2]; } @@ -113,9 +104,8 @@ /** * Updates the description displayed. - * @param {string} description_json A JSON string. */ - onDescription(description_json) { + onDescription(description_json: string) { if (this.description_json_ === description_json) { return; } @@ -123,13 +113,15 @@ /** * Helper for recursively flattening an Object. * - * @param {!Set} visited The set of visited objects, excluding + * @param visited The set of visited objects, excluding * {@code object}. - * @param {!Object<?,?>} flattened The flattened object being built. - * @param {string} path The current flattened path. - * @param {!Object<?,?>} object The nested dict to be flattened. + * @param flattened The flattened object being built. + * @param path The current flattened path. + * @param object The nested dict to be flattened. */ - function flattenObjectRec(visited, flattened, path, object) { + function flattenObjectRec( + visited: Set<object>, flattened: {[key: string]: any}, path: string, + object: {[key: string]: any}) { if (typeof object !== 'object' || visited.has(object)) { return; } @@ -176,10 +168,8 @@ * 'baz.z.0': '1', * 'baz.y.1': '2' * } - * @param {!Object<?,?>} object The object to be flattened. - * @return {!Object<?,?>} the flattened object. */ - function flattenObject(object) { + function flattenObject(object: {[key: string]: any}): {[key: string]: any} { const flattened = {}; flattenObjectRec(new Set(), flattened, '', object); return flattened; @@ -191,12 +181,11 @@ // 'heading' with [`the describer's name`, null], followed by some number of // entries with a two-element list, each representing a key/value pair. this.description_json_ = description_json; - const description = - /** @type {!Object<?,?>} */ (JSON.parse(description_json)); + const description = JSON.parse(description_json); const flattenedDescription = []; for (const [title, value] of Object.entries(description)) { flattenedDescription.push([title, null]); - const flattenedValue = flattenObject(value); + const flattenedValue = flattenObject(value as {[key: string]: any}); for (const [propName, propValue] of Object.entries(flattenedValue)) { let strValue = String(propValue); if (strValue.length > 50) { @@ -215,71 +204,67 @@ tr.exit().remove(); tr = this.div_.selectAll('tr'); - tr.select('td').attr('colspan', function(d) { - return (d3.select(this.parentElement).datum()[1] === null) ? 2 : null; + tr.select('td').attr('colspan', function(_d: any) { + return ((d3.select((this as HTMLElement).parentElement!).datum() as + Array<any>)[1] === null) ? + 2 : + null; }); - tr = tr.attr('class', d => d[1] === null ? 'heading' : 'value'); - tr.selectAll('td').data(d => d).text(d => d === null ? '' : d); + tr = tr.attr( + 'class', + (d: unknown) => + (d as Array<string|null>)[1] === null ? 'heading' : 'value'); + tr.selectAll('td').data(d => d).text( + (d: unknown) => d === null ? '' : d as string); } - /** @private */ - onDragStart_() { + private onDragStart_() { this.floating = false; } - /** @private */ - onDrag_() { + private onDrag_() { this.x = d3.event.x; this.y = d3.event.y; this.div_.style('left', `${this.x}px`).style('top', `${this.y}px`); - graph.updateToolTipLinks(); + graph!.updateToolTipLinks(); } } -/** @implements {d3.ForceNode} */ -class GraphNode { - constructor(id) { - /** @type {bigint} */ +class GraphNode implements d3.SimulationNodeDatum { + id: bigint; + color: string = 'black'; + iconUrl: string = ''; + tooltip: ToolTip|null = null; + + /** + * Implementation of the d3.SimulationNodeDatum interface. + * See https://github.com/d3/d3-force#simulation_nodes. + */ + index?: number; + x: number; + y: number; + vx?: number; + vy?: number; + fx: number|null = null; + fy: number|null = null; + + + constructor(id: bigint) { this.id = id; - /** @type {string} */ - this.color = 'black'; - /** @type {string} */ - this.iconUrl = ''; - - /** @type {ToolTip} */ - this.tooltip = null; - - /** - * Implementation of the d3.ForceNode interface. - * See https://github.com/d3/d3-force#simulation_nodes. - * @type {number|undefined} - */ - this.index; - /** @type {number} */ - this.x; - /** @type {number} */ - this.y; - /** @type {number|undefined} */ - this.vx; - /** @type {number|undefined} */ - this.vy; - this.fx = null; - this.fy = null; } - /** @return {string} */ - get title() { + get title(): string { return ''; } /** * Sets the initial x and y position of this node, also resets * vx and vy. - * @param {number} graphWidth Width of the graph view (svg). - * @param {number} graphHeight Height of the graph view (svg). + * @param graphWidth Width of the graph view (svg). + * @param graphHeight Height of the graph view (svg). */ - setInitialPosition(graphWidth, graphHeight) { + setInitialPosition(graphWidth: number, graphHeight: number) { this.x = graphWidth / 2; this.y = this.targetYPosition(graphHeight); this.vx = 0; @@ -287,46 +272,44 @@ } /** - * @param {number} graphHeight Height of the graph view (svg). - * @return {number} + * @param graphHeight Height of the graph view (svg). */ - targetYPosition(graphHeight) { + targetYPosition(graphHeight: number): number { const bounds = this.allowedYRange(graphHeight); return (bounds[0] + bounds[1]) / 2; } /** - * @return {number} The strength of the force that pulls the node towards + * @return The strength of the force that pulls the node towards * its target y position. */ - get targetYPositionStrength() { + get targetYPositionStrength(): number { return kWeakYStrength; } /** - * @return {number} A scaling factor applied to the strength of links to this + * @return A scaling factor applied to the strength of links to this * node. */ - get linkStrengthScalingFactor() { + get linkStrengthScalingFactor(): number { return 1; } /** - * @param {number} graphHeight Height of the graph view. - * @return {!Array<number>} + * @param graphHeight Height of the graph view. */ - allowedYRange(graphHeight) { + allowedYRange(graphHeight: number): [number, number] { // By default, nodes just need to be in bounds of the graph. return [0, graphHeight]; } - /** @return {number} The strength of the repulsion force with other nodes. */ - get manyBodyStrength() { + /** @return The strength of the repulsion force with other nodes. */ + get manyBodyStrength(): number { return -200; } - /** @return {!Array<bigint>} an array of node ids. */ - get linkTargets() { + /** @return an array of node ids. */ + get linkTargets(): bigint[] { return []; } @@ -335,18 +318,17 @@ * things, but be owned by exactly one (per relationship type). As such, the * relationship is expressed on the *owned* object. These links are drawn with * an arrow at the beginning of the link, pointing to the owned object. - * @return {!Array<bigint>} an array of node ids. + * @return an array of node ids. */ - get dashedLinkTargets() { + get dashedLinkTargets(): bigint[] { return []; } /** * Selects a color string from an id. - * @param {bigint} id The id the returned color is selected from. - * @return {string} + * @param id The id the returned color is selected from. */ - selectColor(id) { + selectColor(id: bigint): string { if (id < 0) { id = -id; } @@ -355,47 +337,41 @@ } class PageNode extends GraphNode { - /** @param {!PageInfo} page */ - constructor(page) { + page: PageInfo; + + constructor(page: PageInfo) { super(page.id); - /** @type {!PageInfo} */ this.page = page; this.y = kPageNodesTargetY; } - /** override */ - get title() { + override get title() { return this.page.mainFrameUrl.url.length > 0 ? this.page.mainFrameUrl.url : 'Page'; } - /** @override */ - get targetYPositionStrength() { + override get targetYPositionStrength() { // Gravitate strongly towards the top of the graph. Can be overridden by // the bounding force which uses kMaxBoundaryStrength. return kHighYStrength; } - /** @override */ - get linkStrengthScalingFactor() { + override get linkStrengthScalingFactor() { // Give links from frame nodes to page nodes less weight than links between // frame nodes, so the that Y forces pulling page nodes into their area can // dominate over link forces pulling them towards frame nodes. return 0.5; } - /** override */ - allowedYRange(graphHeight) { + override allowedYRange(_graphHeight: number): [number, number] { return [0, kPageNodesYRange]; } - /** override */ - get manyBodyStrength() { + override get manyBodyStrength() { return -600; } - /** override */ - get dashedLinkTargets() { + override get dashedLinkTargets() { const targets = []; if (this.page.openerFrameId) { targets.push(this.page.openerFrameId); @@ -408,31 +384,27 @@ } class FrameNode extends GraphNode { - /** @param {!FrameInfo} frame */ - constructor(frame) { + frame: FrameInfo; + + constructor(frame: FrameInfo) { super(frame.id); - /** @type {!FrameInfo} frame */ this.frame = frame; this.color = this.selectColor(frame.processId); } - /** override */ - get title() { + override get title() { return this.frame.url.url.length > 0 ? this.frame.url.url : 'Frame'; } - /** override */ - targetYPosition(graphHeight) { + override targetYPosition(_graphHeight: number) { return kFrameNodesTargetY; } - /** override */ - allowedYRange(graphHeight) { + override allowedYRange(graphHeight: number): [number, number] { return [kFrameNodesTopMargin, graphHeight - kFrameNodesBottomMargin]; } - /** override */ - get linkTargets() { + override get linkTargets() { // Only link to the page if there isn't a parent frame. return [ this.frame.parentFrameId || this.frame.pageId, this.frame.processId @@ -441,82 +413,72 @@ } class ProcessNode extends GraphNode { - /** @param {!ProcessInfo} process */ - constructor(process) { + process: ProcessInfo; + + constructor(process: ProcessInfo) { super(process.id); - /** @type {!ProcessInfo} */ this.process = process; this.color = this.selectColor(process.id); } - /** override */ - get title() { + override get title() { return `PID: ${this.process.pid.pid}`; } - /** @return {number} */ - get targetYPositionStrength() { + override get targetYPositionStrength() { // Gravitate strongly towards the bottom of the graph. Can be overridden by // the bounding force which uses kMaxBoundaryStrength. return kHighYStrength; } - /** @override */ - get linkStrengthScalingFactor() { + override get linkStrengthScalingFactor() { // Give links to process nodes less weight than links between frame nodes, // so the that Y forces pulling process nodes into their area can dominate // over link forces pulling them towards frame nodes. return 0.5; } - /** override */ - allowedYRange(graphHeight) { + override allowedYRange(graphHeight: number): [number, number] { return [graphHeight - kProcessNodesYRange, graphHeight]; } - /** override */ - get manyBodyStrength() { + override get manyBodyStrength() { return -600; } } class WorkerNode extends GraphNode { - /** @param {!WorkerInfo} worker */ - constructor(worker) { + worker: WorkerInfo; + + constructor(worker: WorkerInfo) { super(worker.id); - /** @type {!WorkerInfo} */ this.worker = worker; this.color = this.selectColor(worker.processId); } - /** override */ - get title() { + override get title() { return this.worker.url.url.length > 0 ? this.worker.url.url : 'Worker'; } - /** @return {number} */ - get targetYPositionStrength() { + override get targetYPositionStrength() { // Gravitate strongly towards the worker area of the graph. Can be // overridden by the bounding force which uses kMaxBoundaryStrength. return kHighYStrength; } - /** override */ - allowedYRange(graphHeight) { + override allowedYRange(graphHeight: number): [number, number] { return [ graphHeight - kWorkerNodesYRange, graphHeight - kProcessNodesYRange ]; } - /** override */ - get manyBodyStrength() { + override get manyBodyStrength() { return -600; } - /** override */ - get linkTargets() { + override get linkTargets() { // Link the process, in addition to all the client and child workers. return [ this.worker.processId, ...this.worker.clientFrameIds, @@ -528,20 +490,16 @@ /** * A force that bounds GraphNodes |allowedYRange| in Y, * as well as bounding them to stay in page bounds in X. - * @param {number} graphHeight - * @param {number} graphWidth */ -function boundingForce(graphHeight, graphWidth) { - /** @type {!Array<!GraphNode>} */ - let nodes = []; - /** @type {!Array<!Array>} */ - let bounds = []; - const xBounds = [2 * kNodeRadius, graphWidth - 2 * kNodeRadius]; - const boundPosition = (pos, bound) => +function boundingForce(graphHeight: number, graphWidth: number) { + let nodes: GraphNode[] = []; + let bounds: Array<[number, number]> = []; + const xBounds: [number, number] = + [2 * kNodeRadius, graphWidth - 2 * kNodeRadius]; + const boundPosition = (pos: number, bound: [number, number]) => Math.max(bound[0], Math.min(pos, bound[1])); - /** @param {number} alpha */ - function force(alpha) { + function force(_alpha: number) { const n = nodes.length; for (let i = 0; i < n; ++i) { const bound = bounds[i]; @@ -549,26 +507,25 @@ // Calculate where the node will end up after movement. If it will be out // of bounds apply a counter-force to bring it back in. - const yNextPosition = node.y + node.vy; + const yNextPosition = node.y + node.vy!; const yBoundedPosition = boundPosition(yNextPosition, bound); if (yNextPosition !== yBoundedPosition) { // Do not include alpha because we want to be strongly repelled from // the boundary even if alpha has decayed. - node.vy += (yBoundedPosition - yNextPosition) * kMaxBoundaryStrength; + node.vy! += (yBoundedPosition - yNextPosition) * kMaxBoundaryStrength; } - const xNextPosition = node.x + node.vx; + const xNextPosition = node.x + node.vx!; const xBoundedPosition = boundPosition(xNextPosition, xBounds); if (xNextPosition !== xBoundedPosition) { // Do not include alpha because we want to be strongly repelled from // the boundary even if alpha has decayed. - node.vx += (xBoundedPosition - xNextPosition) * kMaxBoundaryStrength; + node.vx! += (xBoundedPosition - xNextPosition) * kMaxBoundaryStrength; } } } - /** @param {!Array<!GraphNode>} n */ - force.initialize = function(n) { + force.initialize = function(n: GraphNode[]) { nodes = n; bounds = nodes.map(node => { const nodeBounds = node.allowedYRange(graphHeight); @@ -582,99 +539,42 @@ return force; } -/** - * @implements {GraphChangeStreamInterface} - */ -class Graph { - /** - * TODO(siggi): This should be SVGElement, but closure doesn't have externs - * for this yet. - * @param {Element} svg - * @param {Element} div - */ - constructor(svg, div) { - /** - * TODO(siggi): SVGElement. - * @private {Element} - */ +class Graph implements GraphChangeStreamInterface { + private svg_: SVGElement; + private div_: Element; + private wasResized_: boolean = false; + private width_: number = 0; + private height_: number = 0; + private simulation_: d3.Simulation<GraphNode, undefined>|null = null; + /** A selection for the top-level <g> node that contains all tooltip links. */ + private toolTipLinkGroup_: + d3.Selection<SVGGElement, unknown, null, undefined>|null = null; + /** A selection for the top-level <g> node that contains all separators. */ + private separatorGroup_: d3.Selection<SVGGElement, unknown, null, undefined>| + null = null; + /** A selection for the top-level <g> node that contains all nodes. */ + private nodeGroup_: d3.Selection<SVGGElement, unknown, null, undefined>|null = + null; + /** A selection for the top-level <g> node that contains all edges. */ + private linkGroup_: d3.Selection< + SVGGElement, d3.SimulationLinkDatum<GraphNode>, null, undefined>|null = + null; + /** A selection for the top-level <g> node that contains all dashed edges. */ + private dashedLinkGroup_: d3.Selection< + SVGGElement, d3.SimulationLinkDatum<GraphNode>, null, undefined>|null = + null; + private nodes_: Map<bigint, GraphNode> = new Map(); + private links_: d3.SimulationLinkDatum<GraphNode>[] = []; + private dashedLinks_: d3.SimulationLinkDatum<GraphNode>[] = []; + private hostWindow_: Window|null = null; + /** The interval timer used to poll for node descriptions. */ + private pollDescriptionsInterval_: number = 0; + /** The d3.drag instance applied to nodes. */ + private drag_: d3.DragBehavior<SVGGElement, GraphNode, unknown>|null = null; + + constructor(svg: SVGElement, div: Element) { this.svg_ = svg; - - /** @private {Element} */ this.div_ = div; - - /** @private {boolean} */ - this.wasResized_ = false; - - /** @private {number} */ - this.width_ = 0; - /** @private {number} */ - this.height_ = 0; - - /** @private {d3.ForceSimulation} */ - this.simulation_ = null; - - /** - * A selection for the top-level <g> node that contains all tooltip links. - * @private {d3.selection} - */ - this.toolTipLinkGroup_ = null; - - /** - * A selection for the top-level <g> node that contains all separators. - * @private {d3.selection} - */ - this.separatorGroup_ = null; - - /** - * A selection for the top-level <g> node that contains all nodes. - * @private {d3.selection} - */ - this.nodeGroup_ = null; - - /** - * A selection for the top-level <g> node that contains all edges. - * @private {d3.selection} - */ - this.linkGroup_ = null; - - /** - * A selection for the top-level <g> node that contains all dashed edges. - * @private {d3.selection} - */ - this.dashedLinkGroup_ = null; - - /** @private {!Map<bigint, !GraphNode>} */ - this.nodes_ = new Map(); - - /** - * The links. - * @private {!Array<!d3.ForceLink>} - */ - this.links_ = []; - - /** - * The dashed links. - * @private {!Array<!d3.ForceLink>} - */ - this.dashedLinks_ = []; - - /** - * The host window. - * @private {Window} - */ - this.hostWindow_ = null; - - /** - * The interval timer used to poll for node descriptions. - * @private {number} - */ - this.pollDescriptionsInterval_ = 0; - - /** - * The d3.drag instance applied to nodes. - * @private {?d3.Drag} - */ - this.drag_ = null; } initialize() { @@ -687,10 +587,14 @@ window.addEventListener('resize', this.onResize_.bind(this)); // Create the simulation and set up the permanent forces. - const simulation = d3.forceSimulation(); + const simulation = + d3.forceSimulation() as d3.Simulation<GraphNode, undefined>; simulation.on('tick', this.onTick_.bind(this)); - const linkForce = d3.forceLink().id(d => d.id); + const linkForce = + (d3.forceLink() as + d3.ForceLink<GraphNode, d3.SimulationLinkDatum<GraphNode>>) + .id(d => d.id.toString()); const defaultStrength = linkForce.strength(); // Override the default link strength function to apply scaling factors @@ -700,14 +604,16 @@ simulation.force( 'link', linkForce.strength( - l => defaultStrength(l) * l.source.linkStrengthScalingFactor * - l.target.linkStrengthScalingFactor)); + (l, i, n) => defaultStrength(l, i, n) * + (l.source as GraphNode).linkStrengthScalingFactor * + (l.target as GraphNode).linkStrengthScalingFactor)); // Sets the repulsion force between nodes (positive number is attraction, // negative number is repulsion). simulation.force( 'charge', - d3.forceManyBody().strength(this.getManyBodyStrength_.bind(this))); + (d3.forceManyBody() as d3.ForceManyBody<GraphNode>) + .strength(this.getManyBodyStrength_.bind(this))); this.simulation_ = simulation; @@ -715,12 +621,16 @@ // The link groups are created first so that all links end up behind nodes. const svg = d3.select(this.svg_); this.toolTipLinkGroup_ = svg.append('g').attr('class', 'tool-tip-links'); - this.linkGroup_ = svg.append('g').attr('class', 'links'); - this.dashedLinkGroup_ = svg.append('g').attr('class', 'dashed-links'); + this.linkGroup_ = + svg.append('g').attr('class', 'links') as d3.Selection< + SVGGElement, d3.SimulationLinkDatum<GraphNode>, null, undefined>; + this.dashedLinkGroup_ = + svg.append('g').attr('class', 'dashed-links') as d3.Selection< + SVGGElement, d3.SimulationLinkDatum<GraphNode>, null, undefined>; this.nodeGroup_ = svg.append('g').attr('class', 'nodes'); this.separatorGroup_ = svg.append('g').attr('class', 'separators'); - const drag = d3.drag(); + const drag = d3.drag() as d3.DragBehavior<any, GraphNode, unknown>; drag.clickDistance(4); drag.on('start', this.onDragStart_.bind(this)); drag.on('drag', this.onDrag_.bind(this)); @@ -728,35 +638,29 @@ this.drag_ = drag; } - /** @override */ - frameCreated(frame) { + frameCreated(frame: FrameInfo) { this.addNode_(new FrameNode(frame)); } - /** @override */ - pageCreated(page) { + pageCreated(page: PageInfo) { this.addNode_(new PageNode(page)); } - /** @override */ - processCreated(process) { + processCreated(process: ProcessInfo) { this.addNode_(new ProcessNode(process)); } - /** @override */ - workerCreated(worker) { + workerCreated(worker: WorkerInfo) { this.addNode_(new WorkerNode(worker)); } - /** @override */ - frameChanged(frame) { - const frameNode = /** @type {!FrameNode} */ (this.nodes_.get(frame.id)); + frameChanged(frame: FrameInfo) { + const frameNode = this.nodes_.get(frame.id) as FrameNode; frameNode.frame = frame; } - /** @override */ - pageChanged(page) { - const pageNode = /** @type {!PageNode} */ (this.nodes_.get(page.id)); + pageChanged(page: PageInfo) { + const pageNode = this.nodes_.get(page.id) as PageNode; // Page node dashed links may change dynamically, so account for that here. this.removeDashedNodeLinks_(pageNode); @@ -764,17 +668,13 @@ this.addDashedNodeLinks_(pageNode); } - /** @override */ - processChanged(process) { - const processNode = - /** @type {!ProcessNode} */ (this.nodes_.get(process.id)); + processChanged(process: ProcessInfo) { + const processNode = this.nodes_.get(process.id) as ProcessNode; processNode.process = process; } - /** @override */ - workerChanged(worker) { - const workerNode = - /** @type {!WorkerNode} */ (this.nodes_.get(worker.id)); + workerChanged(worker: WorkerInfo) { + const workerNode = this.nodes_.get(worker.id) as WorkerNode; // Worker node links may change dynamically, so account for that here. this.removeNodeLinks_(workerNode); @@ -782,17 +682,15 @@ this.addNodeLinks_(workerNode); } - /** @override */ - favIconDataAvailable(iconInfo) { + favIconDataAvailable(iconInfo: FavIconInfo) { const graphNode = this.nodes_.get(iconInfo.nodeId); if (graphNode) { graphNode.iconUrl = 'data:image/png;base64,' + iconInfo.iconData; } } - /** @override */ - nodeDeleted(nodeId) { - const node = this.nodes_.get(nodeId); + nodeDeleted(nodeId: bigint) { + const node = this.nodes_.get(nodeId)!; // Remove any links, and then the node itself. this.removeNodeLinks_(node); @@ -815,52 +713,46 @@ } } - function setLineEndpoints(d) { - const line = d3.select(this); + function setLineEndpoints( + d: ToolTip, line: d3.Selection<any, unknown, null, unknown>) { const center = d.getCenter(); - line.attr('x1', d => center[0]) - .attr('y1', d => center[1]) - .attr('x2', d => d.node.x) - .attr('y2', d => d.node.y); + line.attr('x1', _d => center[0]) + .attr('y1', _d => center[1]) + .attr('x2', d => (d as {node: {x: number, y: number}}).node.x) + .attr('y2', d => (d as {node: {x: number, y: number}}).node.y); } const toolTipLinks = - this.toolTipLinkGroup_.selectAll('line').data(pinnedTooltips); + this.toolTipLinkGroup_!.selectAll('line').data(pinnedTooltips); toolTipLinks.enter() .append('line') .attr('stroke', 'LightGray') .attr('stroke-dasharray', '1') .attr('stroke-opacity', '0.8') - .each(setLineEndpoints); - toolTipLinks.each(setLineEndpoints); + .each(function(d: ToolTip) { + const line = d3.select(this); + setLineEndpoints(d, line); + }); + toolTipLinks.each(function(d: ToolTip) { + const line = d3.select(this); + setLineEndpoints(d, line); + }); toolTipLinks.exit().remove(); } - /** - * @param {!GraphNode} node - * @private - */ - removeNodeLinks_(node) { + private removeNodeLinks_(node: GraphNode) { // Filter away any links to or from the provided node. this.links_ = this.links_.filter( link => link.source !== node && link.target !== node); } - /** - * @param {!GraphNode} node - * @private - */ - removeDashedNodeLinks_(node) { + private removeDashedNodeLinks_(node: GraphNode) { // Filter away any dashed links to or from the provided node. this.dashedLinks_ = this.dashedLinks_.filter( link => link.source !== node && link.target !== node); } - /** - * @param {!Object<string>} nodeDescriptions - * @private - */ - nodeDescriptions_(nodeDescriptions) { + private nodeDescriptions_(nodeDescriptions: {[key: string]: any}) { for (const nodeId in nodeDescriptions) { const node = this.nodes_.get(BigInt(nodeId)); if (node && node.tooltip) { @@ -869,11 +761,8 @@ } } - /** - * @private - */ - pollForNodeDescriptions_() { - const nodeIds = []; + private pollForNodeDescriptions_() { + const nodeIds: bigint[] = []; for (const node of this.nodes_.values()) { if (node.tooltip) { nodeIds.push(node.id); @@ -881,7 +770,7 @@ } if (nodeIds.length) { - this.hostWindow_.postMessage(['requestNodeDescriptions', nodeIds], '*'); + this.hostWindow_!.postMessage(['requestNodeDescriptions', nodeIds], '*'); if (this.pollDescriptionsInterval_ === 0) { // Start polling if not already in progress. @@ -896,69 +785,55 @@ } /** - * @param {!Event} event A graph update event posted from the WebUI. - * @private + * @param event A graph update event posted from the WebUI. */ - onMessage_(event) { + private onMessage_(event: MessageEvent) { if (!this.hostWindow_) { - this.hostWindow_ = event.source; + this.hostWindow_ = event.source as Window; } - const type = /** @type {string} */ (event.data[0]); - const data = /** @type {Object|number|bigint} */ (event.data[1]); + const type = event.data[0] as string; + const data = event.data[1]; switch (type) { case 'frameCreated': - this.frameCreated( - /** @type {!FrameInfo} */ (data)); + this.frameCreated(data as FrameInfo); break; case 'pageCreated': - this.pageCreated( - /** @type {!PageInfo} */ (data)); + this.pageCreated(data as PageInfo); break; case 'processCreated': - this.processCreated( - /** @type {!ProcessInfo} */ (data)); + this.processCreated(data as ProcessInfo); break; case 'workerCreated': - this.workerCreated( - /** @type {!WorkerInfo} */ (data)); + this.workerCreated(data as WorkerInfo); break; case 'frameChanged': - this.frameChanged( - /** @type {!FrameInfo} */ (data)); + this.frameChanged(data as FrameInfo); break; case 'pageChanged': - this.pageChanged( - /** @type {!PageInfo} */ (data)); + this.pageChanged(data as PageInfo); break; case 'processChanged': - this.processChanged( - /** @type {!ProcessInfo} */ (data)); + this.processChanged(data as ProcessInfo); break; case 'favIconDataAvailable': - this.favIconDataAvailable( - /** @type {!FavIconInfo} */ (data)); + this.favIconDataAvailable(data as FavIconInfo); break; case 'workerChanged': - this.workerChanged( - /** @type {!WorkerInfo} */ (data)); + this.workerChanged(data as WorkerInfo); break; case 'nodeDeleted': - this.nodeDeleted(/** @type {bigint} */ (data)); + this.nodeDeleted(data as bigint); break; case 'nodeDescriptions': - this.nodeDescriptions_(/** @type {!Object<string>} */ (data)); + this.nodeDescriptions_(data as {[key: string]: any}); break; } this.render_(); } - /** - * @param {GraphNode} node - * @private - */ - onGraphNodeClick_(node) { + private onGraphNodeClick_(node: GraphNode) { if (node.tooltip) { node.tooltip.goAway(); node.tooltip = null; @@ -983,12 +858,10 @@ * available. * Deleted nodes are classed '.dead', and CSS takes care of hiding their * image element if it's been populated with an icon. - * - * @private */ - render_() { + private render_() { // Select the links. - const link = this.linkGroup_.selectAll('line').data(this.links_); + const link = this.linkGroup_!.selectAll('line').data(this.links_); // Add new links. link.enter().append('line'); // Remove dead links. @@ -996,7 +869,7 @@ // Select the dashed links. const dashedLink = - this.dashedLinkGroup_.selectAll('line').data(this.dashedLinks_); + this.dashedLinkGroup_!.selectAll('line').data(this.dashedLinks_); // Add new dashed links. dashedLink.enter().append('line'); // Remove dead dashed links. @@ -1004,14 +877,15 @@ // Select the nodes, except for any dead ones that are still transitioning. const nodes = Array.from(this.nodes_.values()); - const node = - this.nodeGroup_.selectAll('g:not(.dead)').data(nodes, d => d.id); + const node = (this.nodeGroup_!.selectAll('g:not(.dead)') as + d3.Selection<any, GraphNode, SVGGElement, unknown>) + .data(nodes, d => d.id as unknown as number); // Add new nodes, if any. if (!node.enter().empty()) { const newNodes = node.enter() .append('g') - .call(this.drag_) + .call(this.drag_!) .on('click', this.onGraphNodeClick_.bind(this)); const circles = newNodes.append('circle') .attr('id', d => `circle-${d.id}`) @@ -1028,14 +902,15 @@ // Transition new nodes to their chosen color in 2 seconds. circles.transition() .duration(2000) - .attr('fill', d => d.color) + .attr('fill', (d: unknown) => (d as {color: string}).color) .attr('r', kNodeRadius); } if (!node.exit().empty()) { // Give dead nodes a distinguishing class to exclude them from the // selection above. - const deletedNodes = node.exit().classed('dead', true); + const deletedNodes = node.exit().classed('dead', true) as + d3.Selection<any, GraphNode, SVGGElement, unknown>; // Interrupt any ongoing transitions. deletedNodes.interrupt(); @@ -1043,7 +918,7 @@ // Turn down the node associated tooltips. deletedNodes.each(d => { if (d.tooltip) { - d.tooltip.goAway(); + d.tooltip!.goAway(); } }); @@ -1059,38 +934,46 @@ } // Update the title for all nodes. - node.selectAll('title').text(d => d.title); + (node.selectAll('title') as d3.Selection<any, GraphNode, any, unknown>) + .text(d => d.title); // Update the favicon for all nodes. - node.selectAll('image').attr('href', d => d.iconUrl); + (node.selectAll('image') as d3.Selection<any, GraphNode, any, unknown>) + .attr('href', d => d.iconUrl); // Update and restart the simulation if the graph changed. if (!node.enter().empty() || !node.exit().empty() || !link.enter().empty() || !link.exit().empty() || !dashedLink.enter().empty() || !dashedLink.exit().empty()) { - this.simulation_.nodes(nodes); + this.simulation_!.nodes(nodes); const links = this.links_.concat(this.dashedLinks_); - this.simulation_.force('link').links(links); + (this.simulation_!.force('link')! as d3.ForceLink<GraphNode, any>) + .links(links); this.restartSimulation_(); } } - /** @private */ - onTick_() { - const nodes = this.nodeGroup_.selectAll('g'); + private onTick_() { + const nodes: d3.Selection<SVGGElement, GraphNode, SVGGElement, unknown> = + this.nodeGroup_!.selectAll('g'); nodes.attr('transform', d => `translate(${d.x},${d.y})`); - const lines = this.linkGroup_.selectAll('line'); - lines.attr('x1', d => d.source.x) - .attr('y1', d => d.source.y) - .attr('x2', d => d.target.x) - .attr('y2', d => d.target.y); + const lines: d3.Selection< + SVGLineElement, d3.SimulationLinkDatum<GraphNode>, SVGGElement, + d3.SimulationLinkDatum<GraphNode>> = this.linkGroup_!.selectAll('line'); + lines.attr('x1', d => (d.source as GraphNode).x) + .attr('y1', d => (d.source as GraphNode).y) + .attr('x2', d => (d.target as GraphNode).x) + .attr('y2', d => (d.target as GraphNode).y); - const dashedLines = this.dashedLinkGroup_.selectAll('line'); - dashedLines.attr('x1', d => d.source.x) - .attr('y1', d => d.source.y) - .attr('x2', d => d.target.x) - .attr('y2', d => d.target.y); + const dashedLines: d3.Selection< + SVGLineElement, d3.SimulationLinkDatum<GraphNode>, SVGGElement, + d3.SimulationLinkDatum<GraphNode>> = + this.dashedLinkGroup_!.selectAll('line'); + dashedLines.attr('x1', d => (d.source as GraphNode).x) + .attr('y1', d => (d.source as GraphNode).y) + .attr('x2', d => (d.target as GraphNode).x) + .attr('y2', d => (d.target as GraphNode).y); this.updateToolTipLinks(); } @@ -1098,11 +981,8 @@ /** * Adds a new node to the graph, populates its links and gives it an initial * position. - * - * @param {!GraphNode} node - * @private */ - addNode_(node) { + private addNode_(node: GraphNode) { this.nodes_.set(node.id, node); this.addNodeLinks_(node); this.addDashedNodeLinks_(node); @@ -1111,11 +991,8 @@ /** * Adds all the links for a node to the graph. - * - * @param {!GraphNode} node - * @private */ - addNodeLinks_(node) { + private addNodeLinks_(node: GraphNode) { for (const linkTarget of node.linkTargets) { const target = this.nodes_.get(linkTarget); if (target) { @@ -1126,11 +1003,8 @@ /** * Adds all the dashed links for a node to the graph. - * - * @param {!GraphNode} node - * @private */ - addDashedNodeLinks_(node) { + private addDashedNodeLinks_(node: GraphNode) { for (const dashedLinkTarget of node.dashedLinkTargets) { const target = this.nodes_.get(dashedLinkTarget); if (target) { @@ -1140,10 +1014,9 @@ } /** - * @param {!GraphNode} d The dragged node. - * @private + * @param d The dragged node. */ - onDragStart_(d) { + private onDragStart_(d: GraphNode) { if (!d3.event.active) { this.restartSimulation_(); } @@ -1152,21 +1025,19 @@ } /** - * @param {!GraphNode} d The dragged node. - * @private + * @param d The dragged node. */ - onDrag_(d) { + private onDrag_(d: GraphNode) { d.fx = d3.event.x; d.fy = d3.event.y; } /** - * @param {!GraphNode} d The dragged node. - * @private + * @param d The dragged node. */ - onDragEnd_(d) { + private onDragEnd_(d: GraphNode) { if (!d3.event.active) { - this.simulation_.alphaTarget(0); + this.simulation_!.alphaTarget(0); } // Leave the node pinned where it was dropped. Return it to free // positioning if it's dropped outside its designated area. @@ -1180,36 +1051,23 @@ d3.select(`#circle-${d.id}`).classed('pinned', d.fx != null); } - /** - * @param {!d3.ForceNode} d The node to position. - * @private - */ - getTargetYPosition_(d) { + private getTargetYPosition_(d: GraphNode): number { return d.targetYPosition(this.height_); } - /** - * @param {!d3.ForceNode} d The node to position. - * @private - */ - getTargetYPositionStrength_(d) { + private getTargetYPositionStrength_(d: GraphNode): number { return d.targetYPositionStrength; } - /** - * @param {!d3.ForceNode} d The node to position. - * @private - */ - getManyBodyStrength_(d) { + private getManyBodyStrength_(d: GraphNode): number { return d.manyBodyStrength; } /** - * @param {number} graphWidth Width of the graph view (svg). - * @param {number} graphHeight Height of the graph view (svg). - * @private + * @param graphWidth Width of the graph view (svg). + * @param graphHeight Height of the graph view (svg). */ - updateSeparators_(graphWidth, graphHeight) { + private updateSeparators_(graphWidth: number, graphHeight: number) { const separators = [ ['Pages', 'Frame Tree', kPageNodesYRange], ['', 'Workers', graphHeight - kWorkerNodesYRange], @@ -1218,10 +1076,10 @@ const kAboveLabelOffset = -6; const kBelowLabelOffset = 14; - const groups = this.separatorGroup_.selectAll('g').data(separators); + const groups = this.separatorGroup_!.selectAll('g').data(separators); if (groups.enter()) { const group = groups.enter().append('g').attr( - 'transform', d => `translate(0,${d[2]})`); + 'transform', (d: Array<number|string>) => `translate(0,${d[2]})`); group.append('line') .attr('x1', 10) .attr('y1', 0) @@ -1230,40 +1088,41 @@ .attr('stroke', 'black') .attr('stroke-dasharray', '4'); - group.each(function(d) { + group.each(function(d: unknown) { const parentGroup = d3.select(this); - if (d[0]) { + if ((d as Array<string|number>)[0]) { parentGroup.append('text') .attr('x', 20) .attr('y', kAboveLabelOffset) .attr('class', 'separator') - .text(d => d[0]); + .text(d => (d as Array<string|number>)[0]); } - if (d[1]) { + if ((d as Array<string|number>)[1]) { parentGroup.append('text') .attr('x', 20) .attr('y', kBelowLabelOffset) .attr('class', 'separator') - .text(d => d[1]); + .text(d => (d as Array<string|number>)[1]); } }); } - groups.attr('transform', d => `translate(0,${d[2]})`); + groups.attr('transform', (d: unknown) => { + const value = (d as Array<string|number>)[2]; + return `translate(0,${value})`; + }); groups.selectAll('line').attr('x2', graphWidth - 10); } - /** @private */ - restartSimulation_() { + private restartSimulation_() { // Restart the simulation. - this.simulation_.alphaTarget(0.3).restart(); + this.simulation_!.alphaTarget(0.3).restart(); } /** * Resizes and restarts the animation after a size change. - * @private */ - onResize_() { + private onResize_() { this.width_ = this.svg_.clientWidth; this.height_ = this.svg_.clientHeight; @@ -1271,12 +1130,13 @@ // Reset both X and Y attractive forces, as they're cached. const xForce = d3.forceX().x(this.width_ / 2).strength(0.1); - const yForce = d3.forceY() + const yForce = (d3.forceY() as d3.ForceY<GraphNode>) .y(this.getTargetYPosition_.bind(this)) .strength(this.getTargetYPositionStrength_.bind(this)); - this.simulation_.force('x_pos', xForce); - this.simulation_.force('y_pos', yForce); - this.simulation_.force('y_bound', boundingForce(this.height_, this.width_)); + this.simulation_!.force('x_pos', xForce); + this.simulation_!.force('y_pos', yForce); + this.simulation_!.force( + 'y_bound', boundingForce(this.height_, this.width_)); if (!this.wasResized_) { this.wasResized_ = true; @@ -1287,20 +1147,19 @@ // Allow the simulation to settle by running it for a bit. for (let i = 0; i < 200; ++i) { - this.simulation_.tick(); + this.simulation_!.tick(); } } this.restartSimulation_(); } } -/* @type {?Graph} */ -let graph = null; +let graph: Graph|null = null; function onLoad() { graph = - new Graph(document.querySelector('svg'), document.querySelector('div')); + new Graph(document.querySelector('svg')!, document.querySelector('div')!); - graph.initialize(); + graph!.initialize(); } window.addEventListener('load', onLoad);
diff --git a/chrome/browser/resources/discards/graph_tab_template.html b/chrome/browser/resources/discards/graph_tab_template.html index 7d782455..f6e2f39 100644 --- a/chrome/browser/resources/discards/graph_tab_template.html +++ b/chrome/browser/resources/discards/graph_tab_template.html
@@ -1,4 +1,4 @@ - <webview id="webView" src="${data_url}" on-contentload="onWebViewReady_" + <webview id="webView" src="{__data_url__}" on-contentload="onWebViewReady_" allowscaling> </webview>
diff --git a/chrome/browser/resources/discards/tsconfig_base.json b/chrome/browser/resources/discards/tsconfig_base.json index 3e71f763..9199ab6f 100644 --- a/chrome/browser/resources/discards/tsconfig_base.json +++ b/chrome/browser/resources/discards/tsconfig_base.json
@@ -2,8 +2,11 @@ "extends": "../../../../tools/typescript/tsconfig_base.json", "compilerOptions": { "allowJs": true, + "allowUmdGlobalAccess": true, "noUncheckedIndexedAccess": false, "noUnusedLocals": false, - "strictPropertyInitialization": false + "strictPropertyInitialization": false, + "typeRoots": [ "../../../../third_party/node/node_modules/@types" ], + "types": [ "d3" ] } }
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn index 16feaa9..682e04f1 100644 --- a/chrome/browser/resources/pdf/BUILD.gn +++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -208,23 +208,8 @@ manifest_excludes = [ "pdf_internal_plugin_wrapper.js" ] + print_preview_only_files - definitions = [ - "//tools/typescript/definitions/chrome_event.d.ts", - "//tools/typescript/definitions/content_settings.d.ts", - "//tools/typescript/definitions/file_system.d.ts", - "//tools/typescript/definitions/metrics_private.d.ts", - "//tools/typescript/definitions/mime_handler_private.d.ts", - "//tools/typescript/definitions/pending.d.ts", - "//tools/typescript/definitions/resources_private.d.ts", - "//tools/typescript/definitions/runtime.d.ts", - "//tools/typescript/definitions/tabs.d.ts", - "//tools/typescript/definitions/windows.d.ts", - "source_capabilities.d.ts", - ] - if (enable_ink) { - definitions += [ "ink/drawing_canvas.d.ts" ] - } + definitions = ts_definitions deps = [ "//third_party/polymer/v3_0:library",
diff --git a/chrome/browser/resources/pdf/manifest.json b/chrome/browser/resources/pdf/manifest.json index dbe8dd5..dbc6ccd 100644 --- a/chrome/browser/resources/pdf/manifest.json +++ b/chrome/browser/resources/pdf/manifest.json
@@ -9,6 +9,7 @@ "incognito": "split", "permissions": [ "chrome://resources/", + "chrome://webui-test/", "contentSettings", "metricsPrivate", "resourcesPrivate", @@ -18,6 +19,6 @@ "mime_types": [ "application/pdf" ], - "content_security_policy": "script-src 'self' 'wasm-eval' blob: filesystem: chrome://resources; object-src * blob: externalfile: file: filesystem: data:", + "content_security_policy": "script-src 'self' 'wasm-eval' blob: filesystem: chrome://resources chrome://webui-test; object-src * blob: externalfile: file: filesystem: data:", "mime_types_handler": "index.html" }
diff --git a/chrome/browser/resources/pdf/pdf.gni b/chrome/browser/resources/pdf/pdf.gni index 22f1298d..f8ca4e0 100644 --- a/chrome/browser/resources/pdf/pdf.gni +++ b/chrome/browser/resources/pdf/pdf.gni
@@ -154,6 +154,27 @@ print_preview_only_files = print_preview_ts_files + print_preview_html_wrapper_files +ts_definitions = [ + # Using an absolute path because this variable is also consumed by + # chrome/test/data/pdf:build_ts. + "//chrome/browser/resources/pdf/source_capabilities.d.ts", + + "//tools/typescript/definitions/chrome_event.d.ts", + "//tools/typescript/definitions/content_settings.d.ts", + "//tools/typescript/definitions/file_system.d.ts", + "//tools/typescript/definitions/metrics_private.d.ts", + "//tools/typescript/definitions/mime_handler_private.d.ts", + "//tools/typescript/definitions/pending.d.ts", + "//tools/typescript/definitions/resources_private.d.ts", + "//tools/typescript/definitions/runtime.d.ts", + "//tools/typescript/definitions/tabs.d.ts", + "//tools/typescript/definitions/windows.d.ts", +] + +if (enable_ink) { + ts_definitions += [ "//chrome/browser/resources/pdf/ink/drawing_canvas.d.ts" ] +} + # Print Preview's .grdp file needs all the shared + Print Preview specific files. print_preview_grdp_ts_files = shared_ts_files + shared_html_wrapper_files + shared_css_wrapper_files +
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/OWNERS b/chrome/browser/resources/settings/chromeos/multidevice_page/OWNERS index 7027ab73..18377141 100644 --- a/chrome/browser/resources/settings/chromeos/multidevice_page/OWNERS +++ b/chrome/browser/resources/settings/chromeos/multidevice_page/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/safe_browsing/tailored_security/unconsented_message_android.cc b/chrome/browser/safe_browsing/tailored_security/unconsented_message_android.cc index 133d509..22f17a0 100644 --- a/chrome/browser/safe_browsing/tailored_security/unconsented_message_android.cc +++ b/chrome/browser/safe_browsing/tailored_security/unconsented_message_android.cc
@@ -28,7 +28,6 @@ #include "ui/gfx/image/canvas_image_source.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/paint_vector_icon.h" -#include "ui/views/image_model_utils.h" namespace safe_browsing {
diff --git a/chrome/browser/search/contextual_search_policy_handler_android.cc b/chrome/browser/search/contextual_search_policy_handler_android.cc index 25c3ec1..fa6e945 100644 --- a/chrome/browser/search/contextual_search_policy_handler_android.cc +++ b/chrome/browser/search/contextual_search_policy_handler_android.cc
@@ -22,13 +22,14 @@ void ContextualSearchPolicyHandlerAndroid::ApplyPolicySettings( const PolicyMap& policies, PrefValueMap* prefs) { - const base::Value* value = policies.GetValue(policy_name()); + const base::Value* value = + policies.GetValue(policy_name(), base::Value::Type::BOOLEAN); // From a Contextual Search preference point of view, "false" means the // feature is turned off completely. "" means the feature is uninitialized and // an opt-in screen is presented to the user, after which the preference is // either "true" or "false", depending on their choice. Here a false policy // explicitly disables Contextual Search. - if (value && value->is_bool() && !value->GetBool()) { + if (value && !value->GetBool()) { prefs->SetString(prefs::kContextualSearchEnabled, prefs::kContextualSearchDisabledValue); }
diff --git a/chrome/browser/search/ntp_custom_background_enabled_policy_handler.cc b/chrome/browser/search/ntp_custom_background_enabled_policy_handler.cc index 3e8d184..c32d2fa 100644 --- a/chrome/browser/search/ntp_custom_background_enabled_policy_handler.cc +++ b/chrome/browser/search/ntp_custom_background_enabled_policy_handler.cc
@@ -21,8 +21,9 @@ void NtpCustomBackgroundEnabledPolicyHandler::ApplyPolicySettings( const policy::PolicyMap& policies, PrefValueMap* prefs) { - const base::Value* value = policies.GetValue(policy_name()); - if (value && value->is_bool() && !value->GetBool()) { + const base::Value* value = + policies.GetValue(policy_name(), base::Value::Type::BOOLEAN); + if (value && !value->GetBool()) { prefs->SetValue(prefs::kNtpCustomBackgroundDict, base::Value(base::Value::Type::DICTIONARY)); }
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl.cc b/chrome/browser/segmentation_platform/model_provider_factory_impl.cc new file mode 100644 index 0000000..da757858 --- /dev/null +++ b/chrome/browser/segmentation_platform/model_provider_factory_impl.cc
@@ -0,0 +1,39 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/segmentation_platform/model_provider_factory_impl.h" + +#include "components/optimization_guide/machine_learning_tflite_buildflags.h" +#include "components/optimization_guide/proto/models.pb.h" +#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h" + +namespace segmentation_platform { + +ModelProviderFactoryImpl::ModelProviderFactoryImpl( + optimization_guide::OptimizationGuideModelProvider* + optimization_guide_provider, + scoped_refptr<base::SequencedTaskRunner> background_task_runner) + : optimization_guide_provider_(optimization_guide_provider), + background_task_runner_(background_task_runner) {} + +ModelProviderFactoryImpl::~ModelProviderFactoryImpl() = default; + +std::unique_ptr<ModelProvider> ModelProviderFactoryImpl::CreateProvider( + optimization_guide::proto::OptimizationTarget optimization_target) { +#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) + return std::make_unique<OptimizationGuideSegmentationModelProvider>( + optimization_guide_provider_, background_task_runner_, + optimization_target); +#endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) + + NOTREACHED(); + return nullptr; +} + +std::unique_ptr<ModelProvider> ModelProviderFactoryImpl::CreateDefaultProvider( + optimization_guide::proto::OptimizationTarget optimization_target) { + return nullptr; +} + +} // namespace segmentation_platform
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl.h b/chrome/browser/segmentation_platform/model_provider_factory_impl.h new file mode 100644 index 0000000..b108b26b --- /dev/null +++ b/chrome/browser/segmentation_platform/model_provider_factory_impl.h
@@ -0,0 +1,49 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_ +#define CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_ + +#include <memory> + +#include "base/containers/flat_map.h" +#include "base/task/sequenced_task_runner.h" +#include "components/optimization_guide/proto/models.pb.h" +#include "components/segmentation_platform/public/model_provider.h" + +namespace optimization_guide { +class OptimizationGuideModelProvider; +} + +namespace segmentation_platform { + +class ModelProviderFactoryImpl : public ModelProviderFactory { + public: + ModelProviderFactoryImpl( + optimization_guide::OptimizationGuideModelProvider* + optimization_guide_provider, + scoped_refptr<base::SequencedTaskRunner> background_task_runner); + + ~ModelProviderFactoryImpl() override; + + ModelProviderFactoryImpl(ModelProviderFactoryImpl&) = delete; + ModelProviderFactoryImpl& operator=(ModelProviderFactoryImpl&) = delete; + + // ModelProviderFactory impl: + std::unique_ptr<ModelProvider> CreateProvider( + optimization_guide::proto::OptimizationTarget optimization_target) + override; + std::unique_ptr<ModelProvider> CreateDefaultProvider( + optimization_guide::proto::OptimizationTarget optimization_target) + override; + + private: + raw_ptr<optimization_guide::OptimizationGuideModelProvider> + optimization_guide_provider_; + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; +}; + +} // namespace segmentation_platform + +#endif // CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc b/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc new file mode 100644 index 0000000..3df4a81 --- /dev/null +++ b/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
@@ -0,0 +1,54 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/segmentation_platform/model_provider_factory_impl.h" + +#include "base/test/task_environment.h" +#include "base/test/test_simple_task_runner.h" +#include "components/optimization_guide/core/test_optimization_guide_model_provider.h" +#include "components/optimization_guide/machine_learning_tflite_buildflags.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace segmentation_platform { + +#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) +class ModelProviderFactoryImplTest : public testing::Test { + public: + ModelProviderFactoryImplTest() = default; + ~ModelProviderFactoryImplTest() override = default; + + void SetUp() override { + task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>(); + model_provider_ = std::make_unique< + optimization_guide::TestOptimizationGuideModelProvider>(); + provider_factory_ = std::make_unique<ModelProviderFactoryImpl>( + model_provider_.get(), task_runner_); + } + + void TearDown() override { + task_runner_->RunPendingTasks(); + provider_factory_.reset(); + model_provider_.reset(); + } + + protected: + base::test::TaskEnvironment task_environment_; + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider> + model_provider_; + + std::unique_ptr<ModelProviderFactoryImpl> provider_factory_; +}; + +TEST_F(ModelProviderFactoryImplTest, ProviderCreated) { + EXPECT_TRUE(provider_factory_->CreateProvider( + optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_VOICE)); + EXPECT_TRUE(provider_factory_->CreateProvider( + optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_SHARE)); +} +#endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) + +} // namespace segmentation_platform
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc b/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc index 07f91141..7b04c284 100644 --- a/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc +++ b/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc
@@ -14,6 +14,7 @@ #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/segmentation_platform/model_provider_factory_impl.h" #include "chrome/browser/segmentation_platform/segmentation_platform_config.h" #include "chrome/browser/segmentation_platform/segmentation_platform_profile_observer.h" #include "chrome/browser/segmentation_platform/ukm_database_client.h" @@ -23,6 +24,7 @@ #include "components/segmentation_platform/internal/segmentation_platform_service_impl.h" #include "components/segmentation_platform/public/config.h" #include "components/segmentation_platform/public/features.h" +#include "components/segmentation_platform/public/model_provider.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/storage_partition.h" @@ -81,8 +83,11 @@ profile->GetDefaultStoragePartition()->GetProtoDatabaseProvider(); base::DefaultClock* clock = base::DefaultClock::GetInstance(); + auto model_provider_factory = std::make_unique<ModelProviderFactoryImpl>( + optimization_guide, task_runner); + auto* service = new SegmentationPlatformServiceImpl( - optimization_guide, db_provider, storage_dir, + std::move(model_provider_factory), db_provider, storage_dir, UkmDatabaseClient::GetInstance().GetUkmDataManager(), profile->GetPrefs(), history_service, task_runner, clock, GetSegmentationPlatformConfig());
diff --git a/chrome/browser/sessions/restore_on_startup_policy_handler.cc b/chrome/browser/sessions/restore_on_startup_policy_handler.cc index 01b12cc..f473cc2 100644 --- a/chrome/browser/sessions/restore_on_startup_policy_handler.cc +++ b/chrome/browser/sessions/restore_on_startup_policy_handler.cc
@@ -27,10 +27,8 @@ const PolicyMap& policies, PrefValueMap* prefs) { const base::Value* restore_on_startup_value = - policies.GetValue(policy_name()); + policies.GetValue(policy_name(), base::Value::Type::INTEGER); if (restore_on_startup_value) { - if (!restore_on_startup_value->is_int()) - return; prefs->SetInteger(prefs::kRestoreOnStartup, restore_on_startup_value->GetInt()); } @@ -42,10 +40,10 @@ if (!TypeCheckingPolicyHandler::CheckPolicySettings(policies, errors)) return false; - const base::Value* restore_policy = policies.GetValue(key::kRestoreOnStartup); + const base::Value* restore_policy = + policies.GetValue(key::kRestoreOnStartup, base::Value::Type::INTEGER); if (restore_policy) { - CHECK(restore_policy->is_int()); // Passed type check. switch (restore_policy->GetInt()) { case 0: // Deprecated kPrefValueHomePage. errors->AddError(policy_name(), IDS_POLICY_VALUE_DEPRECATED); @@ -55,10 +53,9 @@ // If the "restore last session" policy is set, session cookies are // treated as permanent cookies and site data needed to restore the // session is not cleared so we have to warn the user in that case. - const base::Value* cookies_policy = - policies.GetValue(key::kCookiesSessionOnlyForUrls); - if (cookies_policy && cookies_policy->is_list() && - !cookies_policy->GetListDeprecated().empty()) { + const base::Value* cookies_policy = policies.GetValue( + key::kCookiesSessionOnlyForUrls, base::Value::Type::LIST); + if (cookies_policy && !cookies_policy->GetListDeprecated().empty()) { errors->AddError(key::kCookiesSessionOnlyForUrls, IDS_POLICY_OVERRIDDEN, key::kRestoreOnStartup);
diff --git a/chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.cc b/chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.cc index ad02483..1c104c7 100644 --- a/chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.cc +++ b/chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.cc
@@ -59,13 +59,14 @@ const policy::PolicyMap& policies, PrefValueMap* prefs) { // Ignore this policy if the SpellcheckEnabled policy disables spellcheck. - const base::Value* spellcheck_enabled_value = - policies.GetValue(policy::key::kSpellcheckEnabled); - if (spellcheck_enabled_value && spellcheck_enabled_value->GetBool() == false) + const base::Value* spellcheck_enabled_value = policies.GetValue( + policy::key::kSpellcheckEnabled, base::Value::Type::BOOLEAN); + if (spellcheck_enabled_value && !spellcheck_enabled_value->GetBool()) return; // If this policy isn't set, don't modify spellcheck languages. - const base::Value* value = policies.GetValue(policy_name()); + const base::Value* value = + policies.GetValue(policy_name(), base::Value::Type::LIST); if (!value) return; @@ -98,13 +99,14 @@ std::vector<base::Value>* const blocklisted, std::vector<std::string>* const unknown, std::vector<std::string>* const duplicates) { - const base::Value* value = policies.GetValue(policy_name()); + const base::Value* value = + policies.GetValue(policy_name(), base::Value::Type::LIST); if (!value) return; // Build a lookup of force-enabled spellcheck languages to find duplicates. - const base::Value* forced_enabled_value = - policies.GetValue(policy::key::kSpellcheckLanguage); + const base::Value* forced_enabled_value = policies.GetValue( + policy::key::kSpellcheckLanguage, base::Value::Type::LIST); std::unordered_set<std::string> forced_languages_lookup; if (forced_enabled_value) { for (const auto& forced_language :
diff --git a/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc b/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc index 316f636..814a507 100644 --- a/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc +++ b/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc
@@ -51,13 +51,14 @@ const policy::PolicyMap& policies, PrefValueMap* prefs) { // Ignore this policy if the SpellcheckEnabled policy disables spellcheck. - const base::Value* spellcheck_enabled_value = - policies.GetValue(policy::key::kSpellcheckEnabled); - if (spellcheck_enabled_value && spellcheck_enabled_value->GetBool() == false) + const base::Value* spellcheck_enabled_value = policies.GetValue( + policy::key::kSpellcheckEnabled, base::Value::Type::BOOLEAN); + if (spellcheck_enabled_value && !spellcheck_enabled_value->GetBool()) return; // If this policy isn't set, don't modify spellcheck languages. - const base::Value* value = policies.GetValue(policy_name()); + const base::Value* value = + policies.GetValue(policy_name(), base::Value::Type::LIST); if (!value) return; @@ -82,7 +83,8 @@ const policy::PolicyMap& policies, std::vector<base::Value>* const forced, std::vector<std::string>* const unknown) { - const base::Value* value = policies.GetValue(policy_name()); + const base::Value* value = + policies.GetValue(policy_name(), base::Value::Type::LIST); if (!value) return;
diff --git a/chrome/browser/ssl/https_only_mode_browsertest.cc b/chrome/browser/ssl/https_only_mode_browsertest.cc index cf332dc..45209d7 100644 --- a/chrome/browser/ssl/https_only_mode_browsertest.cc +++ b/chrome/browser/ssl/https_only_mode_browsertest.cc
@@ -8,6 +8,7 @@ #include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/simple_test_clock.h" #include "chrome/browser/interstitials/security_interstitial_page_test_utils.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ssl/https_only_mode_navigation_throttle.h" @@ -821,6 +822,96 @@ security_interstitials::MetricsHelper::Decision::DONT_PROCEED, 1); } +// Tests that if a user allowlists a host and then does not visit it again for +// seven days (the expiration period), then the interstitial will be shown again +// the next time they visit the host. +IN_PROC_BROWSER_TEST_F(HttpsOnlyModeBrowserTest, AllowlistEntryExpires) { + content::WebContents* contents = + browser()->tab_strip_model()->GetActiveWebContents(); + Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); + content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); + + // Set a testing clock on the StatefulSSLHostStateDelegate, keeping a pointer + // to the clock object around so the test can manipulate time. `chrome_state` + // takes ownership of `clock`. + auto clock = std::make_unique<base::SimpleTestClock>(); + auto* clock_ptr = clock.get(); + StatefulSSLHostStateDelegate* chrome_state = + static_cast<StatefulSSLHostStateDelegate*>(state); + chrome_state->SetClockForTesting(std::move(clock)); + + // Start the clock at standard system time. + clock_ptr->SetNow(base::Time::NowFromSystemTime()); + + // Visit a host that doesn't support HTTPS for the first time, and click + // through the HTTPS-First Mode interstitial to allowlist the host. + GURL http_url = http_server()->GetURL("bad-https.test", "/simple.html"); + EXPECT_FALSE(content::NavigateToURL(contents, http_url)); + EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( + contents)); + ProceedThroughInterstitial(contents); + EXPECT_EQ(http_url, contents->GetLastCommittedURL()); + EXPECT_TRUE(state->IsHttpAllowedForHost(http_url.host(), contents)); + + // Simulate the clock advancing by eight days, which is past the expiration + // point. + clock_ptr->Advance(base::Days(8)); + + // The host should no longer be allowlisted, and the interstitial should + // trigger again. + EXPECT_FALSE(state->IsHttpAllowedForHost(http_url.host(), contents)); + EXPECT_FALSE(content::NavigateToURL(contents, http_url)); + EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( + contents)); +} + +// Tests that re-visiting an allowlisted host bumps the expiration time to a new +// seven days in the future from now. +IN_PROC_BROWSER_TEST_F(HttpsOnlyModeBrowserTest, RevisitingBumpsExpiration) { + content::WebContents* contents = + browser()->tab_strip_model()->GetActiveWebContents(); + Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); + content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); + + // Set a testing clock on the StatefulSSLHostStateDelegate, keeping a pointer + // to the clock object around so the test can manipulate time. `chrome_state` + // takes ownership of `clock`. + auto clock = std::make_unique<base::SimpleTestClock>(); + auto* clock_ptr = clock.get(); + StatefulSSLHostStateDelegate* chrome_state = + static_cast<StatefulSSLHostStateDelegate*>(state); + chrome_state->SetClockForTesting(std::move(clock)); + + // Start the clock at standard system time. + clock_ptr->SetNow(base::Time::NowFromSystemTime()); + + // Visit a host that doesn't support HTTPS for the first time, and click + // through the HTTPS-First Mode interstitial to allowlist the host. + GURL http_url = http_server()->GetURL("bad-https.test", "/simple.html"); + EXPECT_FALSE(content::NavigateToURL(contents, http_url)); + EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( + contents)); + ProceedThroughInterstitial(contents); + EXPECT_EQ(http_url, contents->GetLastCommittedURL()); + EXPECT_TRUE(state->IsHttpAllowedForHost(http_url.host(), contents)); + + // Simulate the clock advancing by five days. + clock_ptr->Advance(base::Days(5)); + + // Navigate to the host again; this will reset the allowlist expiration to + // now + 7 days. + EXPECT_TRUE(content::NavigateToURL(contents, http_url)); + + // Simulate the clock advancing another five days. This will be _after_ the + // initial expiration date of the allowlist entry, but _before_ the bumped + // expiration date from the second navigation. + clock_ptr->Advance(base::Days(5)); + EXPECT_TRUE(content::NavigateToURL(contents, http_url)); + EXPECT_FALSE( + chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( + contents)); +} + // A simple test fixture that ensures the kHttpsOnlyMode feature is enabled and // constructs a HistogramTester (so that it gets initialized before browser // startup). Used for testing pref tracking logic.
diff --git a/chrome/browser/ssl/https_only_mode_policy_handler.cc b/chrome/browser/ssl/https_only_mode_policy_handler.cc index eee04af..e209596b 100644 --- a/chrome/browser/ssl/https_only_mode_policy_handler.cc +++ b/chrome/browser/ssl/https_only_mode_policy_handler.cc
@@ -19,7 +19,8 @@ void HttpsOnlyModePolicyHandler::ApplyPolicySettings(const PolicyMap& policies, PrefValueMap* prefs) { - const base::Value* value = policies.GetValue(key::kHttpsOnlyMode); + const base::Value* value = + policies.GetValue(key::kHttpsOnlyMode, base::Value::Type::STRING); if (value && value->GetString() == "disallowed") { // Only apply the policy to the pref if it is set to "disallowed". prefs->SetBoolean(prefs::kHttpsOnlyModeEnabled, false);
diff --git a/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc b/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc index b7ed0d2..0a35279 100644 --- a/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc +++ b/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
@@ -118,6 +118,13 @@ // StatefulSSLHostStateDelegate can be null during tests. if (state && state->IsHttpAllowedForHost( tentative_resource_request.url.host(), web_contents)) { + // Renew the allowlist expiration for this host as the user is still + // actively using it. This means that the allowlist entry will stay valid + // until the user stops visiting this host for the entire expiration + // period (one week). + state->AllowHttpForHost(tentative_resource_request.url.host(), + web_contents); + std::move(callback).Run({}); return; }
diff --git a/chrome/browser/ssl/secure_origin_policy_handler.cc b/chrome/browser/ssl/secure_origin_policy_handler.cc index d0d2206..2ae4f4f8 100644 --- a/chrome/browser/ssl/secure_origin_policy_handler.cc +++ b/chrome/browser/ssl/secure_origin_policy_handler.cc
@@ -28,7 +28,8 @@ void SecureOriginPolicyHandler::ApplyPolicySettings(const PolicyMap& policies, PrefValueMap* prefs) { - const base::Value* value = policies.GetValue(policy_name()); + const base::Value* value = + policies.GetValue(policy_name(), base::Value::Type::LIST); if (!value) return;
diff --git a/chrome/browser/supervised_user/supervised_user_settings_service_unittest.cc b/chrome/browser/supervised_user/supervised_user_settings_service_unittest.cc index dbfe822..ddd20c7 100644 --- a/chrome/browser/supervised_user/supervised_user_settings_service_unittest.cc +++ b/chrome/browser/supervised_user/supervised_user_settings_service_unittest.cc
@@ -111,9 +111,8 @@ EXPECT_TRUE(expected_value); } - std::unique_ptr<base::Value> value = - base::JSONReader::ReadDeprecated(supervised_user_setting.value()); - EXPECT_TRUE(expected_value->Equals(value.get())); + EXPECT_EQ(*expected_value, + base::JSONReader::Read(supervised_user_setting.value())); } void OnNewSettingsAvailable(const base::DictionaryValue* settings) { @@ -207,7 +206,7 @@ ASSERT_TRUE(value); const base::DictionaryValue* dict_value = nullptr; ASSERT_TRUE(value->GetAsDictionary(&dict_value)); - EXPECT_TRUE(dict_value->Equals(&dict)); + EXPECT_EQ(*dict_value, dict); } TEST_F(SupervisedUserSettingsServiceTest, NotifyForWebsiteApprovals) {
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java index 92dbf5f..d67a758 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
@@ -16,7 +16,6 @@ import org.chromium.base.Log; import org.chromium.base.ObserverList; import org.chromium.base.TraceEvent; -import org.chromium.base.annotations.DoNotClassMerge; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.supplier.Supplier; import org.chromium.chrome.browser.tab.Tab; @@ -38,11 +37,7 @@ /** * Data which is core to the app and must be retrieved as quickly as possible on startup. - * - * This class should not be merged because it is being used as a key in a Map - * in PersistedTabDataConfiguration.java. */ -@DoNotClassMerge public class CriticalPersistedTabData extends PersistedTabData { private static final String TAG = "CriticalPTD"; private static final Class<CriticalPersistedTabData> USER_DATA_KEY =
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/MockPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/MockPersistedTabData.java index 1f90b780..332f880 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/MockPersistedTabData.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/MockPersistedTabData.java
@@ -5,7 +5,6 @@ package org.chromium.chrome.browser.tab.state; import org.chromium.base.Callback; -import org.chromium.base.annotations.DoNotClassMerge; import org.chromium.base.supplier.Supplier; import org.chromium.chrome.browser.tab.Tab; @@ -13,11 +12,7 @@ /** * MockPersistedTabData object used for testing - * - * This class should not be merged because it is being used as a key in a Map - * in PersistedTabDataConfiguration.java. */ -@DoNotClassMerge public class MockPersistedTabData extends PersistedTabData { private int mField;
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java index 2b28f0d..d342812 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
@@ -14,7 +14,6 @@ import org.chromium.base.Callback; import org.chromium.base.FeatureList; import org.chromium.base.Log; -import org.chromium.base.annotations.DoNotClassMerge; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.supplier.Supplier; @@ -56,11 +55,7 @@ /** * {@link PersistedTabData} for Shopping related websites - * - * This class should not be merged because it is being used as a key in a Map - * in PersistedTabDataConfiguration.java. */ -@DoNotClassMerge public class ShoppingPersistedTabData extends PersistedTabData { private static final String TAG = "SPTD"; private static final String STALE_TAB_THRESHOLD_SECONDS_PARAM =
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/StorePersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/StorePersistedTabData.java index 76e105d3..b82541dc 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/StorePersistedTabData.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/StorePersistedTabData.java
@@ -14,7 +14,6 @@ import org.chromium.base.Callback; import org.chromium.base.Log; -import org.chromium.base.annotations.DoNotClassMerge; import org.chromium.base.supplier.Supplier; import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher; import org.chromium.chrome.browser.profiles.Profile; @@ -29,11 +28,7 @@ /** * {@link PersistedTabData} for Store websites with opening/closing hours. * TODO(crbug.com/1199134) Add tests for StorePersistedTabData - * - * This class should not be merged because it is being used as a key in a Map - * in PersistedTabDataConfiguration.java. */ -@DoNotClassMerge public class StorePersistedTabData extends PersistedTabData { private static final String MISSING_STRING = "missing"; private static final String COLON_STRING = ":";
diff --git a/chrome/browser/task_manager/providers/web_contents/devtools_tag_browsertest.cc b/chrome/browser/task_manager/providers/web_contents/devtools_tag_browsertest.cc index cc08d92..ddedbe5 100644 --- a/chrome/browser/task_manager/providers/web_contents/devtools_tag_browsertest.cc +++ b/chrome/browser/task_manager/providers/web_contents/devtools_tag_browsertest.cc
@@ -63,7 +63,7 @@ // Tests that opening a DevToolsWindow will result in tagging its main // WebContents and that tag will be recorded by the TagsManager. -IN_PROC_BROWSER_TEST_F(DevToolsTagTest, TagsManagerRecordsATag) { +IN_PROC_BROWSER_TEST_F(DevToolsTagTest, DISABLED_TagsManagerRecordsATag) { // Browser tests start with a single tab. EXPECT_EQ(1U, tags_manager()->tracked_tags().size());
diff --git a/chrome/browser/touch_to_fill/android/BUILD.gn b/chrome/browser/touch_to_fill/android/BUILD.gn index a415ebb..079a203 100644 --- a/chrome/browser/touch_to_fill/android/BUILD.gn +++ b/chrome/browser/touch_to_fill/android/BUILD.gn
@@ -62,6 +62,7 @@ "//chrome/browser/flags:java", "//chrome/browser/touch_to_fill/android:public_java", "//chrome/browser/touch_to_fill/android/internal:java", + "//chrome/browser/touch_to_fill/android/internal:resource_provider_public_impl_java", "//chrome/browser/ui/android/favicon:java", "//chrome/test/android:chrome_java_test_support", "//components/browser_ui/bottomsheet/android:java", @@ -95,6 +96,7 @@ "//chrome/android:chrome_test_util_java", "//chrome/browser/flags:java", "//chrome/browser/touch_to_fill/android/internal:java", + "//chrome/browser/touch_to_fill/android/internal:resource_provider_public_impl_java", "//chrome/test/android:chrome_java_test_support", "//components/browser_ui/bottomsheet/android:java", "//components/browser_ui/bottomsheet/android/test:java",
diff --git a/chrome/browser/touch_to_fill/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/android/internal/BUILD.gn index bb59c3226..a06ec37 100644 --- a/chrome/browser/touch_to_fill/android/internal/BUILD.gn +++ b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
@@ -22,6 +22,7 @@ "//components/favicon/android:java", "//components/url_formatter/android:url_formatter_java", "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_appcompat_appcompat_resources_java", "//third_party/androidx:androidx_recyclerview_recyclerview_java", "//ui/android:ui_java", "//url:gurl_java",
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml index c717b4f..0b73a70 100644 --- a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml +++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml
@@ -14,11 +14,11 @@ android:orientation="vertical"> <ImageView + android:id="@+id/touch_to_fill_sheet_header_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" - android:importantForAccessibility="no" - app:srcCompat="@drawable/touch_to_fill_header_image" /> + android:importantForAccessibility="no" /> <org.chromium.ui.widget.TextViewWithLeading android:id="@+id/touch_to_fill_sheet_title"
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java index c46ec6ad..339e955 100644 --- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java +++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java
@@ -10,6 +10,7 @@ import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.ON_CLICK_LISTENER; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL; +import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SHOW_SUBMIT_SUBTITLE; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SINGLE_CREDENTIAL; @@ -69,6 +70,8 @@ assert credentials != null; mModel.set(ON_CLICK_MANAGE, this::onManagePasswordSelected); + TouchToFillResourceProvider resourceProvider = new TouchToFillResourceProviderImpl(); + ListModel<ListItem> sheetItems = mModel.get(SHEET_ITEMS); sheetItems.clear(); @@ -83,6 +86,7 @@ url, SchemeDisplay.OMIT_HTTP_AND_HTTPS)) .with(ORIGIN_SECURE, isOriginSecure) .with(SHOW_SUBMIT_SUBTITLE, show_submit_subtitle) + .with(IMAGE_DRAWABLE_ID, resourceProvider.getHeaderImageDrawableId()) .build())); mCredentials = credentials;
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java index 3995ac56..d7de24f 100644 --- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java +++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java
@@ -93,9 +93,11 @@ new PropertyModel.ReadableObjectPropertyKey<>("formatted_url"); static final PropertyModel.ReadableBooleanPropertyKey ORIGIN_SECURE = new PropertyModel.ReadableBooleanPropertyKey("origin_secure"); + static final PropertyModel.ReadableIntPropertyKey IMAGE_DRAWABLE_ID = + new PropertyModel.ReadableIntPropertyKey("image_drawable_id"); - static final PropertyKey[] ALL_KEYS = { - SHOW_SUBMIT_SUBTITLE, SINGLE_CREDENTIAL, FORMATTED_URL, ORIGIN_SECURE}; + static final PropertyKey[] ALL_KEYS = {SHOW_SUBMIT_SUBTITLE, SINGLE_CREDENTIAL, + FORMATTED_URL, ORIGIN_SECURE, IMAGE_DRAWABLE_ID}; private HeaderProperties() {} }
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java index 5a8d8811..1c00d125 100644 --- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java +++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
@@ -11,6 +11,7 @@ import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.DISMISS_HANDLER; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL; +import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SHOW_SUBMIT_SUBTITLE; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SINGLE_CREDENTIAL; @@ -27,6 +28,7 @@ import android.widget.TextView; import androidx.annotation.StringRes; +import androidx.appcompat.content.res.AppCompatResources; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties; @@ -233,12 +235,16 @@ */ private static void bindHeaderView(PropertyModel model, View view, PropertyKey key) { if (key == SHOW_SUBMIT_SUBTITLE || key == SINGLE_CREDENTIAL || key == FORMATTED_URL - || key == ORIGIN_SECURE) { + || key == ORIGIN_SECURE || key == IMAGE_DRAWABLE_ID) { TextView sheetTitleText = view.findViewById(R.id.touch_to_fill_sheet_title); sheetTitleText.setText(getTitle(model, view.getContext())); TextView sheetSubtitleText = view.findViewById(R.id.touch_to_fill_sheet_subtitle); sheetSubtitleText.setText(getSubtitle(model, view.getContext())); + + ImageView sheetHeaderImage = view.findViewById(R.id.touch_to_fill_sheet_header_image); + sheetHeaderImage.setImageDrawable(AppCompatResources.getDrawable( + view.getContext(), model.get(IMAGE_DRAWABLE_ID))); } else { assert false : "Unhandled update to property:" + key; }
diff --git a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java index 4cd1e3b..169208f 100644 --- a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java +++ b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
@@ -18,6 +18,7 @@ import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.ON_CLICK_LISTENER; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL; +import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SHOW_SUBMIT_SUBTITLE; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SINGLE_CREDENTIAL; @@ -89,6 +90,7 @@ private PropertyModel mModel; private TouchToFillView mTouchToFillView; private BottomSheetController mBottomSheetController; + TouchToFillResourceProvider mResourceProvider; @Rule public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); @@ -100,6 +102,7 @@ mBottomSheetController = mActivityTestRule.getActivity() .getRootUiCoordinatorForTesting() .getBottomSheetController(); + mResourceProvider = new TouchToFillResourceProviderImpl(); TestThreadUtils.runOnUiThreadBlocking(() -> { mModel = TouchToFillProperties.createDefaultModel(mDismissHandler); mTouchToFillView = new TouchToFillView(getActivity(), mBottomSheetController); @@ -134,6 +137,8 @@ .with(SINGLE_CREDENTIAL, true) .with(FORMATTED_URL, "www.example.org") .with(ORIGIN_SECURE, true) + .with(IMAGE_DRAWABLE_ID, + mResourceProvider.getHeaderImageDrawableId()) .build())); mModel.set(VISIBLE, true); }); @@ -158,6 +163,8 @@ .with(SINGLE_CREDENTIAL, false) .with(FORMATTED_URL, "www.example.org") .with(ORIGIN_SECURE, true) + .with(IMAGE_DRAWABLE_ID, + mResourceProvider.getHeaderImageDrawableId()) .build())); mModel.set(VISIBLE, true); }); @@ -180,6 +187,8 @@ .with(SINGLE_CREDENTIAL, true) .with(FORMATTED_URL, "www.example.org") .with(ORIGIN_SECURE, true) + .with(IMAGE_DRAWABLE_ID, + mResourceProvider.getHeaderImageDrawableId()) .build())); mModel.set(VISIBLE, true); }); @@ -202,6 +211,8 @@ .with(SINGLE_CREDENTIAL, false) .with(FORMATTED_URL, "www.example.org") .with(ORIGIN_SECURE, true) + .with(IMAGE_DRAWABLE_ID, + mResourceProvider.getHeaderImageDrawableId()) .build())); mModel.set(VISIBLE, true); }); @@ -222,6 +233,8 @@ new PropertyModel.Builder(HeaderProperties.ALL_KEYS) .with(FORMATTED_URL, "www.example.org") .with(ORIGIN_SECURE, true) + .with(IMAGE_DRAWABLE_ID, + mResourceProvider.getHeaderImageDrawableId()) .build())); mModel.set(VISIBLE, true); }); @@ -241,6 +254,8 @@ new PropertyModel.Builder(HeaderProperties.ALL_KEYS) .with(FORMATTED_URL, "m.example.org") .with(ORIGIN_SECURE, false) + .with(IMAGE_DRAWABLE_ID, + mResourceProvider.getHeaderImageDrawableId()) .build())); mModel.set(VISIBLE, true); }); @@ -262,6 +277,8 @@ .with(SHOW_SUBMIT_SUBTITLE, true) .with(FORMATTED_URL, "m.example.org") .with(ORIGIN_SECURE, true) + .with(IMAGE_DRAWABLE_ID, + mResourceProvider.getHeaderImageDrawableId()) .build())); mModel.set(VISIBLE, true); }); @@ -283,6 +300,8 @@ .with(SHOW_SUBMIT_SUBTITLE, true) .with(FORMATTED_URL, "m.example.org") .with(ORIGIN_SECURE, false) + .with(IMAGE_DRAWABLE_ID, + mResourceProvider.getHeaderImageDrawableId()) .build())); mModel.set(VISIBLE, true); });
diff --git a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java index f88d290..365bea7 100644 --- a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java +++ b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
@@ -22,6 +22,7 @@ import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.DISMISS_HANDLER; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL; +import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SHOW_SUBMIT_SUBTITLE; import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SINGLE_CREDENTIAL; @@ -155,6 +156,8 @@ assertThat(itemList.get(2).model.get(CREDENTIAL), is(CARL)); assertNotNull(itemList.get(2).model.get(ON_CLICK_LISTENER)); assertThat(itemList.get(2).model.get(FORMATTED_ORIGIN), is(format(CARL.getOriginUrl()))); + assertThat(itemList.get(0).model.get(IMAGE_DRAWABLE_ID), + is(R.drawable.touch_to_fill_header_image)); } @Test
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 936021a..ad7eb75 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -2957,6 +2957,8 @@ "//ash/components/drivefs/mojom:mojom", "//ash/components/login/auth", "//ash/components/login/session", + "//ash/components/multidevice", + "//ash/components/multidevice/logging", "//ash/components/peripheral_notification", "//ash/components/phonehub", "//ash/components/phonehub:debug", @@ -3052,8 +3054,6 @@ "//chromeos/assistant:buildflags", "//chromeos/components/local_search_service/public/cpp", "//chromeos/components/local_search_service/public/mojom", - "//chromeos/components/multidevice", - "//chromeos/components/multidevice/logging", "//chromeos/components/onc", "//chromeos/components/quick_answers", "//chromeos/components/quick_answers/public/cpp:cpp", @@ -4163,8 +4163,6 @@ "views/device_chooser_content_view.h", "views/devtools_process_observer.cc", "views/devtools_process_observer.h", - "views/download/bubble/download_bubble_controller.cc", - "views/download/bubble/download_bubble_controller.h", "views/download/bubble/download_bubble_row_list_view.cc", "views/download/bubble/download_bubble_row_list_view.h", "views/download/bubble/download_bubble_row_view.cc", @@ -5149,6 +5147,7 @@ "//extensions/components/native_app_window", "//extensions/strings", "//ui/base/cursor", + "//ui/color:color", ] allow_circular_includes_from += [ "//chrome/browser/apps/platform_apps",
diff --git a/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconUtils.java b/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconUtils.java index 7ad1fce..2320fefd 100644 --- a/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconUtils.java +++ b/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconUtils.java
@@ -9,6 +9,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import androidx.annotation.ColorInt; import androidx.annotation.Nullable; import androidx.core.graphics.drawable.RoundedBitmapDrawable; @@ -80,7 +81,7 @@ * @return A {@link Drawable} to be displayed as the favicon. */ public static Drawable getIconDrawableWithoutFilter(@Nullable Bitmap icon, GURL url, - int fallbackColor, RoundedIconGenerator iconGenerator, Resources resources, + @ColorInt int fallbackColor, RoundedIconGenerator iconGenerator, Resources resources, int iconSize) { return getIconDrawableWithoutFilter( icon, url.getSpec(), fallbackColor, iconGenerator, resources, iconSize); @@ -88,7 +89,7 @@ @Deprecated // Use GURL variant instead. public static Drawable getIconDrawableWithoutFilter(@Nullable Bitmap icon, String url, - int fallbackColor, RoundedIconGenerator iconGenerator, Resources resources, + @ColorInt int fallbackColor, RoundedIconGenerator iconGenerator, Resources resources, int iconSize) { if (icon == null) { iconGenerator.setBackgroundColor(fallbackColor);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/ChromeAutocompleteSchemeClassifier.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/ChromeAutocompleteSchemeClassifier.java index 4647248..ecc13975 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/ChromeAutocompleteSchemeClassifier.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/ChromeAutocompleteSchemeClassifier.java
@@ -31,7 +31,7 @@ } @NativeMethods - interface Natives { + public interface Natives { long createAutocompleteClassifier(Profile profile); void deleteAutocompleteClassifier(long chromeAutocompleteSchemeClassifier); }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java index df8e396d..1dec07d 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
@@ -7,6 +7,7 @@ import android.content.Context; import android.text.SpannableStringBuilder; import android.text.TextUtils; +import android.util.LruCache; import androidx.annotation.ColorInt; import androidx.annotation.ColorRes; @@ -42,6 +43,7 @@ import org.chromium.components.embedder_support.util.UrlConstants; import org.chromium.components.embedder_support.util.UrlUtilities; import org.chromium.components.metrics.OmniboxEventProtos.OmniboxEventProto.PageClassification; +import org.chromium.components.omnibox.AutocompleteSchemeClassifier; import org.chromium.components.omnibox.OmniboxUrlEmphasizer; import org.chromium.components.omnibox.SecurityStatusIcon; import org.chromium.components.prefs.PrefService; @@ -54,11 +56,59 @@ import org.chromium.url.URI; import java.net.URISyntaxException; +import java.util.Objects; /** * Provides a way of accessing toolbar data and state. */ public class LocationBarModel implements ToolbarDataProvider, LocationBarDataProvider { + private static final int LRU_CACHE_SIZE = 10; + static class SpannableDisplayTextCacheKey { + @NonNull + private final String mUrl; + @NonNull + private final String mDisplayText; + private final int mSecurityLevel; + private final int mNonEmphasizedColor; + private final int mEmphasizedColor; + private final int mDangerColor; + private final int mSecureColor; + + private SpannableDisplayTextCacheKey(@NonNull String url, @NonNull String displayText, + int securityLevel, int nonEmphasizedColor, int emphasizedColor, int dangerColor, + int secureColor) { + mUrl = url; + mDisplayText = displayText; + mSecurityLevel = securityLevel; + mNonEmphasizedColor = nonEmphasizedColor; + mEmphasizedColor = emphasizedColor; + mDangerColor = dangerColor; + mSecureColor = secureColor; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SpannableDisplayTextCacheKey that = (SpannableDisplayTextCacheKey) o; + return mSecurityLevel == that.mSecurityLevel + && mNonEmphasizedColor == that.mNonEmphasizedColor + && mEmphasizedColor == that.mEmphasizedColor + && mDangerColor == that.mDangerColor && mSecureColor == that.mSecureColor + && mUrl.equals(that.mUrl) && mDisplayText.equals(that.mDisplayText); + } + + @Override + public int hashCode() { + return Objects.hash(mUrl, mDisplayText, mSecurityLevel, mNonEmphasizedColor, + mEmphasizedColor, mDangerColor, mSecureColor); + } + } + /** * Formats the given URL to the original one of a distillation. */ @@ -100,6 +150,19 @@ private final @NonNull ProfileProvider mProfileProvider; private final @NonNull OfflineStatus mOfflineStatus; private final SearchEngineLogoUtils mSearchEngineLogoUtils; + // Always null if optimizations are disabled. Otherwise, non-null and unchanging following + // native init. Always tied to the mLastUsedNonOTRProfile which is safe because no underlying + // services have an incognito-specific instance. + @Nullable + private AutocompleteSchemeClassifier mChromeAutocompleteSchemeClassifier; + // Non-null and unchanging following native init. The last used non-OTR (or regular) profile + // can't change after this point because we don't support multi-profile on Android. + @Nullable + private Profile mLastUsedNonOTRProfile; + @Nullable + private LruCache<SpannableDisplayTextCacheKey, SpannableStringBuilder> + mSpannableDisplayTextCache; + private boolean mOptimizationsEnabled; private Tab mTab; private int mPrimaryColor; @@ -141,6 +204,15 @@ * Handle any initialization that must occur after native has been initialized. */ public void initializeWithNative() { + mOptimizationsEnabled = + ChromeFeatureList.isEnabled(ChromeFeatureList.LOCATION_BAR_MODEL_OPTIMIZATIONS); + mLastUsedNonOTRProfile = Profile.getLastUsedRegularProfile(); + if (mOptimizationsEnabled) { + mSpannableDisplayTextCache = new LruCache<>(LRU_CACHE_SIZE); + mChromeAutocompleteSchemeClassifier = + new ChromeAutocompleteSchemeClassifier(getProfile()); + } + mNativeLocationBarModelAndroid = LocationBarModelJni.get().init(LocationBarModel.this); } @@ -148,6 +220,10 @@ * Destroys the native LocationBarModel. */ public void destroy() { + if (mChromeAutocompleteSchemeClassifier != null) { + mChromeAutocompleteSchemeClassifier.destroy(); + mChromeAutocompleteSchemeClassifier = null; + } if (mNativeLocationBarModelAndroid == 0) return; LocationBarModelJni.get().destroy(mNativeLocationBarModelAndroid, LocationBarModel.this); mNativeLocationBarModelAndroid = 0; @@ -207,19 +283,19 @@ } @Override + // TODO(https://crbug.com/1305374): migrate to GURL. public String getCurrentUrl() { // Provide NTP url instead of most recent tab url for searches in overview mode (when Start - // Surface is enabled). . + // Surface is enabled). + String url = getTab() != null && getTab().isInitialized() + ? getTab().getUrl().getSpec().trim() + : ""; if (isInOverviewAndShowingOmnibox() - || StartSurfaceConfiguration.shouldHandleAsNtp(getTab())) { + || StartSurfaceConfiguration.shouldHandleAsNtp(getTab(), url)) { return UrlConstants.NTP_URL; } - // TODO(yusufo) : Consider using this for all calls from getTab() for accessing url. - if (!hasTab() || !getTab().isInitialized()) return ""; - - // Tab.getUrl() returns empty string if it does not have a URL. - return getTab().getUrl().getSpec().trim(); + return url; } public void notifyUrlChanged() { @@ -250,11 +326,11 @@ // Part of scroll jank investigation http://crbug.com/905461. Will remove TraceEvent after // the investigation is complete. try (TraceEvent te = TraceEvent.scoped("LocationBarModel.getUrlBarData")) { - if (!hasTab() || StartSurfaceConfiguration.shouldHandleAsNtp(getTab())) { + String url = getCurrentUrl(); + if (!hasTab() || StartSurfaceConfiguration.shouldHandleAsNtp(getTab(), url)) { return UrlBarData.EMPTY; } - String url = getCurrentUrl(); if (!UrlBarData.shouldShowUrl(url, isIncognito())) { return UrlBarData.EMPTY; } @@ -299,7 +375,6 @@ private UrlBarData buildUrlBarData(String url, String displayText, String editingText) { SpannableStringBuilder spannableDisplayText = new SpannableStringBuilder(displayText); - if (mNativeLocationBarModelAndroid != 0 && spannableDisplayText.length() > 0 && shouldEmphasizeUrl()) { boolean isInternalPage = false; @@ -309,8 +384,6 @@ // Ignore as this only is for applying color } - ChromeAutocompleteSchemeClassifier chromeAutocompleteSchemeClassifier = - new ChromeAutocompleteSchemeClassifier(getProfile()); final @BrandedColorScheme int brandedColorScheme = OmniboxResourceProvider.getBrandedColorScheme( mContext, isIncognito(), getPrimaryColor()); @@ -323,13 +396,32 @@ OmniboxResourceProvider.getUrlBarDangerColor(mContext, brandedColorScheme); final @ColorInt int secureColor = OmniboxResourceProvider.getUrlBarSecureColor(mContext, brandedColorScheme); - OmniboxUrlEmphasizer.emphasizeUrl(spannableDisplayText, - chromeAutocompleteSchemeClassifier, getSecurityLevel(), isInternalPage, - shouldEmphasizeHttpsScheme(), nonEmphasizedColor, emphasizedColor, dangerColor, - secureColor); - chromeAutocompleteSchemeClassifier.destroy(); - } + AutocompleteSchemeClassifier autocompleteSchemeClassifier; + SpannableDisplayTextCacheKey cacheKey = + new SpannableDisplayTextCacheKey(url, displayText, getSecurityLevel(), + nonEmphasizedColor, emphasizedColor, dangerColor, secureColor); + SpannableStringBuilder cachedSpannableDisplayText = null; + if (mOptimizationsEnabled) { + autocompleteSchemeClassifier = mChromeAutocompleteSchemeClassifier; + cachedSpannableDisplayText = mSpannableDisplayTextCache.get(cacheKey); + } else { + autocompleteSchemeClassifier = new ChromeAutocompleteSchemeClassifier(getProfile()); + } + + if (cachedSpannableDisplayText != null) { + return UrlBarData.forUrlAndText(url, cachedSpannableDisplayText, editingText); + } else { + OmniboxUrlEmphasizer.emphasizeUrl(spannableDisplayText, + autocompleteSchemeClassifier, getSecurityLevel(), isInternalPage, + shouldEmphasizeHttpsScheme(), nonEmphasizedColor, emphasizedColor, + dangerColor, secureColor); + if (mOptimizationsEnabled) { + mSpannableDisplayTextCache.put(cacheKey, spannableDisplayText); + } + } + if (!mOptimizationsEnabled) autocompleteSchemeClassifier.destroy(); + } return UrlBarData.forUrlAndText(url, spannableDisplayText, editingText); } @@ -344,6 +436,11 @@ return TrustedCdn.getPublisherUrl(mTab) == null; } + @VisibleForTesting + LruCache<SpannableDisplayTextCacheKey, SpannableStringBuilder> getCacheForTesting() { + return mSpannableDisplayTextCache; + } + /** * @return Whether the light security theme should be used. */ @@ -398,7 +495,6 @@ @Override public Profile getProfile() { - Profile lastUsedRegularProfile = Profile.getLastUsedRegularProfile(); if (mIsIncognito) { WindowAndroid windowAndroid = (mTab != null) ? mTab.getWindowAndroid() : null; // If the mTab belongs to a CustomTabActivity then we return the non-primary OTR profile @@ -408,11 +504,11 @@ // When in overview mode with no open tabs, there has not been created an // OTR profile yet. - assert lastUsedRegularProfile.hasPrimaryOTRProfile() || isInOverviewAndShowingOmnibox(); + assert mLastUsedNonOTRProfile.hasPrimaryOTRProfile() || isInOverviewAndShowingOmnibox(); // Return the primary OTR profile. - return lastUsedRegularProfile.getPrimaryOTRProfile(/*createIfNeeded=*/true); + return mLastUsedNonOTRProfile.getPrimaryOTRProfile(/*createIfNeeded=*/true); } - return lastUsedRegularProfile; + return mLastUsedNonOTRProfile; } public void setLayoutStateProvider(LayoutStateProvider layoutStateProvider) {
diff --git a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_data_sharing_consent_item.xml b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_data_sharing_consent_item.xml index 36852cc1..ed01b838 100644 --- a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_data_sharing_consent_item.xml +++ b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_data_sharing_consent_item.xml
@@ -12,7 +12,7 @@ android:layout_marginEnd="@dimen/account_selection_sheet_horizontal_margin" android:layout_marginStart="@dimen/account_selection_sheet_horizontal_margin" android:layout_marginBottom="@dimen/account_selection_sheet_item_padding" - android:layout_marginTop="@dimen/account_selection_sheet_item_padding" + android:layout_marginTop="8dp" android:gravity="start" android:textAppearance="@style/TextAppearance.TextSmall.Secondary" android:minHeight="@dimen/account_selection_sheet_title_height"/>
diff --git a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml index 1a93a0e..52f3041 100644 --- a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml +++ b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml
@@ -35,6 +35,8 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" + android:paddingTop="12dp" + android:paddingBottom="12dp" android:focusable="true" android:focusableInTouchMode="true" android:screenReaderFocusable="true"
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc index 9de15dab..8da415b 100644 --- a/chrome/browser/ui/app_list/app_list_client_impl.cc +++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -310,8 +310,6 @@ void AppListClientImpl::ViewClosing() { display_id_ = display::kInvalidDisplayId; - if (search_controller_) - search_controller_->ViewClosing(); } void AppListClientImpl::ViewShown(int64_t display_id) { @@ -396,6 +394,8 @@ !ash::features::IsProductivityLauncherEnabled()) { search_controller_->StartSearch(std::u16string()); } + if (!visible && search_controller_) + search_controller_->ViewClosing(); } void AppListClientImpl::OnAppListVisibilityChanged(bool visible) {
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result.cc b/chrome/browser/ui/app_list/search/omnibox_answer_result.cc index 9038075..560b824 100644 --- a/chrome/browser/ui/app_list/search/omnibox_answer_result.cc +++ b/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
@@ -122,20 +122,6 @@ return {text_item}; } -std::vector<TextItem> AddBoldTags(std::vector<TextItem> text_vector) { - std::vector<TextItem> bolded_vector; - for (const auto& old_text : text_vector) { - auto new_text = old_text; - if (old_text.GetType() == TextType::kString) { - auto tags = old_text.GetTextTags(); - tags.push_back(Tag(Tag::MATCH, 0, old_text.GetText().length())); - new_text.SetTextTags(tags); - } - bolded_vector.push_back(new_text); - } - return bolded_vector; -} - std::u16string ComputeAccessibleName( const std::vector<std::vector<TextItem>>& text_vectors) { std::vector<std::u16string> text; @@ -217,12 +203,12 @@ std::vector<TextItem> contents_vector = MatchFieldsToTextVector(match_.contents, match_.contents_class); if (match_.description.empty()) { - SetTitleTextVector(contents_vector); - SetDetailsTextVector({CreateStringTextItem(query_)}); - } else { - SetTitleTextVector(MatchFieldsToTextVector(match_.description, - match_.description_class)); + SetTitleTextVector({CreateStringTextItem(query_)}); SetDetailsTextVector(contents_vector); + } else { + SetTitleTextVector(contents_vector); + SetDetailsTextVector(MatchFieldsToTextVector(match_.description, + match_.description_class)); } } else if (IsWeatherResult()) { const auto& second_line = match_.answer->second_line(); @@ -246,18 +232,10 @@ auto second_vector = ImageLineToTextVector(second_line); AppendAdditionalText(second_line, second_vector); - if (IsDictionaryResult()) { - SetTitleTextVector(first_vector); - SetDetailsTextVector(second_vector); - } else { - SetTitleTextVector(second_vector); - SetDetailsTextVector(first_vector); - } + SetTitleTextVector(first_vector); + SetDetailsTextVector(second_vector); } - // Bold the title field. - SetTitleTextVector(AddBoldTags(title_text_vector())); - std::u16string accessible_name = ComputeAccessibleName( {big_title_text_vector(), title_text_vector(), details_text_vector()}); SetAccessibleName(accessible_name); @@ -315,11 +293,6 @@ return match_.type == AutocompleteMatchType::CALCULATOR; } -bool OmniboxAnswerResult::IsDictionaryResult() const { - return match_.answer.has_value() && - match_.answer->type() == SuggestionAnswer::ANSWER_TYPE_DICTIONARY; -} - bool OmniboxAnswerResult::IsWeatherResult() const { return match_.answer.has_value() && match_.answer->type() == SuggestionAnswer::ANSWER_TYPE_WEATHER;
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result.h b/chrome/browser/ui/app_list/search/omnibox_answer_result.h index 623ce49b..a121accc 100644 --- a/chrome/browser/ui/app_list/search/omnibox_answer_result.h +++ b/chrome/browser/ui/app_list/search/omnibox_answer_result.h
@@ -49,7 +49,6 @@ void FetchImage(const GURL& url); bool IsCalculatorResult() const; - bool IsDictionaryResult() const; bool IsWeatherResult() const; Profile* profile_;
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc b/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc index c81d397..47ae7f0 100644 --- a/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc +++ b/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc
@@ -108,15 +108,13 @@ ASSERT_EQ(result.title_text_vector().size(), 1); const auto& title = result.title_text_vector()[0]; ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(title.GetText(), u"4"); - EXPECT_THAT(title.GetTextTags(), - testing::UnorderedElementsAre(TagEquals( - Tag(Tag::Style::MATCH, 0, title.GetText().length())))); + EXPECT_EQ(title.GetText(), u"2+2"); + EXPECT_TRUE(title.GetTextTags().empty()); ASSERT_EQ(result.details_text_vector().size(), 1); const auto& details = result.details_text_vector()[0]; ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(details.GetText(), u"2+2"); + EXPECT_EQ(details.GetText(), u"4"); EXPECT_TRUE(details.GetTextTags().empty()); } @@ -133,15 +131,13 @@ ASSERT_EQ(result.title_text_vector().size(), 1); const auto& title = result.title_text_vector()[0]; ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(title.GetText(), u"4"); - EXPECT_THAT(title.GetTextTags(), - testing::UnorderedElementsAre(TagEquals( - Tag(Tag::Style::MATCH, 0, title.GetText().length())))); + EXPECT_EQ(title.GetText(), u"2+2"); + EXPECT_TRUE(title.GetTextTags().empty()); ASSERT_EQ(result.details_text_vector().size(), 1); const auto& details = result.details_text_vector()[0]; ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(details.GetText(), u"2+2"); + EXPECT_EQ(details.GetText(), u"4"); EXPECT_TRUE(details.GetTextTags().empty()); } @@ -185,9 +181,7 @@ // Suggest server, this should be updated to display the weather description // instead of |match.contents|. EXPECT_EQ(title.GetText(), u"contents"); - EXPECT_THAT(title.GetTextTags(), - testing::UnorderedElementsAre(TagEquals( - Tag(Tag::Style::MATCH, 0, title.GetText().length())))); + EXPECT_TRUE(title.GetTextTags().empty()); ASSERT_EQ(result.details_text_vector().size(), 1); const auto& details = result.details_text_vector()[0]; @@ -196,71 +190,6 @@ EXPECT_TRUE(details.GetTextTags().empty()); } -TEST_F(OmniboxAnswerResultTest, DictionaryResult) { - // This comes from SuggestionAnswer::AnswerType::ANSWER_TYPE_DICTIONARY. - const std::u16string kDictionaryType = u"1"; - - SuggestionAnswer answer; - std::string json = - "{ \"l\": [" - " { \"il\": { \"t\": [{ \"t\": \"text one\", \"tt\": 8 }], " - " \"at\": { \"t\": \"additional one\", \"tt\": 42 } } }, " - " { \"il\": { \"t\": [{ \"t\": \"text two\", \"tt\": 8 }], " - " \"at\": { \"t\": \"additional two\", \"tt\": 42 } } } " - "] }"; - absl::optional<base::Value> value = base::JSONReader::Read(json); - ASSERT_TRUE(value && value->is_dict()); - ASSERT_TRUE(SuggestionAnswer::ParseAnswer(value->GetDict(), kDictionaryType, - &answer)); - - AutocompleteMatch match; - match.answer = answer; - match.contents = u"contents"; - match.description = u"description"; - - OmniboxAnswerResult result(nullptr, nullptr, nullptr, match, u"query"); - EXPECT_EQ(result.display_type(), ash::SearchResultDisplayType::kAnswerCard); - EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kOmnibox); - EXPECT_EQ(result.metrics_type(), ash::OMNIBOX_ANSWER); - - // All title fields should have the MATCH tag, and there should be a space - // delimiter added between each field. - const auto& title = result.title_text_vector(); - ASSERT_EQ(title.size(), 3); - ASSERT_EQ(title[0].GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(title[0].GetText(), u"contents"); - size_t length = title[0].GetText().length(); - EXPECT_THAT(title[0].GetTextTags(), testing::UnorderedElementsAre(TagEquals( - Tag(Tag::Style::MATCH, 0, length)))); - - ASSERT_EQ(title[1].GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(title[1].GetText(), u" "); - length = title[1].GetText().length(); - EXPECT_THAT(title[1].GetTextTags(), testing::UnorderedElementsAre(TagEquals( - Tag(Tag::Style::MATCH, 0, length)))); - - ASSERT_EQ(title[2].GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(title[2].GetText(), u"additional one"); - length = title[2].GetText().length(); - EXPECT_THAT(title[2].GetTextTags(), testing::UnorderedElementsAre(TagEquals( - Tag(Tag::Style::MATCH, 0, length)))); - - // Details text should not have tags. - const auto& details = result.details_text_vector(); - ASSERT_EQ(details.size(), 3); - ASSERT_EQ(details[0].GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(details[0].GetText(), u"text two"); - EXPECT_TRUE(details[0].GetTextTags().empty()); - - ASSERT_EQ(details[1].GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(details[1].GetText(), u" "); - EXPECT_TRUE(details[1].GetTextTags().empty()); - - ASSERT_EQ(details[2].GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(details[2].GetText(), u"additional two"); - EXPECT_TRUE(details[2].GetTextTags().empty()); -} - TEST_F(OmniboxAnswerResultTest, AnswerResult) { // This comes from SuggestionAnswer::AnswerType::ANSWER_TYPE_FINANCE. const std::u16string kWeatherType = u"2"; @@ -290,47 +219,41 @@ EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kOmnibox); EXPECT_EQ(result.metrics_type(), ash::OMNIBOX_ANSWER); - // All title fields should have the MATCH tag, and there should be a space - // delimiter added between each field. Additionally, the NEGATIVE text type - // should have RED tags, and the POSITIVE text type should have GREEN tags. const auto& title = result.title_text_vector(); ASSERT_EQ(title.size(), 3); ASSERT_EQ(title[0].GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(title[0].GetText(), u"text two"); - size_t length = title[0].GetText().length(); - EXPECT_THAT(title[0].GetTextTags(), - testing::UnorderedElementsAre( - TagEquals(Tag(Tag::Style::MATCH, 0, length)), - TagEquals(Tag(Tag::Style::RED, 0, length)))); + EXPECT_EQ(title[0].GetText(), u"contents"); + EXPECT_TRUE(title[0].GetTextTags().empty()); ASSERT_EQ(title[1].GetType(), ash::SearchResultTextItemType::kString); EXPECT_EQ(title[1].GetText(), u" "); - length = title[1].GetText().length(); - EXPECT_THAT(title[1].GetTextTags(), testing::UnorderedElementsAre(TagEquals( - Tag(Tag::Style::MATCH, 0, length)))); + EXPECT_TRUE(title[1].GetTextTags().empty()); ASSERT_EQ(title[2].GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(title[2].GetText(), u"additional two"); - length = title[2].GetText().length(); - EXPECT_THAT(title[2].GetTextTags(), - testing::UnorderedElementsAre( - TagEquals(Tag(Tag::Style::MATCH, 0, length)), - TagEquals(Tag(Tag::Style::GREEN, 0, length)))); + EXPECT_EQ(title[2].GetText(), u"additional one"); + EXPECT_TRUE(title[2].GetTextTags().empty()); - // Details text should not have tags. + // The NEGATIVE text type should have RED tags, and the POSITIVE text type + // should have GREEN tags. const auto& details = result.details_text_vector(); ASSERT_EQ(details.size(), 3); ASSERT_EQ(details[0].GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(details[0].GetText(), u"contents"); - EXPECT_TRUE(details[0].GetTextTags().empty()); + EXPECT_EQ(details[0].GetText(), u"text two"); + size_t length = details[0].GetText().length(); + EXPECT_THAT(details[0].GetTextTags(), testing::UnorderedElementsAre(TagEquals( + Tag(Tag::Style::RED, 0, length)))); ASSERT_EQ(details[1].GetType(), ash::SearchResultTextItemType::kString); EXPECT_EQ(details[1].GetText(), u" "); + length = details[1].GetText().length(); EXPECT_TRUE(details[1].GetTextTags().empty()); ASSERT_EQ(details[2].GetType(), ash::SearchResultTextItemType::kString); - EXPECT_EQ(details[2].GetText(), u"additional one"); - EXPECT_TRUE(details[2].GetTextTags().empty()); + EXPECT_EQ(details[2].GetText(), u"additional two"); + length = details[2].GetText().length(); + EXPECT_THAT(details[2].GetTextTags(), + testing::UnorderedElementsAre( + TagEquals(Tag(Tag::Style::GREEN, 0, length)))); } } // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/ranking/query_highlighter.cc b/chrome/browser/ui/app_list/search/ranking/query_highlighter.cc index 2e857963..30177d6 100644 --- a/chrome/browser/ui/app_list/search/ranking/query_highlighter.cc +++ b/chrome/browser/ui/app_list/search/ranking/query_highlighter.cc
@@ -44,17 +44,16 @@ DCHECK(it != results.end()); for (const auto& result : it->second) { - // Don't perform query highlighting on answer cards. - if (result->display_type() == ChromeSearchResult::DisplayType::kAnswerCard) - continue; - TextVector title_vector = result->title_text_vector(); SetMatchTags(last_query_, title_vector); result->SetTitleTextVector(title_vector); - TextVector details_vector = result->details_text_vector(); - SetMatchTags(last_query_, details_vector); - result->SetDetailsTextVector(details_vector); + if (result->display_type() != + ChromeSearchResult::DisplayType::kAnswerCard) { + TextVector details_vector = result->details_text_vector(); + SetMatchTags(last_query_, details_vector); + result->SetDetailsTextVector(details_vector); + } } }
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc index 5eb2c7af..6900baa 100644 --- a/chrome/browser/ui/ash/chrome_shell_delegate.cc +++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -110,6 +110,32 @@ return std::make_unique<ChromeCaptureModeDelegate>(); } +ash::AccessibilityDelegate* ChromeShellDelegate::CreateAccessibilityDelegate() { + return new ChromeAccessibilityDelegate; +} + +std::unique_ptr<ash::BackGestureContextualNudgeDelegate> +ChromeShellDelegate::CreateBackGestureContextualNudgeDelegate( + ash::BackGestureContextualNudgeController* controller) { + return std::make_unique<BackGestureContextualNudgeDelegate>(controller); +} + +std::unique_ptr<ash::NearbyShareDelegate> +ChromeShellDelegate::CreateNearbyShareDelegate( + ash::NearbyShareController* controller) const { + return std::make_unique<NearbyShareDelegateImpl>(controller); +} + +std::unique_ptr<ash::DesksTemplatesDelegate> +ChromeShellDelegate::CreateDesksTemplatesDelegate() const { + return std::make_unique<ChromeDesksTemplatesDelegate>(); +} + +scoped_refptr<network::SharedURLLoaderFactory> +ChromeShellDelegate::GetGeolocationSharedURLLoaderFactory() const { + return g_browser_process->shared_url_loader_factory(); +} + void ChromeShellDelegate::OpenKeyboardShortcutHelpPage() const { ash::NewWindowDelegate::GetPrimary()->OpenUrl( GURL(kKeyboardShortcutHelpPageUrl), @@ -193,27 +219,6 @@ return &content::GetMediaSessionService(); } -ash::AccessibilityDelegate* ChromeShellDelegate::CreateAccessibilityDelegate() { - return new ChromeAccessibilityDelegate; -} - -std::unique_ptr<ash::BackGestureContextualNudgeDelegate> -ChromeShellDelegate::CreateBackGestureContextualNudgeDelegate( - ash::BackGestureContextualNudgeController* controller) { - return std::make_unique<BackGestureContextualNudgeDelegate>(controller); -} - -std::unique_ptr<ash::NearbyShareDelegate> -ChromeShellDelegate::CreateNearbyShareDelegate( - ash::NearbyShareController* controller) const { - return std::make_unique<NearbyShareDelegateImpl>(controller); -} - -std::unique_ptr<ash::DesksTemplatesDelegate> -ChromeShellDelegate::CreateDesksTemplatesDelegate() const { - return std::make_unique<ChromeDesksTemplatesDelegate>(); -} - bool ChromeShellDelegate::IsSessionRestoreInProgress() const { Profile* profile = ProfileManager::GetActiveUserProfile(); return SessionRestore::IsRestoring(profile);
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h index 1ff456f..57cb4704 100644 --- a/chrome/browser/ui/ash/chrome_shell_delegate.h +++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -32,6 +32,8 @@ ash::NearbyShareController* controller) const override; std::unique_ptr<ash::DesksTemplatesDelegate> CreateDesksTemplatesDelegate() const override; + scoped_refptr<network::SharedURLLoaderFactory> + GetGeolocationSharedURLLoaderFactory() const override; void OpenKeyboardShortcutHelpPage() const override; bool CanGoBack(gfx::NativeWindow window) const override; void SetTabScrubberChromeOSEnabled(bool enabled) override;
diff --git a/chrome/browser/ui/ash/desks_templates/chrome_desks_templates_delegate.cc b/chrome/browser/ui/ash/desks_templates/chrome_desks_templates_delegate.cc index ebb045e..1d69a526 100644 --- a/chrome/browser/ui/ash/desks_templates/chrome_desks_templates_delegate.cc +++ b/chrome/browser/ui/ash/desks_templates/chrome_desks_templates_delegate.cc
@@ -49,11 +49,11 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/image_model.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/base/themed_vector_icon.h" #include "ui/color/color_id.h" #include "ui/color/color_provider.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/paint_vector_icon.h" -#include "ui/native_theme/themed_vector_icon.h" #include "url/gurl.h" namespace {
diff --git a/chrome/browser/ui/ash/network/tether_notification_presenter.cc b/chrome/browser/ui/ash/network/tether_notification_presenter.cc index 0f26818..f3528974 100644 --- a/chrome/browser/ui/ash/network/tether_notification_presenter.cc +++ b/chrome/browser/ui/ash/network/tether_notification_presenter.cc
@@ -6,6 +6,7 @@ #include <string> +#include "ash/components/multidevice/logging/logging.h" #include "ash/public/cpp/network_icon_image_source.h" #include "ash/public/cpp/notification_utils.h" #include "base/bind.h" @@ -20,7 +21,6 @@ #include "chrome/common/url_constants.h" #include "chrome/common/webui_url_constants.h" #include "chrome/grit/generated_resources.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/network/network_connect.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/color_palette.h"
diff --git a/chrome/browser/ui/ash/network/tether_notification_presenter.h b/chrome/browser/ui/ash/network/tether_notification_presenter.h index b3eabe4..50cb44f 100644 --- a/chrome/browser/ui/ash/network/tether_notification_presenter.h +++ b/chrome/browser/ui/ash/network/tether_notification_presenter.h
@@ -8,10 +8,10 @@ #include <memory> #include <string> +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/tether/notification_presenter.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "chromeos/network/network_state.h" #include "ui/message_center/public/cpp/notification.h"
diff --git a/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc b/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc index 47311ca..e33b3b34 100644 --- a/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc +++ b/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "base/memory/ptr_util.h" #include "base/observer_list.h" #include "base/test/metrics/histogram_tester.h" @@ -14,7 +15,6 @@ #include "chrome/common/url_constants.h" #include "chrome/test/base/browser_with_test_window_test.h" #include "chrome/test/base/testing_profile.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "chromeos/network/network_connect.h" namespace {
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc index 9d7bbb9..a65fc5d6 100644 --- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc +++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
@@ -3120,18 +3120,29 @@ }; IN_PROC_BROWSER_TEST_F(FilesSystemWebAppPinnedTest, EnterpriseMigration) { - // Setup: the customer pins Files Chrome App (hhaomji... app ID). + // Setup: the customer pins Files Chrome App (ID:hhaomji...). base::DictionaryValue entry; entry.SetKey(ChromeShelfPrefs::kPinnedAppsPrefAppIDKey, base::Value(file_manager::kFileManagerAppId)); base::ListValue policy_value; policy_value.Append(std::move(entry)); profile()->GetPrefs()->Set(prefs::kPolicyPinnedLauncherApps, policy_value); - WaitForSystemAppsSynchronized(); - // Expected results: fkiggjm... is pinned. - ash::ShelfID shelf_id(file_manager::kFileManagerSwaAppId); - EXPECT_TRUE(ChromeShelfController::instance()->IsPinned(shelf_id)); + // Ensure shelf is updated. + WaitForSystemAppsSynchronized(); + web_app::WebAppProvider::GetForTest(browser()->profile()) + ->install_manager() + .NotifyWebAppInstalledWithOsHooks(file_manager::kFileManagerSwaAppId); + apps::AppServiceProxyFactory::GetForProfile(profile()) + ->FlushMojoCallsForTesting(); + + // Expected results: Files SWA App (ID:fkiggjm...) is force-pinned. + ash::ShelfID swa_shelf_id(file_manager::kFileManagerSwaAppId); + EXPECT_TRUE(ChromeShelfController::instance()->IsPinned(swa_shelf_id)); + ash::ShelfID extension_shelf_id(file_manager::kFileManagerAppId); + EXPECT_FALSE(ChromeShelfController::instance()->IsPinned(extension_shelf_id)); + EXPECT_EQ(AppListControllerDelegate::PIN_FIXED, + GetPinnableForAppID(file_manager::kFileManagerSwaAppId, profile())); } INSTANTIATE_TEST_SUITE_P(All, PerDeskShelfAppBrowserTest, ::testing::Bool());
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc index 1e2158fa..f392a82 100644 --- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc +++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
@@ -62,16 +62,18 @@ // Chrome App. std::string GetPolicyValueFromAppId(const std::string& app_id, Profile* profile) { - // Handle Web App ids + // Handle App Service apps (eg. web apps). // - // WebAppProvider is absent in some cases e.g. Arc++ Kiosk Mode. - if (auto* provider = web_app::WebAppProvider::GetDeprecated(profile)) { - std::map<web_app::AppId, GURL> installed_apps = - provider->registrar().GetExternallyInstalledApps( - web_app::ExternalInstallSource::kExternalPolicy); - auto it = installed_apps.find(app_id); - if (it != installed_apps.end()) - return it->second.spec(); + // App Service is absent in some cases e.g. Arc++ Kiosk Mode. + if (apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)) { + std::string policy_id; + apps::AppServiceProxyFactory::GetForProfile(profile) + ->AppRegistryCache() + .ForOneApp(app_id, [&policy_id](const apps::AppUpdate& update) { + policy_id = update.PolicyId(); + }); + if (!policy_id.empty()) + return policy_id; } // Handle Arc++ ids
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc index e4a5da8..5adbc8b 100644 --- a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc +++ b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
@@ -250,25 +250,6 @@ continue; } - // Handle Chrome App ids - if (crx_file::id_util::IdIsValid(*policy_entry)) { - if (*policy_entry == file_manager::kFileManagerAppId && - chromeos::features::IsFileManagerSwaEnabled()) { - absl::optional<std::string> files_app_id = - web_app::GetAppIdForSystemWebApp( - helper->profile(), web_app::SystemAppType::FILE_MANAGER); - if (files_app_id) { - result.emplace_back(*files_app_id); - } else { - // Fall-back to the policy_entry if we cannot fetch one for Files app. - result.emplace_back(*policy_entry); - } - } else { - result.emplace_back(*policy_entry); - } - continue; - } - // URLs provided through policy might not match exactly (eg. missing // trailing slash), so check the normalized version of valid URLs too. std::vector<std::string> policy_entries_to_check{*policy_entry}; @@ -281,6 +262,7 @@ // Handle App Service policy IDs (currently Web Apps only) if (apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile( helper->profile())) { + size_t initial_result_size = result.size(); apps::AppServiceProxyFactory::GetForProfile(helper->profile()) ->AppRegistryCache() .ForEachApp([&result, &policy_entries_to_check]( @@ -290,6 +272,15 @@ result.emplace_back(update.AppId()); } }); + if (result.size() > initial_result_size) { + continue; + } + } + + // Handle Chrome App ids + if (crx_file::id_util::IdIsValid(*policy_entry)) { + result.emplace_back(*policy_entry); + continue; } // Handle Arc++ App ids
diff --git a/chrome/browser/ui/autofill/payments/virtual_card_enroll_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/virtual_card_enroll_bubble_controller_impl.cc index ee7accd..fc7e54f 100644 --- a/chrome/browser/ui/autofill/payments/virtual_card_enroll_bubble_controller_impl.cc +++ b/chrome/browser/ui/autofill/payments/virtual_card_enroll_bubble_controller_impl.cc
@@ -156,7 +156,11 @@ result = VirtualCardEnrollmentBubbleResult:: VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS; break; - default: + case PaymentsBubbleClosedReason::kCancelled: + result = VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_CANCELLED; + break; + case PaymentsBubbleClosedReason::kUnknown: NOTREACHED(); result = VirtualCardEnrollmentBubbleResult:: VIRTUAL_CARD_ENROLLMENT_BUBBLE_RESULT_UNKNOWN;
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils.cc b/chrome/browser/ui/bookmarks/bookmark_utils.cc index 9dd0ce8f..7c41c782 100644 --- a/chrome/browser/ui/bookmarks/bookmark_utils.cc +++ b/chrome/browser/ui/bookmarks/bookmark_utils.cc
@@ -40,6 +40,7 @@ #if defined(TOOLKIT_VIEWS) #include "chrome/grit/theme_resources.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/base/themed_vector_icon.h" #include "ui/color/color_id.h" #include "ui/color/color_provider.h" #include "ui/gfx/canvas.h" @@ -48,7 +49,6 @@ #include "ui/gfx/image/image_skia_source.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/scoped_canvas.h" -#include "ui/native_theme/themed_vector_icon.h" #include "ui/resources/grit/ui_resources.h" #endif
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index 3284c6c..ace0c593 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc
@@ -1984,8 +1984,11 @@ ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler( protocol, url, GetProtocolHandlerSecurityLevel(requesting_frame)); - if (!handler.IsValid()) - return; + // The parameters's normalization process defined in the spec has been already + // applied in the WebContentImpl class, so at this point it shouldn't be + // possible to create an invalid handler. + // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters + DCHECK(handler.IsValid()); custom_handlers::ProtocolHandlerRegistry* registry = ProtocolHandlerRegistryFactory::GetForBrowserContext(context);
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc index f9d291e..123291fe 100644 --- a/chrome/browser/ui/browser_command_controller.cc +++ b/chrome/browser/ui/browser_command_controller.cc
@@ -811,7 +811,7 @@ case IDC_SHOW_BETA_FORUM: ShowBetaForum(browser_); break; - case IDC_TOGGLE_COMMANDER: + case IDC_TOGGLE_QUICK_COMMANDS: ToggleCommander(browser_); break; #if BUILDFLAG(ENABLE_DICE_SUPPORT) @@ -1119,7 +1119,7 @@ IDC_SHOW_SAVE_LOCAL_CARD_SIGN_IN_PROMO_IF_APPLICABLE, true); command_updater_.UpdateCommandEnabled(IDC_CLOSE_SIGN_IN_PROMO, true); command_updater_.UpdateCommandEnabled(IDC_CARET_BROWSING_TOGGLE, true); - command_updater_.UpdateCommandEnabled(IDC_TOGGLE_COMMANDER, + command_updater_.UpdateCommandEnabled(IDC_TOGGLE_QUICK_COMMANDS, commander::IsEnabled()); UpdateShowSyncState(true);
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h index 19e4df3..9220a35 100644 --- a/chrome/browser/ui/browser_window.h +++ b/chrome/browser/ui/browser_window.h
@@ -472,7 +472,8 @@ // Whether or not the shelf view is visible. virtual bool IsDownloadShelfVisible() const = 0; - // Returns the DownloadShelf. + // Returns the DownloadShelf. Returns null if download shelf is disabled. This + // can happen if the new download bubble UI is enabled. virtual DownloadShelf* GetDownloadShelf() = 0; // Shows the confirmation dialog box warning that the browser is closing with
diff --git a/chrome/browser/ui/cocoa/accelerators_cocoa.mm b/chrome/browser/ui/cocoa/accelerators_cocoa.mm index 7f86487..809554c 100644 --- a/chrome/browser/ui/cocoa/accelerators_cocoa.mm +++ b/chrome/browser/ui/cocoa/accelerators_cocoa.mm
@@ -138,7 +138,7 @@ if (commander::IsEnabled()) { result = accelerators_.insert( - std::make_pair(IDC_TOGGLE_COMMANDER, + std::make_pair(IDC_TOGGLE_QUICK_COMMANDS, ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN))); DCHECK(result.second); }
diff --git a/chrome/browser/ui/cocoa/main_menu_builder.mm b/chrome/browser/ui/cocoa/main_menu_builder.mm index aff8f93a..a116531 100644 --- a/chrome/browser/ui/cocoa/main_menu_builder.mm +++ b/chrome/browser/ui/cocoa/main_menu_builder.mm
@@ -286,8 +286,8 @@ Item(IDS_DISTILL_PAGE) .command_id(IDC_DISTILL_PAGE) .remove_if(!dom_distiller::IsDomDistillerEnabled()), - Item(IDS_TOGGLE_COMMANDER) - .command_id(IDC_TOGGLE_COMMANDER) + Item(IDS_TOGGLE_QUICK_COMMANDS) + .command_id(IDC_TOGGLE_QUICK_COMMANDS) .remove_if(!commander::IsEnabled()), Item().is_separator(),
diff --git a/chrome/browser/ui/cocoa/task_manager_mac.mm b/chrome/browser/ui/cocoa/task_manager_mac.mm index 00c21be..0f55c08 100644 --- a/chrome/browser/ui/cocoa/task_manager_mac.mm +++ b/chrome/browser/ui/cocoa/task_manager_mac.mm
@@ -33,7 +33,6 @@ #include "ui/base/models/image_model.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_util_mac.h" -#include "ui/views/image_model_utils.h" namespace { @@ -715,8 +714,8 @@ NSImage* TaskManagerMac::GetImageForRow(int row) { const NSSize kImageSize = NSMakeSize(16.0, 16.0); - NSImage* image = gfx::NSImageFromImageSkia( - views::GetImageSkiaFromImageModel(table_model_.GetIcon(row), nullptr)); + NSImage* image = + gfx::NSImageFromImageSkia(table_model_.GetIcon(row).Rasterize(nullptr)); if (image) image.size = kImageSize; else
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h index cf2b8ef..d081979 100644 --- a/chrome/browser/ui/color/chrome_color_id.h +++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -173,6 +173,16 @@ E_CPONLY(kColorPipWindowHangUpButtonForeground) \ E_CPONLY(kColorPipWindowSkipAdButtonBackground) \ E_CPONLY(kColorPipWindowSkipAdButtonBorder) \ + /* PWA colors. */ \ + E_CPONLY(kColorPwaBackground) \ + E_CPONLY(kColorPwaMenuButtonIcon) \ + E_CPONLY(kColorPwaSecurityChipForeground) \ + E_CPONLY(kColorPwaSecurityChipForegroundDangerous) \ + E_CPONLY(kColorPwaSecurityChipForegroundPolicyCert) \ + E_CPONLY(kColorPwaSecurityChipForegroundSecure) \ + E_CPONLY(kColorPwaTheme) \ + E_CPONLY(kColorPwaToolbarBackground) \ + E_CPONLY(kColorPwaToolbarForeground) \ /* QR code colors. */ \ E_CPONLY(kColorQrCodeBackground) \ E_CPONLY(kColorQrCodeBorder) \
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc index d9d86c06..c8705f7 100644 --- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc +++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -90,7 +90,6 @@ #include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h" #include "third_party/blink/public/common/switches.h" #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h" -#include "ui/views/image_model_utils.h" #if BUILDFLAG(IS_CHROMEOS_ASH) #include "ash/constants/ash_features.h" @@ -504,8 +503,7 @@ app_service_test().LoadAppIconBlocking( apps::mojom::AppType::kChromeApp, app_id_, extension_misc::EXTENSION_ICON_SMALL), - views::GetImageSkiaFromImageModel( - app_browser_->app_controller()->GetWindowAppIcon(), nullptr))); + app_browser_->app_controller()->GetWindowAppIcon().Rasterize(nullptr))); } #endif
diff --git a/chrome/browser/ui/intent_picker_tab_helper.cc b/chrome/browser/ui/intent_picker_tab_helper.cc index 5bb52a2..70fb7296 100644 --- a/chrome/browser/ui/intent_picker_tab_helper.cc +++ b/chrome/browser/ui/intent_picker_tab_helper.cc
@@ -12,6 +12,7 @@ #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h" +#include "chrome/browser/apps/intent_helper/intent_picker_features.h" #include "chrome/browser/apps/intent_helper/intent_picker_helpers.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" @@ -91,7 +92,7 @@ tab_helper->should_show_icon_ = should_show_icon; - if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) { + if (apps::features::LinkCapturingUiUpdateEnabled()) { tab_helper->UpdateCollapsedState(); }
diff --git a/chrome/browser/ui/startup/web_app_startup_utils.cc b/chrome/browser/ui/startup/web_app_startup_utils.cc index 0570a70d..56be9f61 100644 --- a/chrome/browser/ui/startup/web_app_startup_utils.cc +++ b/chrome/browser/ui/startup/web_app_startup_utils.cc
@@ -47,6 +47,7 @@ #include "components/keep_alive_registry/keep_alive_types.h" #include "components/keep_alive_registry/scoped_keep_alive.h" #include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h" +#include "third_party/blink/public/common/security/protocol_handler_security_level.h" #include "url/gurl.h" namespace web_app { @@ -172,11 +173,10 @@ // has a wait for "on_registry_ready()", `potential_protocol` checks for // blink::IsValidCustomHandlerScheme() here to avoid loading the // WebAppProvider with a false positive. - bool unused_has_custom_scheme_prefix = false; if (potential_protocol.is_valid() && - blink::IsValidCustomHandlerScheme(potential_protocol.scheme(), - /*allow_ext_prefix=*/false, - unused_has_custom_scheme_prefix)) { + blink::IsValidCustomHandlerScheme( + potential_protocol.scheme(), + blink::ProtocolHandlerSecurityLevel::kStrict)) { protocol_url = std::move(potential_protocol); break; }
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc index b14b485a..0f0a8f0 100644 --- a/chrome/browser/ui/toolbar/app_menu_model.cc +++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -238,7 +238,7 @@ AddItemWithStringId(IDC_CREATE_SHORTCUT, IDS_ADD_TO_OS_LAUNCH_SURFACE); AddItemWithStringId(IDC_NAME_WINDOW, IDS_NAME_WINDOW); if (commander::IsEnabled()) - AddItemWithStringId(IDC_TOGGLE_COMMANDER, IDS_TOGGLE_COMMANDER); + AddItemWithStringId(IDC_TOGGLE_QUICK_COMMANDS, IDS_TOGGLE_QUICK_COMMANDS); AddSeparator(ui::NORMAL_SEPARATOR); AddItemWithStringId(IDC_CLEAR_BROWSING_DATA, IDS_CLEAR_BROWSING_DATA);
diff --git a/chrome/browser/ui/views/accelerator_table.cc b/chrome/browser/ui/views/accelerator_table.cc index 27564a1..6a99ee14 100644 --- a/chrome/browser/ui/views/accelerator_table.cc +++ b/chrome/browser/ui/views/accelerator_table.cc
@@ -226,7 +226,7 @@ // TODO(https://crbug.com/1016439): This is a temporary hotkey. Chrome OS // uses this for switching IMEs, but since this feature is only exposed via // command line flag at the moment, we'll exclude them entirely for now. - {ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, IDC_TOGGLE_COMMANDER}, + {ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, IDC_TOGGLE_QUICK_COMMANDS}, #endif // !BUILDFLAG(IS_CHROMEOS_ASH) #endif // !BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc index 9242d25..782cfa1 100644 --- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc +++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -52,7 +52,6 @@ #include "ui/views/controls/menu/menu_model_adapter.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/webview/webview.h" -#include "ui/views/image_model_utils.h" #include "ui/views/widget/widget.h" #include "ui/wm/core/coordinate_conversion.h" @@ -183,8 +182,7 @@ return ui::ImageModel(); DCHECK(image.IsImage()); - const gfx::ImageSkia image_skia = - views::GetImageSkiaFromImageModel(image, nullptr); + const gfx::ImageSkia image_skia = image.Rasterize(nullptr); return ui::ImageModel::FromImageSkia( apps::CreateStandardIconImage(image_skia)); }
diff --git a/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views_interactive_uitest.cc b/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views_interactive_uitest.cc index afcef475..1763373 100644 --- a/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views_interactive_uitest.cc +++ b/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views_interactive_uitest.cc
@@ -156,6 +156,62 @@ } } + void TestCloseBubbleForExpectedResultFromSource( + VirtualCardEnrollmentBubbleResult expected_result, + VirtualCardEnrollmentSource source) { + base::HistogramTester histogram_tester; + ShowBubbleAndWaitUntilShown(GetFieldsForSource(source), base::DoNothing(), + base::DoNothing()); + + ASSERT_TRUE(GetBubbleViews()); + ASSERT_TRUE(IsIconVisible()); + + views::Widget::ClosedReason closed_reason; + switch (expected_result) { + case VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_ACCEPTED: + closed_reason = views::Widget::ClosedReason::kAcceptButtonClicked; + break; + case VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_CLOSED: + closed_reason = views::Widget::ClosedReason::kCloseButtonClicked; + break; + case VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS: + closed_reason = views::Widget::ClosedReason::kLostFocus; + break; + case VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_CANCELLED: + closed_reason = views::Widget::ClosedReason::kCancelButtonClicked; + break; + case VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_NOT_INTERACTED: + // The VirtualCardEnrollBubble will be closed differently in the not + // interacted case, so no need to set |closed_reason| here. + break; + case VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_RESULT_UNKNOWN: + NOTREACHED(); + break; + } + + views::test::WidgetDestroyedWaiter destroyed_waiter( + GetBubbleViews()->GetWidget()); + + if (expected_result != VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_NOT_INTERACTED) { + GetBubbleViews()->GetWidget()->CloseWithReason(closed_reason); + } else { + browser()->tab_strip_model()->CloseAllTabs(); + } + + destroyed_waiter.Wait(); + histogram_tester.ExpectBucketCount( + "Autofill.VirtualCardEnrollBubble.Result." + + VirtualCardEnrollmentSourceToMetricSuffix(source) + ".FirstShow", + expected_result, 1); + } + private: VirtualCardEnrollmentFields downstream_virtual_card_enrollment_fields_; VirtualCardEnrollmentFields upstream_virtual_card_enrollment_fields_; @@ -195,119 +251,69 @@ EXPECT_TRUE(IsIconVisible()); } -IN_PROC_BROWSER_TEST_F(VirtualCardEnrollBubbleViewsInteractiveUiTest, - Metrics_BubbleLostFocus) { - base::HistogramTester histogram_tester; - std::string histogram_name; - - for (VirtualCardEnrollmentSource virtual_card_enrollment_source : - {VirtualCardEnrollmentSource::kUpstream, - VirtualCardEnrollmentSource::kDownstream, - VirtualCardEnrollmentSource::kSettingsPage}) { - ShowBubbleAndWaitUntilShown( - GetFieldsForSource(virtual_card_enrollment_source), base::DoNothing(), - base::DoNothing()); - - ASSERT_TRUE(GetBubbleViews()); - ASSERT_TRUE(IsIconVisible()); - - // Mock deactivation and lose focus. - views::test::WidgetDestroyedWaiter destroyed_waiter( - GetBubbleViews()->GetWidget()); - GetBubbleViews()->GetWidget()->CloseWithReason( - views::Widget::ClosedReason::kLostFocus); - destroyed_waiter.Wait(); - - histogram_tester.ExpectBucketCount( - "Autofill.VirtualCardEnrollBubble.Result." + - VirtualCardEnrollmentSourceToMetricSuffix( - virtual_card_enrollment_source) + - ".FirstShow", - VirtualCardEnrollmentBubbleResult:: - VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS, - 1); - } -} - -IN_PROC_BROWSER_TEST_F(VirtualCardEnrollBubbleViewsInteractiveUiTest, - Metrics_BubbleAccepted) { - base::HistogramTester histogram_tester; - std::string histogram_name; - - for (VirtualCardEnrollmentSource virtual_card_enrollment_source : - {VirtualCardEnrollmentSource::kUpstream, - VirtualCardEnrollmentSource::kDownstream, - VirtualCardEnrollmentSource::kSettingsPage}) { - ShowBubbleAndWaitUntilShown( - GetFieldsForSource(virtual_card_enrollment_source), base::DoNothing(), - base::DoNothing()); - - ASSERT_TRUE(GetBubbleViews()); - ASSERT_TRUE(IsIconVisible()); - - // Mock deactivation and accept. - views::test::WidgetDestroyedWaiter destroyed_waiter( - GetBubbleViews()->GetWidget()); - GetBubbleViews()->GetWidget()->CloseWithReason( - views::Widget::ClosedReason::kAcceptButtonClicked); - destroyed_waiter.Wait(); - - histogram_tester.ExpectBucketCount( - "Autofill.VirtualCardEnrollBubble.Result." + - VirtualCardEnrollmentSourceToMetricSuffix( - virtual_card_enrollment_source) + - ".FirstShow", - VirtualCardEnrollmentBubbleResult:: - VIRTUAL_CARD_ENROLLMENT_BUBBLE_ACCEPTED, - 1); - } -} - -class NotInteractedAndClosedMetricsTest +class VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized : public VirtualCardEnrollBubbleViewsInteractiveUiTest, public testing::WithParamInterface<VirtualCardEnrollmentSource> { public: - NotInteractedAndClosedMetricsTest() = default; - ~NotInteractedAndClosedMetricsTest() override = default; + VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized() = default; + ~VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized() override = + default; }; INSTANTIATE_TEST_SUITE_P( , - NotInteractedAndClosedMetricsTest, + VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized, testing::Values(VirtualCardEnrollmentSource::kUpstream, VirtualCardEnrollmentSource::kDownstream, VirtualCardEnrollmentSource::kSettingsPage)); -IN_PROC_BROWSER_TEST_P(NotInteractedAndClosedMetricsTest, - NotInteractedTest_AllSources) { - VirtualCardEnrollmentSource virtual_card_enrollment_source = GetParam(); - base::HistogramTester histogram_tester; - std::string histogram_name; - ShowBubbleAndWaitUntilShown( - GetFieldsForSource(virtual_card_enrollment_source), base::DoNothing(), - base::DoNothing()); - - ASSERT_TRUE(GetBubbleViews()); - ASSERT_TRUE(IsIconVisible()); - - // Mock browser being closed. - views::test::WidgetDestroyedWaiter destroyed_waiter( - GetBubbleViews()->GetWidget()); - browser()->tab_strip_model()->CloseAllTabs(); - destroyed_waiter.Wait(); - - histogram_tester.ExpectBucketCount( - "Autofill.VirtualCardEnrollBubble.Result." + - VirtualCardEnrollmentSourceToMetricSuffix( - virtual_card_enrollment_source) + - ".FirstShow", +IN_PROC_BROWSER_TEST_P( + VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized, + Metrics_BubbleLostFocus) { + TestCloseBubbleForExpectedResultFromSource( VirtualCardEnrollmentBubbleResult:: - VIRTUAL_CARD_ENROLLMENT_BUBBLE_NOT_INTERACTED, - 1); + VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS, + GetParam()); } -IN_PROC_BROWSER_TEST_P(NotInteractedAndClosedMetricsTest, - ShownAndLostFocusTest_AllSources) { +IN_PROC_BROWSER_TEST_P( + VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized, + Metrics_BubbleAccepted) { + TestCloseBubbleForExpectedResultFromSource( + VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_ACCEPTED, + GetParam()); +} + +IN_PROC_BROWSER_TEST_P( + VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized, + Metrics_BubbleCancelled) { + TestCloseBubbleForExpectedResultFromSource( + VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_CANCELLED, + GetParam()); +} + +IN_PROC_BROWSER_TEST_P( + VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized, + Metrics_BubbleClosed) { + TestCloseBubbleForExpectedResultFromSource( + VirtualCardEnrollmentBubbleResult::VIRTUAL_CARD_ENROLLMENT_BUBBLE_CLOSED, + GetParam()); +} + +IN_PROC_BROWSER_TEST_P( + VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized, + Metrics_NotInteracted) { + TestCloseBubbleForExpectedResultFromSource( + VirtualCardEnrollmentBubbleResult:: + VIRTUAL_CARD_ENROLLMENT_BUBBLE_NOT_INTERACTED, + GetParam()); +} + +IN_PROC_BROWSER_TEST_P( + VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized, + ShownAndLostFocusTest_AllSources) { base::HistogramTester histogram_tester; VirtualCardEnrollmentSource virtual_card_enrollment_source = GetParam(); ShowBubbleAndWaitUntilShown(
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc index 5cda679..6c33509 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -128,7 +128,6 @@ #include "ui/views/controls/label.h" #include "ui/views/controls/separator.h" #include "ui/views/drag_utils.h" -#include "ui/views/image_model_utils.h" #include "ui/views/metrics.h" #include "ui/views/view_constants.h" #include "ui/views/widget/tooltip_manager.h" @@ -1488,10 +1487,9 @@ chrome::BookmarkFolderIconType::kNormal, ui::kColorMenuIcon); } - button_drag_utils::SetDragImage( - node->url(), node->GetTitle(), - views::GetImageSkiaFromImageModel(icon, GetColorProvider()), &press_pt, - data); + button_drag_utils::SetDragImage(node->url(), node->GetTitle(), + icon.Rasterize(GetColorProvider()), &press_pt, + data); WriteBookmarkDragData(node, data); }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc b/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc index 22bf2e2..2b97a31 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc
@@ -40,7 +40,6 @@ #include "ui/gfx/render_text.h" #include "ui/resources/grit/ui_resources.h" #include "ui/views/drag_utils.h" -#include "ui/views/image_model_utils.h" #include "ui/views/style/platform_style.h" #include "ui/views/style/typography.h" #include "ui/views/style/typography_provider.h" @@ -241,7 +240,7 @@ (icon.IsEmpty() || (icon.IsImageGenerator() && !color_provider)) ? *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( IDR_DEFAULT_FAVICON) - : views::GetImageSkiaFromImageModel(icon, color_provider), + : icon.Rasterize(color_provider), count_), BookmarkDragImageSource::kBookmarkDragImageSize);
diff --git a/chrome/browser/ui/views/chrome_typography.cc b/chrome/browser/ui/views/chrome_typography.cc index 3315559..2b5c02c 100644 --- a/chrome/browser/ui/views/chrome_typography.cc +++ b/chrome/browser/ui/views/chrome_typography.cc
@@ -107,6 +107,7 @@ details.size_delta = GetFontSizeDeltaIgnoringUserOrLocaleSettings(14); break; case CONTEXT_IPH_BUBBLE_BUTTON: + case CONTEXT_SIDE_PANEL_TITLE: details.size_delta = GetFontSizeDeltaIgnoringUserOrLocaleSettings(13); break; }
diff --git a/chrome/browser/ui/views/chrome_typography.h b/chrome/browser/ui/views/chrome_typography.h index 6093df4..b5036de 100644 --- a/chrome/browser/ui/views/chrome_typography.h +++ b/chrome/browser/ui/views/chrome_typography.h
@@ -64,7 +64,10 @@ CONTEXT_IPH_BUBBLE_BODY, // Button label in the IPH bubble. Usually 13pt. - CONTEXT_IPH_BUBBLE_BUTTON + CONTEXT_IPH_BUBBLE_BUTTON, + + // Title label in the browser side panel. Usually 13pt. + CONTEXT_SIDE_PANEL_TITLE, }; enum ChromeTextStyle {
diff --git a/chrome/browser/ui/views/commander_frontend_views.cc b/chrome/browser/ui/views/commander_frontend_views.cc index 074b8d5..c1fdf42 100644 --- a/chrome/browser/ui/views/commander_frontend_views.cc +++ b/chrome/browser/ui/views/commander_frontend_views.cc
@@ -156,7 +156,7 @@ views::Widget::InitParams params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); params.delegate = widget_delegate_.get(); - params.name = "Commander"; + params.name = "Quick Commands"; params.parent = parent->GetWidget()->GetNativeView(); // On Windows, this defaults to DesktopNativeWidgetAura, which has incorrect // parenting behavior for this widget. @@ -287,8 +287,8 @@ web_view->set_allow_accelerators(true); // Make the commander WebContents show up in the task manager. content::WebContents* web_contents = web_view->GetWebContents(); - task_manager::WebContentsTags::CreateForToolContents(web_contents, - IDS_COMMANDER_LABEL); + task_manager::WebContentsTags::CreateForToolContents( + web_contents, IDS_QUICK_COMMANDS_LABEL); web_view->LoadInitialURL(GURL(chrome::kChromeUICommanderURL)); return web_view; }
diff --git a/chrome/browser/ui/views/commander_frontend_views_browsertest.cc b/chrome/browser/ui/views/commander_frontend_views_browsertest.cc index 25ca754..ce979f3 100644 --- a/chrome/browser/ui/views/commander_frontend_views_browsertest.cc +++ b/chrome/browser/ui/views/commander_frontend_views_browsertest.cc
@@ -105,7 +105,7 @@ } void OnWidgetShown(views::Widget* widget) { - if (widget->GetName() == "Commander") { + if (widget->GetName() == "Quick Commands") { active_widget_ = widget; if (IsWidgetAttachedToBrowser(expected_browser_)) { expected_browser_ = nullptr;
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_controller.cc b/chrome/browser/ui/views/download/bubble/download_bubble_controller.cc deleted file mode 100644 index 09ad1ef..0000000 --- a/chrome/browser/ui/views/download/bubble/download_bubble_controller.cc +++ /dev/null
@@ -1,75 +0,0 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/views/download/bubble/download_bubble_controller.h" -#include "base/time/time.h" -#include "chrome/browser/download/download_item_model.h" -#include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h" -#include "chrome/browser/ui/views/download/bubble/download_bubble_row_view.h" -#include "components/download/public/common/download_item.h" -#include "content/public/browser/download_manager.h" - -#include "base/files/file_path.h" - -namespace { -constexpr int kShowDownloadsInBubbleForNumDays = 1; -} // namespace - -DownloadBubbleUIController::DownloadBubbleUIController( - content::DownloadManager* manager) - : download_manager_(manager) {} - -void DownloadBubbleUIController::OnManagerGoingDown( - content::DownloadManager* manager) { - if (manager == download_manager_) { - download_manager_ = nullptr; - } -} - -// TODO(bhatiarohit): Check the order of downloads here. They do not -// appear chronological sometimes. -// TODO(bhatiarohit): Include OfflineItems. -std::unique_ptr<DownloadBubbleRowListView> -DownloadBubbleUIController::GetMainView() { - auto row_list_view = std::make_unique<DownloadBubbleRowListView>(); - if (!download_manager_) - return row_list_view; - std::vector<download::DownloadItem*> download_items; - download_manager_->GetAllDownloads(&download_items); - for (download::DownloadItem* item : download_items) { - base::Time end_time = item->GetEndTime(); - if (end_time.is_null() || ((base::Time::Now() - end_time) <= - base::Days(kShowDownloadsInBubbleForNumDays))) { - row_list_view->AddChildView(std::make_unique<DownloadBubbleRowView>( - DownloadItemModel::Wrap( - item, - std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()), - row_list_view.get())); - } - } - return row_list_view; -} - -// TODO(bhatiarohit): Refine this to remove actioned-upon items, and -// items that have been displayed on the main view. -std::unique_ptr<DownloadBubbleRowListView> -DownloadBubbleUIController::GetPartialView() { - auto row_list_view = std::make_unique<DownloadBubbleRowListView>(); - if (!download_manager_) - return row_list_view; - std::vector<download::DownloadItem*> download_items; - download_manager_->GetAllDownloads(&download_items); - for (download::DownloadItem* item : download_items) { - base::Time end_time = item->GetEndTime(); - if (end_time.is_null() || ((base::Time::Now() - end_time) <= - base::Days(kShowDownloadsInBubbleForNumDays))) { - row_list_view->AddChildView(std::make_unique<DownloadBubbleRowView>( - DownloadItemModel::Wrap( - item, - std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()), - row_list_view.get())); - } - } - return row_list_view; -}
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_controller.h b/chrome/browser/ui/views/download/bubble/download_bubble_controller.h deleted file mode 100644 index e1c3710..0000000 --- a/chrome/browser/ui/views/download/bubble/download_bubble_controller.h +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_ -#define CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_ - -#include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h" -#include "components/download/content/public/all_download_item_notifier.h" -#include "content/public/browser/download_manager.h" - -class DownloadBubbleUIController - : public download::AllDownloadItemNotifier::Observer { - public: - explicit DownloadBubbleUIController(content::DownloadManager* manager); - DownloadBubbleUIController(const DownloadBubbleUIController&) = delete; - DownloadBubbleUIController& operator=(const DownloadBubbleUIController&) = - delete; - - // AllDownloadItemNotifier::Observer - void OnManagerGoingDown(content::DownloadManager* manager) override; - - // Get the main view of the Download Bubble. The main view contains all the - // recent downloads (finished within the last 24 hours). - std::unique_ptr<DownloadBubbleRowListView> GetMainView(); - - // Get the partial view of the Download Bubble. The partial view contains - // in-progress and uninteracted downloads, meant to capture the user's - // recent tasks. This can only be opened by the browser in the event of new - // downloads, and user action only creates a main view. - std::unique_ptr<DownloadBubbleRowListView> GetPartialView(); - - private: - content::DownloadManager* download_manager_; -}; - -#endif // CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc b/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc index 7afe0b10..05a6f5df 100644 --- a/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc +++ b/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc
@@ -6,6 +6,7 @@ #include "base/files/file_path.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/download/bubble/download_bubble_controller.h" #include "chrome/browser/download/download_ui_model.h" #include "chrome/browser/icon_manager.h" #include "chrome/browser/ui/layout_constants.h" @@ -100,11 +101,13 @@ DownloadBubbleRowView::DownloadBubbleRowView( DownloadUIModel::DownloadUIModelPtr model, - DownloadBubbleRowListView* row_list_view) + DownloadBubbleRowListView* row_list_view, + DownloadBubbleUIController* bubble_controller) : model_(std::move(model)), context_menu_( std::make_unique<DownloadShelfContextMenuView>(model_->GetWeakPtr())), - row_list_view_(row_list_view) { + row_list_view_(row_list_view), + bubble_controller_(bubble_controller) { model_->AddObserver(this); set_context_menu_controller(this); @@ -169,6 +172,7 @@ } void DownloadBubbleRowView::OnCancelButtonPressed() { + bubble_controller_->RemoveContentIdFromPartialView(model_->GetContentId()); model_->Cancel(/*user_cancel=*/true); } @@ -189,10 +193,15 @@ // PreferredSizeChanged(); } -// TODO(bhatiarohit): Use these methods to update main and partial view. -void DownloadBubbleRowView::OnDownloadOpened() {} +void DownloadBubbleRowView::OnDownloadOpened() { + bubble_controller_->RemoveContentIdFromPartialView(model_->GetContentId()); +} -void DownloadBubbleRowView::OnDownloadDestroyed() {} +void DownloadBubbleRowView::OnDownloadDestroyed() { + // This will return ownership and destroy this object at the end of the + // method. + auto row_view_ptr = row_list_view_->RemoveChildViewT(this); +} void DownloadBubbleRowView::ShowContextMenuForViewImpl( View* source,
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_row_view.h b/chrome/browser/ui/views/download/bubble/download_bubble_row_view.h index 522b35db..9575c44 100644 --- a/chrome/browser/ui/views/download/bubble/download_bubble_row_view.h +++ b/chrome/browser/ui/views/download/bubble/download_bubble_row_view.h
@@ -21,6 +21,7 @@ } // namespace views class DownloadShelfContextMenuView; +class DownloadBubbleUIController; class DownloadBubbleRowView : public views::View, public views::ContextMenuController, @@ -29,7 +30,8 @@ METADATA_HEADER(DownloadBubbleRowView); explicit DownloadBubbleRowView(DownloadUIModel::DownloadUIModelPtr model, - DownloadBubbleRowListView* row_list_view); + DownloadBubbleRowListView* row_list_view, + DownloadBubbleUIController* bubble_controller); DownloadBubbleRowView(const DownloadBubbleRowView&) = delete; DownloadBubbleRowView& operator=(const DownloadBubbleRowView&) = delete; ~DownloadBubbleRowView() override; @@ -37,8 +39,8 @@ void AddedToWidget() override; // Overrides DownloadUIModel::Observer: - void OnDownloadUpdated() override; void OnDownloadOpened() override; + void OnDownloadUpdated() override; void OnDownloadDestroyed() override; // Overrides views::ContextMenuController: @@ -92,6 +94,9 @@ // Parent row list view. raw_ptr<DownloadBubbleRowListView> row_list_view_ = nullptr; + // Controller for keeping track of downloads. + raw_ptr<DownloadBubbleUIController> bubble_controller_ = nullptr; + base::WeakPtrFactory<DownloadBubbleRowView> weak_factory_{this}; };
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc index fca95bb..ac625f6 100644 --- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc +++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "chrome/app/vector_icons/vector_icons.h" +#include "chrome/browser/download/bubble/download_bubble_controller.h" #include "chrome/browser/download/bubble/download_display_controller.h" #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/ui/browser.h" @@ -13,7 +14,8 @@ #include "chrome/browser/ui/view_ids.h" #include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" -#include "chrome/browser/ui/views/download/bubble/download_bubble_controller.h" +#include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h" +#include "chrome/browser/ui/views/download/bubble/download_bubble_row_view.h" #include "chrome/browser/ui/views/download/bubble/download_dialog_view.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/grit/generated_resources.h" @@ -32,6 +34,17 @@ constexpr int kProgressRingRadius = 9; constexpr float kProgressRingStrokeWidth = 1.7f; +std::unique_ptr<DownloadBubbleRowListView> CreateRowListView( + std::vector<DownloadUIModelPtr> model_list, + DownloadBubbleUIController* bubble_controller) { + auto row_list_view = std::make_unique<DownloadBubbleRowListView>(); + for (DownloadUIModelPtr& model : model_list) { + row_list_view->AddChildView(std::make_unique<DownloadBubbleRowView>( + std::move(model), row_list_view.get(), bubble_controller)); + } + return row_list_view; +} + } // namespace DownloadToolbarButtonView::DownloadToolbarButtonView(BrowserView* browser_view) @@ -45,13 +58,13 @@ GetViewAccessibility().OverrideHasPopup(ax::mojom::HasPopup::kDialog); SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_DOWNLOAD_ICON)); Profile* profile = browser_->profile(); - content::DownloadManager* manager = profile->GetDownloadManager(); SetVisible(false); - bubble_controller_ = std::make_unique<DownloadBubbleUIController>(manager); + bubble_controller_ = std::make_unique<DownloadBubbleUIController>(profile); // Wait until we're done with everything else before creating `controller_` // since it can call `Show()` synchronously. - controller_ = std::make_unique<DownloadDisplayController>(this, manager); + controller_ = std::make_unique<DownloadDisplayController>( + this, profile, bubble_controller_.get()); } DownloadToolbarButtonView::~DownloadToolbarButtonView() { @@ -112,7 +125,8 @@ void DownloadToolbarButtonView::ShowDetails() { if (!bubble_delegate_) { std::unique_ptr<views::BubbleDialogDelegate> bubble_delegate = - CreateBubbleDialogDelegate(bubble_controller_->GetPartialView()); + CreateBubbleDialogDelegate(CreateRowListView( + bubble_controller_->GetPartialView(), bubble_controller_.get())); bubble_delegate_ = bubble_delegate.get(); views::BubbleDialogDelegate::CreateBubble(std::move(bubble_delegate)); bubble_delegate_->GetWidget()->Show(); @@ -183,7 +197,8 @@ if (!bubble_delegate_) { std::unique_ptr<views::BubbleDialogDelegate> bubble_delegate = CreateBubbleDialogDelegate(std::make_unique<DownloadDialogView>( - browser_, bubble_controller_->GetMainView())); + browser_, CreateRowListView(bubble_controller_->GetMainView(), + bubble_controller_.get()))); bubble_delegate_ = bubble_delegate.get(); views::BubbleDialogDelegate::CreateBubble(std::move(bubble_delegate)); bubble_delegate_->GetWidget()->Show();
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h index 7647968..5bbd238 100644 --- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h +++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
@@ -8,7 +8,6 @@ #include "base/memory/raw_ptr.h" #include "chrome/browser/download/bubble/download_display.h" #include "chrome/browser/download/bubble/download_icon_state.h" -#include "chrome/browser/ui/views/download/bubble/download_bubble_controller.h" #include "chrome/browser/ui/views/toolbar/toolbar_button.h" #include "ui/base/metadata/metadata_header_macros.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" @@ -16,6 +15,7 @@ class Browser; class BrowserView; class DownloadDisplayController; +class DownloadBubbleUIController; // Download icon shown in the trusted area of the toolbar. Its lifetime is tied // to that of its parent ToolbarView. The icon is made visible when downloads @@ -51,11 +51,13 @@ void OnBubbleDelegateDeleted(); raw_ptr<Browser> browser_; - // Controller for the DownloadToolbarButton. + // Controller for the DownloadToolbarButton UI. std::unique_ptr<DownloadDisplayController> controller_; - // Controller for the DownloadBubbleUI, both main view and partial view. + // Controller for keeping track of items for both main view and partial view. std::unique_ptr<DownloadBubbleUIController> bubble_controller_; raw_ptr<views::BubbleDialogDelegate> bubble_delegate_ = nullptr; + + base::WeakPtrFactory<DownloadToolbarButtonView> weak_factory_{this}; }; #endif // CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_TOOLBAR_BUTTON_VIEW_H_
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc index 0144f27d..118f311 100644 --- a/chrome/browser/ui/views/download/download_item_view.cc +++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -66,6 +66,7 @@ #include "ui/base/metadata/metadata_header_macros.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/base/text/bytes_formatting.h" +#include "ui/base/themed_vector_icon.h" #include "ui/base/ui_base_types.h" #include "ui/color/color_id.h" #include "ui/compositor/layer.h" @@ -84,7 +85,6 @@ #include "ui/gfx/range/range.h" #include "ui/gfx/text_constants.h" #include "ui/gfx/text_elider.h" -#include "ui/native_theme/themed_vector_icon.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/animation/ink_drop.h" #include "ui/views/animation/ink_drop_host_view.h"
diff --git a/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc b/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc index e0b4d05..c7c9131 100644 --- a/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc +++ b/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc
@@ -37,7 +37,6 @@ #include "ui/views/bubble/bubble_dialog_delegate_view.h" #include "ui/views/controls/label.h" #include "ui/views/controls/link.h" -#include "ui/views/image_model_utils.h" #include "ui/views/layout/box_layout.h" #if BUILDFLAG(ENABLE_DICE_SUPPORT) @@ -222,7 +221,7 @@ // Indent by the size of the icon. layout->set_inside_border_insets(gfx::Insets( 0, - views::GetImageSkiaFromImageModel(GetWindowIcon(), nullptr).width() + + GetWindowIcon().Size().width() + provider->GetDistanceMetric(DISTANCE_UNRELATED_CONTROL_HORIZONTAL), 0, 0)); layout->set_cross_axis_alignment(
diff --git a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc index 3bad726..e87118a 100644 --- a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc +++ b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc
@@ -30,6 +30,7 @@ #include "chrome/browser/ui/views/hover_button.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/theme_resources.h" +#include "components/url_formatter/url_formatter.h" #include "components/vector_icons/vector_icons.h" #include "content/public/browser/web_contents.h" #include "extensions/common/extension_urls.h" @@ -184,22 +185,22 @@ parent_view->ReorderChildView(site_access_view, new_index); } -// Returns the active webcontent's host. This method should only be called when +// Returns the web content's host. This method should only be called when // web contents are present. -std::u16string GetCurrentSite(Browser* browser) { - auto* web_contents = browser->tab_strip_model()->GetActiveWebContents(); +std::u16string GetCurrentHost(content::WebContents* web_contents) { DCHECK(web_contents); - return base::UTF8ToUTF16(web_contents->GetLastCommittedURL().host()); + return url_formatter::IDNToUnicode( + url_formatter::StripWWW(web_contents->GetLastCommittedURL().host())); } -// Sets the `label` text to `message_id` with `current_site` emphasized. +// Sets the `label` text to `message_id` with `current_host` emphasized. void SetLabelTextAndStyle(views::Label& label, int message_id, - std::u16string current_site) { + std::u16string current_host) { size_t offset = 0u; - label.SetText(l10n_util::GetStringFUTF16(message_id, current_site, &offset)); + label.SetText(l10n_util::GetStringFUTF16(message_id, current_host, &offset)); label.SetTextStyleRange(ChromeTextStyle::STYLE_EMPHASIZED, - gfx::Range(offset, offset + current_site.length())); + gfx::Range(offset, offset + current_host.length())); } } // namespace @@ -481,7 +482,11 @@ } void ExtensionsTabbedMenuView::CreateSiteAccessTab() { - auto current_site = GetCurrentSite(browser_); + auto* web_contents = browser_->tab_strip_model()->GetActiveWebContents(); + if (!web_contents) + return; + + auto current_host = GetCurrentHost(web_contents); const int horizontal_spacing = ChromeLayoutProvider::Get()->GetDistanceMetric( views::DISTANCE_BUTTON_HORIZONTAL_PADDING); const int vertical_spacing = ChromeLayoutProvider::Get()->GetDistanceMetric( @@ -539,10 +544,10 @@ .Build(); const auto create_radio_button_builder = - [this, current_site](UserSiteSetting site_settings, int label_id) { + [this, current_host](UserSiteSetting site_settings, int label_id) { auto label = ((site_settings == UserSiteSetting::kGrantAllExtensions) || (site_settings == UserSiteSetting::kBlockAllExtensions)) - ? l10n_util::GetStringFUTF16(label_id, current_site) + ? l10n_util::GetStringFUTF16(label_id, current_host) : l10n_util::GetStringUTF16(label_id); return views::Builder<views::RadioButton>( std::make_unique<views::RadioButton>(label, kGroupId)) @@ -699,18 +704,19 @@ // when there are no active web contents (e.g tab strip update is closing its // tabs). // TODO(emiliapaz): Consider adding a message instead of hiding the views. - if (!browser_->tab_strip_model()->GetActiveWebContents()) { + auto* web_contents = browser_->tab_strip_model()->GetActiveWebContents(); + if (!web_contents) { has_access_.container->SetVisible(false); requests_access_.container->SetVisible(false); site_access_message_->SetVisible(false); return; } - auto current_site = GetCurrentSite(browser_); + auto current_host = GetCurrentHost(web_contents); - auto update_section = [current_site](SiteAccessSection* section) { + auto update_section = [current_host](SiteAccessSection* section) { SetLabelTextAndStyle(*section->header, section->header_string_id, - current_site); + current_host); bool should_be_visible = !section->items->children().empty(); if (section->container->GetVisible() != should_be_visible) section->container->SetVisible(should_be_visible); @@ -726,7 +732,7 @@ SetLabelTextAndStyle( *site_access_message_, IDS_EXTENSIONS_MENU_SITE_ACCESS_TAB_NO_EXTENSIONS_HAVE_ACCESS_TEXT, - current_site); + current_host); } else { site_access_message_->SetVisible(false); }
diff --git a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc index 7309977..0cb4cff1 100644 --- a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc +++ b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc
@@ -19,6 +19,7 @@ #include "chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h" #include "chrome/grit/generated_resources.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/url_formatter/url_formatter.h" #include "content/public/browser/notification_service.h" #include "content/public/test/test_utils.h" #include "extensions/browser/notification_types.h" @@ -701,7 +702,7 @@ auto no_extensions_have_access_text = l10n_util::GetStringFUTF16( IDS_EXTENSIONS_MENU_SITE_ACCESS_TAB_NO_EXTENSIONS_HAVE_ACCESS_TEXT, - base::UTF8ToUTF16(url.host())); + url_formatter::IDNToUnicode(url_formatter::StripWWW(url.host()))); // Verify only the correct message is displayed when no extensions have access // to the current site.
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 09d827f..7c4292631 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -33,6 +33,7 @@ #include "chrome/browser/app_mode/app_mode_utils.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/download/bubble/download_bubble_prefs.h" #include "chrome/browser/extensions/browser_extension_window_controller.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/feature_engagement/tracker_factory.h" @@ -795,10 +796,7 @@ } #if BUILDFLAG(GOOGLE_CHROME_BRANDING) - if ((base::FeatureList::IsEnabled(lens::features::kLensStandalone) && - lens::features::kEnableSidePanelForLensImageSearch.Get()) || - (base::FeatureList::IsEnabled(lens::features::kLensRegionSearch) && - lens::features::kEnableSidePanelForLensRegionSearch.Get())) { + if (lens::features::IsLensSidePanelEnabled()) { lens_side_panel_ = AddChildView(std::make_unique<SidePanel>(this)); // If the separator was not already created, create one. if (!right_aligned_side_panel_separator_) @@ -2347,6 +2345,13 @@ } DownloadShelf* BrowserView::GetDownloadShelf() { + // Don't show download shelf if download bubble is enabled, except that the + // shelf is already showing (this can happen if prefs were changed at + // runtime). + if (download::IsDownloadBubbleEnabled(browser_->profile()) && + !download_shelf_) { + return nullptr; + } if (!download_shelf_) { if (base::FeatureList::IsEnabled(features::kWebUIDownloadShelf)) { download_shelf_ = AddChildView(
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc index 8bb97b2..8eb74dd 100644 --- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc +++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -45,7 +45,6 @@ #include "ui/gfx/image/image.h" #include "ui/gfx/scoped_canvas.h" #include "ui/strings/grit/ui_strings.h" -#include "ui/views/image_model_utils.h" #include "ui/views/win/hwnd_util.h" #include "ui/views/window/client_view.h" @@ -795,8 +794,8 @@ HICON small_icon = nullptr; HICON big_icon = nullptr; - gfx::ImageSkia icon = views::GetImageSkiaFromImageModel( - browser_view()->GetWindowIcon(), GetColorProvider()); + gfx::ImageSkia icon = + browser_view()->GetWindowIcon().Rasterize(GetColorProvider()); if (!icon.isNull()) { // Keep previous icons alive as long as they are referenced by the HWND.
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc index 72c3695..5384b82d 100644 --- a/chrome/browser/ui/views/intent_picker_bubble_view.cc +++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -20,11 +20,11 @@ #include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/chrome_typography.h" -#include "chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h" +#include "chrome/browser/ui/views/hover_button.h" #include "chrome/browser/ui/views/toolbar/toolbar_view.h" #include "chrome/common/chrome_features.h" #include "chrome/grit/generated_resources.h" -#include "components/services/app_service/public/cpp/intent_constants.h" +#include "components/services/app_service/public/cpp/intent_util.h" #include "components/url_formatter/elide_url.h" #include "content/public/browser/navigation_handle.h" #include "third_party/skia/include/core/SkColor.h" @@ -82,11 +82,8 @@ provider->GetDistanceMetric(DISTANCE_CONTENT_LIST_VERTICAL_MULTI), provider->GetInsetsMetric(views::INSETS_DIALOG).left()))); views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON); - views::InkDrop::Get(this)->SetVisibleOpacity(kToolbarInkDropVisibleOpacity); - views::InkDrop::Get(this)->SetHighlightOpacity( - kToolbarInkDropHighlightVisibleOpacity); views::InkDrop::Get(this)->SetBaseColorCallback( - base::BindRepeating(&GetToolbarInkDropBaseColor, this)); + base::BindRepeating(&HoverButton::GetInkDropColor, this)); } IntentPickerLabelButton(const IntentPickerLabelButton&) = delete; IntentPickerLabelButton& operator=(const IntentPickerLabelButton&) = delete; @@ -203,7 +200,7 @@ } void IntentPickerBubbleView::OnDialogCancelled() { - const char* launch_name = apps::kUseBrowserForLink; + const char* launch_name = apps_util::kUseBrowserForLink; bool should_persist = remember_selection_checkbox_ && remember_selection_checkbox_->GetChecked(); RunCallbackAndCloseBubble(launch_name, apps::PickerEntryType::kUnknown,
diff --git a/chrome/browser/ui/views/lens/lens_region_search_controller_unittest.cc b/chrome/browser/ui/views/lens/lens_region_search_controller_unittest.cc index 61d3b27d..6591632 100644 --- a/chrome/browser/ui/views/lens/lens_region_search_controller_unittest.cc +++ b/chrome/browser/ui/views/lens/lens_region_search_controller_unittest.cc
@@ -19,7 +19,7 @@ public: void SetUp() override { base::test::ScopedFeatureList features; - features.InitWithFeatures({features::kLensRegionSearch}, {}); + features.InitWithFeatures({features::kLensStandalone}, {}); TestWithBrowserView::SetUp(); // Create an active web contents.
diff --git a/chrome/browser/ui/views/lens/lens_side_panel_controller_unittest.cc b/chrome/browser/ui/views/lens/lens_side_panel_controller_unittest.cc index d46e5f8..3792122f 100644 --- a/chrome/browser/ui/views/lens/lens_side_panel_controller_unittest.cc +++ b/chrome/browser/ui/views/lens/lens_side_panel_controller_unittest.cc
@@ -30,8 +30,7 @@ void SetUp() override { base::test::ScopedFeatureList features; features.InitWithFeaturesAndParameters( - {{features::kLensRegionSearch, - {{"region-search-enable-side-panel", "true"}}}, + {{features::kLensStandalone, {{"enable-side-panel", "true"}}}, {::features::kSidePanel, {}}, {reading_list::switches::kReadLater, {}}}, {}); @@ -116,15 +115,27 @@ content::OpenURLParams(GURL("http://foo.com"), content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, false)); + EXPECT_TRUE(browser_view()->lens_side_panel_controller()); + EXPECT_TRUE(browser_view()->lens_side_panel()->GetVisible()); + + // Closing the controller should hide side panel and delete controller + // pointer. controller_->Close(); - // Verify pointer was reset. EXPECT_FALSE(browser_view()->lens_side_panel_controller()); - // Lens side panel controller needs to be recreated before reopen. + EXPECT_FALSE(browser_view()->lens_side_panel()->GetVisible()); + + // Creating a new controller in browser view should fix pointer, but side + // panel is still not visible. browser_view()->CreateLensSidePanelController(); + controller_ = browser_view()->lens_side_panel_controller(); + EXPECT_TRUE(browser_view()->lens_side_panel_controller()); + EXPECT_FALSE(browser_view()->lens_side_panel()->GetVisible()); + controller_->OpenWithURL( content::OpenURLParams(GURL("http://bar.com"), content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, false)); + EXPECT_TRUE(browser_view()->lens_side_panel_controller()); EXPECT_TRUE(browser_view()->lens_side_panel()->GetVisible()); controller_->Close(); @@ -172,6 +183,7 @@ // Creating a new controller in browser view should fix pointer, but side // panel is still not visible. browser_view()->CreateLensSidePanelController(); + controller_ = browser_view()->lens_side_panel_controller(); EXPECT_TRUE(browser_view()->lens_side_panel_controller()); EXPECT_FALSE(browser_view()->lens_side_panel()->GetVisible());
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc index 168edc31..0d45628 100644 --- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc +++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
@@ -15,6 +15,7 @@ #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/omnibox/omnibox_theme.h" #include "chrome/browser/ui/page_info/page_info_dialog.h" @@ -88,13 +89,13 @@ security_state::SecurityLevel security_level) { switch (security_level) { case security_state::SECURE_WITH_POLICY_INSTALLED_CERT: - return ui::kColorPwaSecurityChipForegroundPolicyCert; + return kColorPwaSecurityChipForegroundPolicyCert; case security_state::SECURE: - return ui::kColorPwaSecurityChipForegroundSecure; + return kColorPwaSecurityChipForegroundSecure; case security_state::DANGEROUS: - return ui::kColorPwaSecurityChipForegroundDangerous; + return kColorPwaSecurityChipForegroundDangerous; default: - return ui::kColorPwaSecurityChipForeground; + return kColorPwaSecurityChipForeground; } } @@ -326,24 +327,22 @@ void CustomTabBarView::OnThemeChanged() { views::AccessiblePaneView::OnThemeChanged(); - absl::optional<SkColor> optional_theme_color = GetThemeColor(); - - title_bar_color_ = optional_theme_color.value_or(GetDefaultFrameColor()); - const auto* color_provider = GetColorProvider(); + + title_bar_color_ = color_provider->GetColor(kColorPwaTheme); const SkColor foreground_color = - color_provider->GetColor(ui::kColorPwaToolbarForeground); + color_provider->GetColor(kColorPwaToolbarForeground); SetImageFromVectorIconWithColor( close_button_, vector_icons::kCloseRoundedIcon, GetLayoutConstant(LOCATION_BAR_ICON_SIZE), foreground_color); - background_color_ = color_provider->GetColor(ui::kColorPwaToolbarBackground); + background_color_ = color_provider->GetColor(kColorPwaToolbarBackground); SetBackground(views::CreateSolidBackground(background_color_)); title_origin_view_->SetColors(background_color_); if (web_app_menu_button_) { - web_app_menu_button_->SetColor(GetThemeProvider()->GetColor( - ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON)); + web_app_menu_button_->SetColor( + color_provider->GetColor(kColorPwaMenuButtonIcon)); } } @@ -403,7 +402,7 @@ } SkColor CustomTabBarView::GetIconLabelBubbleBackgroundColor() const { - return GetColorProvider()->GetColor(ui::kColorPwaToolbarBackground); + return GetColorProvider()->GetColor(kColorPwaToolbarBackground); } content::WebContents* CustomTabBarView::GetWebContents() { @@ -451,20 +450,6 @@ return title_origin_view_ && title_origin_view_->IsShowingOriginForTesting(); } -// TODO(tluk): Remove the use of GetDefaultFrameColor() completely here. When -// drawing the separator the current frame color should be queried directly and -// not assume knowledge of what the color might be. -SkColor CustomTabBarView::GetDefaultFrameColor() const { -#if BUILDFLAG(IS_CHROMEOS_ASH) - // Ash system frames differ from ChromeOS browser frames. - return chromeos::kDefaultFrameColor; -#else - return ThemeProperties::GetDefaultColor( - ThemeProperties::COLOR_FRAME_ACTIVE, false, - GetNativeTheme()->ShouldUseDarkColors()); -#endif -} - void CustomTabBarView::GoBackToApp() { content::WebContents* web_contents = GetWebContents(); content::NavigationController& controller = web_contents->GetController(); @@ -536,20 +521,10 @@ views::MenuAnchorPosition::kTopLeft, source_type); } -absl::optional<SkColor> CustomTabBarView::GetThemeColor() const { - web_app::AppBrowserController* application_controller = app_controller(); - return application_controller ? application_controller->GetThemeColor() - : absl::nullopt; -} - bool CustomTabBarView::GetShowTitle() const { return app_controller() != nullptr; } BEGIN_METADATA(CustomTabBarView, views::AccessiblePaneView) -ADD_READONLY_PROPERTY_METADATA(SkColor, - DefaultFrameColor, - ui::metadata::SkColorConverter) -ADD_READONLY_PROPERTY_METADATA(absl::optional<SkColor>, ThemeColor) ADD_READONLY_PROPERTY_METADATA(bool, ShowTitle) END_METADATA
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h index 359e5226..18d4fab 100644 --- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h +++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h
@@ -92,9 +92,6 @@ bool IsShowingOriginForTesting() const; private: - // Calculate the view's frame color from the current theme provider. - SkColor GetDefaultFrameColor() const; - // Takes the web contents for the custom tab bar back to the app scope. void GoBackToApp(); @@ -116,9 +113,6 @@ return browser_->app_controller(); } - // Convenience method to return the theme color from |app_controller_|. - absl::optional<SkColor> GetThemeColor() const; - // Populates child elements with page details from the current WebContents. void UpdateContents();
diff --git a/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc b/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc index 86d7952..5128993 100644 --- a/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc +++ b/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc
@@ -4,6 +4,7 @@ #include "base/test/scoped_feature_list.h" #include "build/chromeos_buildflags.h" +#include "chrome/browser/apps/intent_helper/intent_picker_features.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/views/frame/browser_view.h" @@ -13,7 +14,6 @@ #include "chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.h" #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" #include "chrome/browser/web_applications/web_app_install_info.h" -#include "chrome/common/chrome_features.h" #include "content/public/test/browser_test.h" #include "ui/events/event_utils.h" #include "ui/views/test/button_test_api.h" @@ -28,7 +28,10 @@ : public web_app::WebAppNavigationBrowserTest { public: IntentChipButtonBrowserTest() { - scoped_feature_list_.InitAndEnableFeature(features::kLinkCapturingUiUpdate); + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/{apps::features::kLinkCapturingUiUpdate, + apps::features::kIntentChipSkipsPicker}, + /*disabled_features=*/{}); } void OpenNewTab(const GURL& url) {
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc index b966ff4..38d81a9 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view.cc +++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -20,6 +20,7 @@ #include "chrome/app/chrome_command_ids.h" #include "chrome/app/vector_icons/vector_icons.h" #include "chrome/browser/accuracy_tips/accuracy_service_factory.h" +#include "chrome/browser/apps/intent_helper/intent_picker_features.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part.h" #include "chrome/browser/command_updater.h" @@ -279,7 +280,7 @@ selected_keyword_view_ = AddChildView(std::make_unique<SelectedKeywordView>( this, TemplateURLServiceFactory::GetForProfile(profile_), font_list)); - if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) { + if (apps::features::LinkCapturingUiUpdateEnabled()) { intent_chip_ = AddChildView(std::make_unique<IntentChipButton>(browser_, this)); } @@ -313,7 +314,7 @@ autofill::features::kAutofillEnableToolbarStatusChip)) { params.types_enabled.push_back(PageActionIconType::kManagePasswords); } - if (!base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) + if (!apps::features::LinkCapturingUiUpdateEnabled()) params.types_enabled.push_back(PageActionIconType::kIntentPicker); params.types_enabled.push_back(PageActionIconType::kPwaInstall); #if BUILDFLAG(ENABLE_SIDE_SEARCH)
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc index cc311e0..a8470a6 100644 --- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc +++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
@@ -165,7 +165,26 @@ service->OnStartPresentationContextCreated(std::move(context)); MediaToolbarButtonView* const media_button = GetMediaButton(); - if (!media_button) { + // If there exists a media button, anchor the dialog to this media button. + if (media_button) { + scoped_widget_observations_.AddObservation(MediaDialogView::ShowDialog( + media_button, views::BubbleBorder::TOP_RIGHT, service, profile, + initiator(), + global_media_controls::GlobalMediaControlsEntryPoint::kPresentation)); + return; + } + Browser* const browser = chrome::FindBrowserWithWebContents(initiator()); + BrowserView* const browser_view = + browser ? BrowserView::GetBrowserViewForBrowser(browser) : nullptr; + // If there exists a browser_view, anchor the dialog to the top center of the + // browser_view. This is necessary only for Mac, but works for other + // platforms. + if (browser_view) { + scoped_widget_observations_.AddObservation(MediaDialogView::ShowDialog( + browser_view->top_container(), views::BubbleBorder::TOP_CENTER, service, + profile, initiator(), + global_media_controls::GlobalMediaControlsEntryPoint::kPresentation)); + } else { // Show the GMC dialog anchored to the top of the web contents. gfx::Rect anchor_bounds = initiator()->GetContainerBounds(); anchor_bounds.set_height(0); @@ -174,13 +193,7 @@ anchor_bounds, service, profile, initiator(), global_media_controls::GlobalMediaControlsEntryPoint:: kPresentation)); - return; } - - scoped_widget_observations_.AddObservation(MediaDialogView::ShowDialog( - media_button, views::BubbleBorder::TOP_RIGHT, service, profile, - initiator(), - global_media_controls::GlobalMediaControlsEntryPoint::kPresentation)); } MediaToolbarButtonView* MediaRouterDialogControllerViews::GetMediaButton() {
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc index 2f0218a..05b45e4 100644 --- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc +++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc
@@ -8,6 +8,8 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/frame/top_container_view.h" #include "chrome/browser/ui/views/media_router/cast_dialog_view.h" #include "chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h" #include "chrome/test/base/in_process_browser_test.h" @@ -135,10 +137,9 @@ auto* view = MediaDialogView::GetDialogViewForTesting(); // If there does not exist a media button, the dialog should not have an // anchor view. - EXPECT_FALSE(view->GetAnchorView()); - gfx::Rect anchor_bounds = initiator_->GetContainerBounds(); - anchor_bounds.set_height(0); - EXPECT_EQ(anchor_bounds, view->GetAnchorRect()); + views::View* anchor_view = + BrowserView::GetBrowserViewForBrowser(browser())->top_container(); + EXPECT_EQ(anchor_view, view->GetAnchorView()); } IN_PROC_BROWSER_TEST_F(GlobalMediaControlsDialogTest,
diff --git a/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc b/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc index 86c7a39..5d024d3 100644 --- a/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc
@@ -5,7 +5,7 @@ #include "base/memory/raw_ptr.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/views/menu_test_base.h" -#include "ui/native_theme/themed_vector_icon.h" +#include "ui/base/themed_vector_icon.h" #include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/submenu_view.h"
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc index 0fe5235..25d4974e 100644 --- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc +++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -35,6 +35,7 @@ #include "ui/base/metadata/metadata_header_macros.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/base/models/image_model.h" +#include "ui/base/themed_vector_icon.h" #include "ui/color/color_id.h" #include "ui/color/color_provider.h" #include "ui/display/display.h" @@ -43,7 +44,6 @@ #include "ui/gfx/image/canvas_image_source.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/vector_icon_types.h" -#include "ui/native_theme/themed_vector_icon.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/animation/ink_drop.h" @@ -54,7 +54,6 @@ #include "ui/views/controls/link.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/separator.h" -#include "ui/views/image_model_utils.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/layout/flex_layout.h" #include "ui/views/layout/flex_layout_types.h" @@ -301,9 +300,9 @@ ImageView::OnThemeChanged(); constexpr int kBadgePadding = 1; DCHECK(!avatar_image_.IsEmpty()); - gfx::ImageSkia sized_avatar_image = views::GetImageSkiaFromImageModel( - SizeImageModel(avatar_image_, ProfileMenuViewBase::kIdentityImageSize), - GetColorProvider()); + gfx::ImageSkia sized_avatar_image = + SizeImageModel(avatar_image_, ProfileMenuViewBase::kIdentityImageSize) + .Rasterize(GetColorProvider()); sized_avatar_image = AddCircularBackground( sized_avatar_image, GetBackgroundColor(), kIdentityImageSizeInclBorder); gfx::ImageSkia sized_badge = AddCircularBackground(
diff --git a/chrome/browser/ui/views/side_search/default_search_icon_source.cc b/chrome/browser/ui/views/side_search/default_search_icon_source.cc index 09fe943e6..34e1099 100644 --- a/chrome/browser/ui/views/side_search/default_search_icon_source.cc +++ b/chrome/browser/ui/views/side_search/default_search_icon_source.cc
@@ -55,18 +55,9 @@ } ui::ImageModel DefaultSearchIconSource::GetSizedIconImage(int size) const { - content::WebContents* active_contents = - browser_->tab_strip_model()->GetActiveWebContents(); - if (!active_contents) - return ui::ImageModel(); - - // Attempt to synchronously get the current default search engine's favicon. - auto* omnibox_view = search::GetOmniboxView(active_contents); - DCHECK(omnibox_view); - gfx::Image icon = - omnibox_view->model()->client()->GetFaviconForDefaultSearchProvider( - base::BindRepeating(&DefaultSearchIconSource::OnIconFetched, - weak_ptr_factory_.GetWeakPtr())); + // If `icon` is empty we may have missed in the cache. Early return and notify + // clients when the icon is ready. + gfx::Image icon = GetRawIconImage(); if (icon.IsEmpty()) return ui::ImageModel(); @@ -85,6 +76,24 @@ padding_border)); } +ui::ImageModel DefaultSearchIconSource::GetIconImage() const { + return ui::ImageModel::FromImage(GetRawIconImage()); +} + +gfx::Image DefaultSearchIconSource::GetRawIconImage() const { + content::WebContents* active_contents = + browser_->tab_strip_model()->GetActiveWebContents(); + if (!active_contents) + return gfx::Image(); + + // Attempt to synchronously get the current default search engine's favicon. + auto* omnibox_view = search::GetOmniboxView(active_contents); + DCHECK(omnibox_view); + return omnibox_view->model()->client()->GetFaviconForDefaultSearchProvider( + base::BindRepeating(&DefaultSearchIconSource::OnIconFetched, + weak_ptr_factory_.GetWeakPtr())); +} + void DefaultSearchIconSource::OnIconFetched(const gfx::Image& icon) { // The favicon requested in the call to GetFaviconForDefaultSearchProvider() // will now have been cached by ChromeOmniboxClient's FaviconCache and
diff --git a/chrome/browser/ui/views/side_search/default_search_icon_source.h b/chrome/browser/ui/views/side_search/default_search_icon_source.h index bdf544f2..d0ef6760 100644 --- a/chrome/browser/ui/views/side_search/default_search_icon_source.h +++ b/chrome/browser/ui/views/side_search/default_search_icon_source.h
@@ -36,16 +36,25 @@ void OnTemplateURLServiceChanged() override; void OnTemplateURLServiceShuttingDown() override; - // Gets the icon image for the current default search provider. Will return an - // empty image model if this misses in the icon cache and will notify the - // `icon_changed_subscription_` when the icon is ready. + // Gets the icon image for the current default search provider with padding + // added to bring the resulting ImageModel up to `size`. ui::ImageModel GetSizedIconImage(int size) const; + // Similar to `GetSizedIconImage()` except this does not add padding. + ui::ImageModel GetIconImage() const; + private: // Callback used for when `GetSizedIconImage()` does not return the icon image // immediately but instead fetches the image asynchronously. void OnIconFetched(const gfx::Image& icon); + // Gets the raw gfx::Image icon from the TemplateURL for the current default + // search provider. Will return an empty image if this misses in the icon + // cache and instead will notify the `icon_changed_subscription_` when the + // icon is ready. FaviconCache guarantees favicons will be of size + // gfx::kFaviconSize (16x16) + gfx::Image GetRawIconImage() const; + // Used to fetch the ChromeOmniboxClient associated with the browser's active // tab. raw_ptr<Browser> browser_;
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller.cc b/chrome/browser/ui/views/side_search/side_search_browser_controller.cc index 873763b62..17542d3 100644 --- a/chrome/browser/ui/views/side_search/side_search_browser_controller.cc +++ b/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
@@ -18,23 +18,30 @@ #include "chrome/browser/ui/user_education/feature_promo_controller.h" #include "chrome/browser/ui/view_ids.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" +#include "chrome/browser/ui/views/chrome_typography.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/side_panel/side_panel.h" +#include "chrome/browser/ui/views/side_search/default_search_icon_source.h" #include "chrome/browser/ui/views/toolbar/toolbar_button.h" #include "chrome/grit/generated_resources.h" #include "components/feature_engagement/public/event_constants.h" #include "components/feature_engagement/public/feature_constants.h" #include "components/feature_engagement/public/tracker.h" +#include "components/url_formatter/elide_url.h" #include "components/vector_icons/vector_icons.h" #include "content/public/browser/navigation_handle.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/metadata/metadata_header_macros.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/base/pointer/touch_ui_controller.h" +#include "ui/color/color_id.h" +#include "ui/gfx/image/canvas_image_source.h" +#include "ui/views/animation/ink_drop.h" #include "ui/views/background.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/image_button_factory.h" #include "ui/views/controls/highlight_path_generator.h" +#include "ui/views/controls/label.h" #include "ui/views/controls/separator.h" #include "ui/views/controls/webview/webview.h" #include "ui/views/layout/box_layout.h" @@ -46,23 +53,8 @@ namespace { -constexpr int kSidePanelWidth = 380; constexpr int kDefaultTouchableIconSize = 24; -// Below are hardcoded color constants for the side panel. This is a UX decision -// to ensure that the colors align with the tier 2 Google SRP which only -// supports light mode. These are not intended to change to match the light/dark -// system setting or custom theme colors. - -// White background to match the Google SRP. -constexpr SkColor kHeaderBackgroundColor = SK_ColorWHITE; - -// Default light mode icon color for controls. -constexpr SkColor kIconColor = gfx::kGoogleGrey700; - -// Default light mode separator color. -constexpr SkColor kSeparatorColor = gfx::kGoogleGrey300; - // Base header button class. Responds appropriately to touch ui changes. class HeaderButton : public views::ImageButton { public: @@ -79,6 +71,13 @@ } ~HeaderButton() override = default; + // views::ImageButton: + void OnThemeChanged() override { + ImageButton::OnThemeChanged(); + views::InkDrop::Get(this)->SetBaseColor( + GetColorProvider()->GetColor(ui::kColorIcon)); + } + void UpdateIcon() { const int icon_size = ui::TouchUiController::Get()->touch_ui() @@ -86,7 +85,11 @@ : ChromeLayoutProvider::Get()->GetDistanceMetric( ChromeDistanceMetric:: DISTANCE_SIDE_PANEL_HEADER_VECTOR_ICON_SIZE); - views::SetImageFromVectorIconWithColor(this, icon_, icon_size, kIconColor); + SetImageModel(Button::STATE_NORMAL, ui::ImageModel::FromVectorIcon( + icon_, ui::kColorIcon, icon_size)); + SetImageModel(Button::STATE_DISABLED, + ui::ImageModel::FromVectorIcon(icon_, ui::kColorIconDisabled, + icon_size)); } private: @@ -96,22 +99,64 @@ BEGIN_METADATA(HeaderButton, views::ImageButton) END_METADATA -// Header view used to house the close control at the top of the side panel. +// A view that tracks the icon image of the current DSE. +class DseImageView : public views::ImageView { + public: + METADATA_HEADER(DseImageView); + explicit DseImageView(Browser* browser) + : default_search_icon_source_( + browser, + base::BindRepeating(&DseImageView::UpdateIconImage, + base::Unretained(this))) { + SetBorder(views::CreateEmptyBorder( + gfx::Insets(0, views::LayoutProvider::Get()->GetDistanceMetric( + views::DISTANCE_RELATED_CONTROL_VERTICAL)))); + } + ~DseImageView() override = default; + + void UpdateIconImage() { + SetImage(default_search_icon_source_.GetIconImage()); + } + + private: + DefaultSearchIconSource default_search_icon_source_; +}; + +BEGIN_METADATA(DseImageView, views::ImageView) +END_METADATA + +// Header view for the side search side panel. The structure is as follows. +// ___________________________________________________________________________ +// | dse_image_view | simple_site_name | feedback_button | close_button | +// | +// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// The image view and buttons are fixed at their preferred size. The simple site +// name label is configured to consume the remaining horizontal space. class HeaderView : public views::View { public: METADATA_HEADER(HeaderView); - explicit HeaderView(base::RepeatingClosure callback, Browser* browser) - : close_button_(AddChildView( - std::make_unique<HeaderButton>(vector_icons::kCloseIcon, - std::move(callback)))), - layout_(SetLayoutManager(std::make_unique<views::BoxLayout>())) { - views::InstallCircleHighlightPathGenerator(close_button_); - close_button_->SetID(SideSearchBrowserController::SideSearchViewID:: - VIEW_ID_SIDE_PANEL_CLOSE_BUTTON); - close_button_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_ACCNAME_SIDE_SEARCH_CLOSE_BUTTON)); - close_button_->SetTooltipText( - l10n_util::GetStringUTF16(IDS_TOOLTIP_SIDE_SEARCH_CLOSE_BUTTON)); + HeaderView(base::RepeatingClosure callback, Browser* browser) + : layout_(SetLayoutManager(std::make_unique<views::FlexLayout>())) { + layout_->SetOrientation(views::LayoutOrientation::kHorizontal) + .SetMainAxisAlignment(views::LayoutAlignment::kStart) + .SetCrossAxisAlignment(views::LayoutAlignment::kCenter); + + dse_image_view_ = AddChildView(std::make_unique<DseImageView>(browser)); + dse_image_view_->SetProperty( + views::kFlexBehaviorKey, + views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred, + views::MaximumFlexSizeRule::kPreferred)); + + auto* simple_site_name = AddChildView(std::make_unique<views::Label>()); + simple_site_name->SetID(SideSearchBrowserController::SideSearchViewID:: + VIEW_ID_SIDE_PANEL_TITLE_LABEL); + simple_site_name->SetHorizontalAlignment(gfx::ALIGN_LEFT); + simple_site_name->SetTextContext(CONTEXT_SIDE_PANEL_TITLE); + simple_site_name->SetTextStyle(views::style::STYLE_PRIMARY); + simple_site_name->SetProperty( + views::kFlexBehaviorKey, + views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero, + views::MaximumFlexSizeRule::kUnbounded)); if (base::FeatureList::IsEnabled(features::kSideSearchFeedback)) { base::RepeatingClosure feedback_callback = base::BindRepeating( @@ -135,33 +180,61 @@ l10n_util::GetStringUTF16(IDS_ACCNAME_SIDE_SEARCH_FEEDBACK_BUTTON)); feedback_button_->SetTooltipText( l10n_util::GetStringUTF16(IDS_TOOLTIP_SIDE_SEARCH_FEEDBACK_BUTTON)); + feedback_button_->SetProperty( + views::kFlexBehaviorKey, + views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred, + views::MaximumFlexSizeRule::kPreferred)); } + close_button_ = AddChildView(std::make_unique<HeaderButton>( + vector_icons::kCloseIcon, std::move(callback))); + views::InstallCircleHighlightPathGenerator(close_button_); + close_button_->SetID(SideSearchBrowserController::SideSearchViewID:: + VIEW_ID_SIDE_PANEL_CLOSE_BUTTON); + close_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_ACCNAME_SIDE_SEARCH_CLOSE_BUTTON)); + close_button_->SetTooltipText( + l10n_util::GetStringUTF16(IDS_TOOLTIP_SIDE_SEARCH_CLOSE_BUTTON)); + close_button_->SetProperty( + views::kFlexBehaviorKey, + views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred, + views::MaximumFlexSizeRule::kPreferred)); + + // Ensure the header view's containing view keeps its vertical size at the + // preferred size when laying out the side panel. The side panel does this + // using a flex layout so we need to ensure we set the correct flex + // behavior. SetProperty( views::kFlexBehaviorKey, views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred, views::MaximumFlexSizeRule::kPreferred)); - SetBackground(views::CreateSolidBackground(kHeaderBackgroundColor)); - layout_->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kEnd); + SetBackground( + views::CreateThemedSolidBackground(this, ui::kColorDialogBackground)); UpdateSpacing(); } ~HeaderView() override = default; private: + // Updates the toolbar insets which may change as we enter / leave touch mode. + // Icons are also updated to give them the opportunity to resize and adjust + // their insets. void UpdateSpacing() { - close_button_->UpdateIcon(); - + dse_image_view_->UpdateIconImage(); if (feedback_button_) feedback_button_->UpdateIcon(); + close_button_->UpdateIcon(); - layout_->set_inside_border_insets( + layout_->SetInteriorMargin( GetLayoutInsets(LayoutInset::TOOLBAR_INTERIOR_MARGIN)); } - HeaderButton* const close_button_; - HeaderButton* feedback_button_ = nullptr; - views::BoxLayout* const layout_; + raw_ptr<DseImageView> dse_image_view_ = nullptr; + raw_ptr<HeaderButton> feedback_button_ = nullptr; + raw_ptr<HeaderButton> close_button_ = nullptr; + raw_ptr<views::FlexLayout> const layout_; + + // Used to listen for when the UI enters / leaves touch mode. base::CallbackListSubscription subscription_ = ui::TouchUiController::Get()->RegisterCallback( base::BindRepeating(&HeaderView::UpdateSpacing, @@ -171,26 +244,16 @@ BEGIN_METADATA(HeaderView, views::View) END_METADATA -std::unique_ptr<views::Separator> CreateSeparator() { - auto separator = std::make_unique<views::Separator>(); - separator->SetColor(kSeparatorColor); - return separator; -} - views::WebView* ConfigureSidePanel(views::View* side_panel, Profile* profile, Browser* browser, base::RepeatingClosure callback) { - // BrowserViewLayout will layout the SidePanel to match the height of the - // content area. - side_panel->SetPreferredSize(gfx::Size(kSidePanelWidth, 1)); - auto container = std::make_unique<views::FlexLayoutView>(); container->SetOrientation(views::LayoutOrientation::kVertical); container->SetCrossAxisAlignment(views::LayoutAlignment::kStretch); container->AddChildView( std::make_unique<HeaderView>(std::move(callback), browser)); - container->AddChildView(CreateSeparator()); + container->AddChildView(std::make_unique<views::Separator>()); // The WebView will fill the remaining space after the header view has been // laid out. @@ -440,6 +503,17 @@ web_view_->SetWebContents(will_show_side_panel ? tab_contents_helper->GetSidePanelContents() : nullptr); + + // Update the side panel header title text if necessary + if (auto last_search_url = tab_contents_helper->last_search_url()) { + views::Label* title_label = + static_cast<views::Label*>(side_panel_->GetViewByID( + static_cast<int>(VIEW_ID_SIDE_PANEL_TITLE_LABEL))); + title_label->SetText( + url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains( + last_search_url.value())); + } + side_panel_->SetVisible(will_show_side_panel); // Update the side panel entrypoints - either the page action or the toolbar
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller.h b/chrome/browser/ui/views/side_search/side_search_browser_controller.h index bb33ead..77d6efc 100644 --- a/chrome/browser/ui/views/side_search/side_search_browser_controller.h +++ b/chrome/browser/ui/views/side_search/side_search_browser_controller.h
@@ -34,6 +34,7 @@ enum SideSearchViewID { VIEW_ID_NONE = 0, VIEW_ID_SIDE_PANEL_CLOSE_BUTTON, + VIEW_ID_SIDE_PANEL_TITLE_LABEL, }; SideSearchBrowserController(SidePanel* side_panel, BrowserView* browser_view);
diff --git a/chrome/browser/ui/views/tab_icon_view.cc b/chrome/browser/ui/views/tab_icon_view.cc index aa90736..6a3acbb8 100644 --- a/chrome/browser/ui/views/tab_icon_view.cc +++ b/chrome/browser/ui/views/tab_icon_view.cc
@@ -20,7 +20,6 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/favicon_size.h" #include "ui/gfx/paint_throbber.h" -#include "ui/views/image_model_utils.h" #if BUILDFLAG(IS_WIN) #include <windows.h> @@ -140,8 +139,8 @@ return; } - gfx::ImageSkia favicon = views::GetImageSkiaFromImageModel( - model_->GetFaviconForTabIconView(), GetColorProvider()); + gfx::ImageSkia favicon = + model_->GetFaviconForTabIconView().Rasterize(GetColorProvider()); if (!favicon.isNull()) { PaintFavicon(canvas, favicon); return;
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc index 4df9f0a5..43328c8 100644 --- a/chrome/browser/ui/views/toolbar/app_menu.cc +++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -57,6 +57,7 @@ #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/base/models/image_model.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/base/themed_vector_icon.h" #include "ui/color/color_id.h" #include "ui/color/color_provider.h" #include "ui/gfx/canvas.h" @@ -66,7 +67,6 @@ #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/scoped_canvas.h" #include "ui/gfx/text_utils.h" -#include "ui/native_theme/themed_vector_icon.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/background.h" #include "ui/views/border.h"
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc index 43ea6c3..0085e4e 100644 --- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc +++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc
@@ -51,14 +51,6 @@ "chrome-labs-tab-search-media-tabs", version_info::Channel::BETA, tab_search_media_tabs_variation_description)); - // Lens Region Search - lab_info.emplace_back(LabInfo( - flag_descriptions::kEnableLensRegionSearchFlagId, - l10n_util::GetStringUTF16(IDS_LENS_REGION_SEARCH_EXPERIMENT_NAME), - l10n_util::GetStringUTF16( - IDS_LENS_REGION_SEARCH_EXPERIMENT_DESCRIPTION), - "chrome-labs-lens-region-search", version_info::Channel::BETA)); - // Side Panel. lab_info.emplace_back(LabInfo( flag_descriptions::kSidePanelFlagId,
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc b/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc index 0a01c35..7683244 100644 --- a/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc +++ b/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc
@@ -48,7 +48,7 @@ // kTabSearchSelected = 2, kTabScrollingSelected = 3, kSidePanelSelected = 4, - kLensRegionSearchSelected = 5, + // kLensRegionSearchSelected = 5, kWebUITabStripSelected = 6, kTabSearchMediaTabsSelected = 7, kMaxValue = kTabSearchMediaTabsSelected, @@ -77,8 +77,6 @@ return ChromeLabsSelectedLab::kTabScrollingSelected; if (internal_name == flag_descriptions::kSidePanelFlagId) return ChromeLabsSelectedLab::kSidePanelSelected; - if (internal_name == flag_descriptions::kEnableLensRegionSearchFlagId) - return ChromeLabsSelectedLab::kLensRegionSearchSelected; #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP) && \ (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS_ASH)) if (internal_name == flag_descriptions::kWebUITabStripFlagId)
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc index b02bd62..9f76962e 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_view.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -19,7 +19,9 @@ #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/apps/intent_helper/intent_picker_features.h" #include "chrome/browser/command_updater.h" +#include "chrome/browser/download/bubble/download_bubble_prefs.h" #include "chrome/browser/media/router/media_router_feature.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profiles_state.h" @@ -268,7 +270,7 @@ } std::unique_ptr<DownloadToolbarButtonView> download_button; - if (base::FeatureList::IsEnabled(safe_browsing::kDownloadBubble)) { + if (download::IsDownloadBubbleEnabled(browser_->profile())) { download_button = std::make_unique<DownloadToolbarButtonView>(browser_view_); } @@ -507,8 +509,9 @@ views::Button* highlighted_button = nullptr; if (bubble_type == IntentPickerBubbleView::BubbleType::kClickToCall) { highlighted_button = + GetPageActionIconView(PageActionIconType::kClickToCall); - } else if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) { + } else if (apps::features::LinkCapturingUiUpdateEnabled()) { highlighted_button = GetIntentChipButton(); } else { highlighted_button =
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.cc b/chrome/browser/ui/web_applications/app_browser_controller.cc index 8585711..bb13ab1 100644 --- a/chrome/browser/ui/web_applications/app_browser_controller.cc +++ b/chrome/browser/ui/web_applications/app_browser_controller.cc
@@ -13,10 +13,12 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ssl/security_state_tab_helper.h" #include "chrome/browser/themes/browser_theme_pack.h" +#include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window_state.h" +#include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/tabs/tab_menu_model_factory.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/system_web_apps/system_web_app_delegate.h" @@ -38,6 +40,9 @@ #include "third_party/blink/public/common/features.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/models/image_model.h" +#include "ui/color/color_id.h" +#include "ui/color/color_recipe.h" +#include "ui/color/color_transform.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/color_palette.h" @@ -51,6 +56,7 @@ #if BUILDFLAG(IS_CHROMEOS_ASH) #include "chrome/browser/apps/icon_standardizer.h" +#include "chromeos/ui/base/chromeos_ui_constants.h" #endif namespace { @@ -389,7 +395,43 @@ void AppBrowserController::AddColorMixers( ui::ColorProvider* provider, - const ui::ColorProviderManager::Key& key) const {} + const ui::ColorProviderManager::Key& key) const { +#if !BUILDFLAG(IS_CHROMEOS_ASH) + // This color is the same as the default active frame color. + const absl::optional<SkColor> theme_color = GetThemeColor(); + ui::ColorTransform default_background = + key.color_mode == ui::ColorProviderManager::ColorMode::kLight + ? ui::ColorTransform(ui::kColorFrameActiveUnthemed) + : ui::HSLShift(ui::kColorFrameActiveUnthemed, + ThemeProperties::GetDefaultTint( + ThemeProperties::TINT_FRAME, true)); +#endif + ui::ColorMixer& mixer = provider->AddMixer(); + absl::optional<SkColor> bg_color = GetBackgroundColor(); + // TODO(kylixrd): The definition of kColorPwaBackground isn't fully fleshed + // out yet. Whether or not the PWA background color is set is used in many + // locations to derive other colors. Those specific locations would need to be + // addressed in their own context. + if (bg_color) + mixer[kColorPwaBackground] = {bg_color.value()}; + mixer[kColorPwaMenuButtonIcon] = {kColorToolbarButtonIcon}; + mixer[kColorPwaSecurityChipForeground] = {ui::kColorSecondaryForeground}; + mixer[kColorPwaSecurityChipForegroundDangerous] = { + ui::kColorAlertHighSeverity}; + mixer[kColorPwaSecurityChipForegroundPolicyCert] = { + ui::kColorDisabledForeground}; + mixer[kColorPwaSecurityChipForegroundSecure] = { + kColorPwaSecurityChipForeground}; +#if BUILDFLAG(IS_CHROMEOS_ASH) + // Ash system frames differ from ChromeOS browser frames. + mixer[kColorPwaTheme] = {chromeos::kDefaultFrameColor}; +#else + mixer[kColorPwaTheme] = theme_color ? ui::ColorTransform(theme_color.value()) + : default_background; +#endif + mixer[kColorPwaToolbarBackground] = {ui::kColorEndpointBackground}; + mixer[kColorPwaToolbarForeground] = {ui::kColorEndpointForeground}; +} void AppBrowserController::OnReceivedInitialURL() { UpdateCustomTabBarVisibility(/*animate=*/false);
diff --git a/chrome/browser/ui/webui/OWNERS b/chrome/browser/ui/webui/OWNERS index 624340e..acfb748 100644 --- a/chrome/browser/ui/webui/OWNERS +++ b/chrome/browser/ui/webui/OWNERS
@@ -18,4 +18,4 @@ per-file signin_internals_ui*=achuith@chromium.org -per-file invalidations_message_handler.*=file://components/invalidation/OWNERS +per-file invalidations/invalidations_message_handler.*=file://components/invalidation/OWNERS
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc index a85cacb..4f14930 100644 --- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc +++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -24,8 +24,9 @@ #include "chrome/grit/generated_resources.h" #include "components/app_constants/constants.h" #include "components/services/app_service/public/cpp/app_registry_cache.h" -#include "components/services/app_service/public/cpp/intent_constants.h" +#include "components/services/app_service/public/cpp/intent_filter.h" #include "components/services/app_service/public/cpp/intent_filter_util.h" +#include "components/services/app_service/public/cpp/intent_util.h" #include "components/services/app_service/public/cpp/preferred_apps_list_handle.h" #include "components/services/app_service/public/cpp/types_util.h" #include "components/services/app_service/public/mojom/types.mojom.h" @@ -113,7 +114,9 @@ if (update.Readiness() == apps::Readiness::kReady) { for (auto& filter : update.IntentFilters()) { if (apps_util::IsSupportedLinkForApp(app_id, filter)) { - intent_filters.emplace_back(std::move(filter)); + intent_filters.emplace_back( + apps::ConvertIntentFilterToMojomIntentFilter( + filter)); } } } @@ -296,7 +299,7 @@ // Remove the use_browser app ID as it's mainly used inside the intent system and is not an app // in app management. This prevents an overlap dialog from being shown when there are no "real" // apps that overlap. - app_ids.erase(apps::kUseBrowserForLink); + app_ids.erase(apps_util::kUseBrowserForLink); std::move(callback).Run(std::move(app_ids).extract()); }
diff --git a/chrome/browser/ui/webui/app_service_internals/app_service_internals_page_handler_impl.cc b/chrome/browser/ui/webui/app_service_internals/app_service_internals_page_handler_impl.cc index 0de418ee..29233a4 100644 --- a/chrome/browser/ui/webui/app_service_internals/app_service_internals_page_handler_impl.cc +++ b/chrome/browser/ui/webui/app_service_internals/app_service_internals_page_handler_impl.cc
@@ -15,8 +15,8 @@ #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "components/services/app_service/public/cpp/app_update.h" -#include "components/services/app_service/public/cpp/intent_constants.h" #include "components/services/app_service/public/cpp/intent_filter_util.h" +#include "components/services/app_service/public/cpp/intent_util.h" AppServiceInternalsPageHandlerImpl::AppServiceInternalsPageHandlerImpl( Profile* profile) @@ -74,7 +74,7 @@ auto ptr = mojom::app_service_internals::PreferredAppInfo::New(); ptr->id = kv.first; - if (ptr->id == apps::kUseBrowserForLink) { + if (ptr->id == apps_util::kUseBrowserForLink) { ptr->name = ptr->id; } else { proxy->AppRegistryCache().ForOneApp(
diff --git a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc index 0f21a75..42259dda 100644 --- a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc +++ b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
@@ -14,6 +14,7 @@ #include <utility> #include <vector> +#include "ash/constants/ash_features.h" #include "base/bind.h" #include "base/callback_helpers.h" #include "base/files/file_enumerator.h" @@ -281,6 +282,16 @@ base::BindRepeating(&DriveInternalsWebUIHandler::SetMirroringEnabled, weak_ptr_factory_.GetWeakPtr())); web_ui()->RegisterMessageCallback( + "addSyncPath", + base::BindRepeating(&DriveInternalsWebUIHandler::ToggleSyncPath, + weak_ptr_factory_.GetWeakPtr(), + drivefs::mojom::MirrorPathStatus::kStart)); + web_ui()->RegisterMessageCallback( + "removeSyncPath", + base::BindRepeating(&DriveInternalsWebUIHandler::ToggleSyncPath, + weak_ptr_factory_.GetWeakPtr(), + drivefs::mojom::MirrorPathStatus::kStop)); + web_ui()->RegisterMessageCallback( "enableTracing", base::BindRepeating(&DriveInternalsWebUIHandler::SetTracingEnabled, weak_ptr_factory_.GetWeakPtr(), true)); @@ -358,6 +369,8 @@ UpdateDriveDebugSection(); + UpdateMirrorSyncSection(); + // When the drive-internals page is reloaded by the reload key, the page // content is recreated, but this WebUI object is not (instead, OnPageLoaded // is called again). In that case, we have to forget the last sent ID here, @@ -473,10 +486,6 @@ MaybeCallJavascript("updateVerboseLogging", base::Value(verbose_logging_enabled)); - bool mirroring_enabled = profile()->GetPrefs()->GetBoolean( - drive::prefs::kDriveFsEnableMirrorSync); - MaybeCallJavascript("updateMirroring", base::Value(mirroring_enabled)); - base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE}, base::BindOnce(GetDeveloperMode), @@ -496,6 +505,82 @@ } } + void UpdateMirrorSyncSection() { + if (!chromeos::features::IsDriveFsMirroringEnabled()) { + SetSectionEnabled("mirror-sync-section", false); + return; + } + + SetSectionEnabled("mirror-sync-section", true); + + bool mirroring_enabled = profile()->GetPrefs()->GetBoolean( + drive::prefs::kDriveFsEnableMirrorSync); + MaybeCallJavascript("updateMirroring", base::Value(mirroring_enabled)); + SetSectionEnabled("mirror-sync-paths", mirroring_enabled); + SetSectionEnabled("mirror-path-form", mirroring_enabled); + if (!mirroring_enabled) { + return; + } + + drive::DriveIntegrationService* integration_service = + GetIntegrationService(); + if (!integration_service) { + return; + } + + integration_service->GetSyncingPaths( + base::BindOnce(&DriveInternalsWebUIHandler::OnGetSyncingPaths, + weak_ptr_factory_.GetWeakPtr())); + } + + void OnGetSyncingPaths(drive::FileError status, + const std::vector<base::FilePath>& paths) { + if (status != drive::FILE_ERROR_OK) { + LOG(ERROR) << "Error retrieving syncing paths: " << status; + return; + } + for (const base::FilePath& sync_path : paths) { + MaybeCallJavascript( + "onAddSyncPath", base::Value(sync_path.value()), + base::Value(drive::FileErrorToString(drive::FILE_ERROR_OK))); + } + } + + void ToggleSyncPath(drivefs::mojom::MirrorPathStatus status, + const base::Value::List& args) { + if (!chromeos::features::IsDriveFsMirroringEnabled()) { + return; + } + + drive::DriveIntegrationService* integration_service = + GetIntegrationService(); + if (!integration_service) { + return; + } + + if (args.size() == 1 && args[0].is_string()) { + const base::FilePath sync_path(args[0].GetString()); + auto callback = + base::BindOnce((status == drivefs::mojom::MirrorPathStatus::kStart) + ? &DriveInternalsWebUIHandler::OnAddSyncPath + : &DriveInternalsWebUIHandler::OnRemoveSyncPath, + weak_ptr_factory_.GetWeakPtr(), sync_path); + integration_service->ToggleSyncForPath(sync_path, status, + std::move(callback)); + } + } + + void OnAddSyncPath(const base::FilePath& sync_path, drive::FileError status) { + MaybeCallJavascript("onAddSyncPath", base::Value(sync_path.value()), + base::Value(drive::FileErrorToString(status))); + } + + void OnRemoveSyncPath(const base::FilePath& sync_path, + drive::FileError status) { + MaybeCallJavascript("onRemoveSyncPath", base::Value(sync_path.value()), + base::Value(drive::FileErrorToString(status))); + } + // Called when GetDeveloperMode() is complete. void OnGetDeveloperMode(bool enabled) { developer_mode_ = enabled; @@ -682,6 +767,8 @@ bool enabled = args[0].GetBool(); profile()->GetPrefs()->SetBoolean(drive::prefs::kDriveFsEnableMirrorSync, enabled); + SetSectionEnabled("mirror-sync-paths", enabled); + SetSectionEnabled("mirror-path-form", enabled); } }
diff --git a/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.cc index 958e19b..e840174b 100644 --- a/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.cc
@@ -210,4 +210,8 @@ void ConsolidatedConsentScreenHandler::SetIsDeviceOwner(bool is_owner) { CallJS("login.ConsolidatedConsentScreen.setIsDeviceOwner", is_owner); } + +void ConsolidatedConsentScreenHandler::HideUsageOptin() { + CallJS("login.ConsolidatedConsentScreen.setUsageOptinHidden"); +} } // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.h index 3fb71919..da9deb5 100644 --- a/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.h
@@ -63,6 +63,9 @@ virtual void SetBackupMode(bool enabled, bool managed) = 0; virtual void SetLocationMode(bool enabled, bool managed) = 0; virtual void SetIsDeviceOwner(bool is_owner) = 0; + + // Hide the entire section that allows user to opt-in/opt-out from metrics. + virtual void HideUsageOptin() = 0; }; class ConsolidatedConsentScreenHandler : public ConsolidatedConsentScreenView, @@ -89,6 +92,7 @@ void SetBackupMode(bool enabled, bool managed) override; void SetLocationMode(bool enabled, bool managed) override; void SetIsDeviceOwner(bool is_owner) override; + void HideUsageOptin() override; // content::WebUIMessageHandler: void RegisterMessages() override;
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/OWNERS b/chrome/browser/ui/webui/chromeos/multidevice_internals/OWNERS index 7027ab73..18377141 100644 --- a/chrome/browser/ui/webui/chromeos/multidevice_internals/OWNERS +++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.cc b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.cc index 3b2387b..230f74f 100644 --- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.cc +++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.cc
@@ -4,10 +4,10 @@ #include "chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/bind.h" #include "base/i18n/time_formatting.h" #include "base/values.h" -#include "chromeos/components/multidevice/logging/logging.h" namespace chromeos {
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.h b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.h index 92289bb..c7b3192 100644 --- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.h +++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.h
@@ -5,9 +5,9 @@ #ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_MULTIDEVICE_INTERNALS_MULTIDEVICE_INTERNALS_LOGS_HANDLER_H_ #define CHROME_BROWSER_UI_WEBUI_CHROMEOS_MULTIDEVICE_INTERNALS_MULTIDEVICE_INTERNALS_LOGS_HANDLER_H_ +#include "ash/components/multidevice/logging/log_buffer.h" +#include "ash/components/multidevice/logging/logging.h" #include "base/scoped_observation.h" -#include "chromeos/components/multidevice/logging/log_buffer.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "content/public/browser/web_ui_message_handler.h" namespace base {
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc index 309f5bd..5fb2c49 100644 --- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc +++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/camera_roll_item.h" #include "ash/components/phonehub/fake_phone_hub_manager.h" #include "ash/components/phonehub/notification.h" @@ -14,7 +15,6 @@ #include "base/time/time.h" #include "chrome/browser/ash/phonehub/phone_hub_manager_factory.h" #include "chrome/browser/profiles/profile.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/prefs/pref_service.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkBitmap.h"
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/OWNERS b/chrome/browser/ui/webui/chromeos/multidevice_setup/OWNERS index 7027ab73..18377141 100644 --- a/chrome/browser/ui/webui/chromeos/multidevice_setup/OWNERS +++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ui/webui/commander/commander_ui.cc b/chrome/browser/ui/webui/commander/commander_ui.cc index 25da2b2..1428bb5 100644 --- a/chrome/browser/ui/webui/commander/commander_ui.cc +++ b/chrome/browser/ui/webui/commander/commander_ui.cc
@@ -13,6 +13,7 @@ #include "chrome/grit/browser_resources.h" #include "chrome/grit/commander_resources.h" #include "chrome/grit/commander_resources_map.h" +#include "chrome/grit/generated_resources.h" #include "content/public/browser/web_ui_data_source.h" CommanderUI::CommanderUI(content::WebUI* web_ui) @@ -23,10 +24,13 @@ content::WebUIDataSource* source = content::WebUIDataSource::Create(chrome::kChromeUICommanderHost); - // TODO(lgrey): Localize when these are no longer temp. - source->AddString("placeholder", "Type to search Chrome Commands…"); - source->AddString("noResults", "No Chrome Commands found."); - source->AddString("pageTitle", "Commander"); + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"placeholder", IDS_QUICK_COMMANDS_PLACEHOLDER}, + {"noResults", IDS_QUICK_COMMANDS_NO_RESULTS}, + {"pageTitle", IDS_QUICK_COMMANDS_LABEL}, + }; + for (const auto& str : kLocalizedStrings) + webui::AddLocalizedString(source, str.name, str.id); webui::SetupWebUIDataSource( source, base::make_span(kCommanderResources, kCommanderResourcesSize), IDR_COMMANDER_COMMANDER_HTML);
diff --git a/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc b/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc index 5639e60..cb56c3a8 100644 --- a/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc +++ b/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc
@@ -86,11 +86,10 @@ } void InvalidationsMessageHandler::OnRegistrationChange( - const std::multiset<std::string>& registered_handlers) { + const std::set<std::string>& registered_handlers) { base::ListValue list_of_handlers; - for (auto it = registered_handlers.begin(); it != registered_handlers.end(); - ++it) { - list_of_handlers.Append(*it); + for (const auto& registered_handler : registered_handlers) { + list_of_handlers.Append(registered_handler); } FireWebUIListener("handlers-updated", list_of_handlers); }
diff --git a/chrome/browser/ui/webui/invalidations/invalidations_message_handler.h b/chrome/browser/ui/webui/invalidations/invalidations_message_handler.h index 56ee1db..5a3da7f3 100644 --- a/chrome/browser/ui/webui/invalidations/invalidations_message_handler.h +++ b/chrome/browser/ui/webui/invalidations/invalidations_message_handler.h
@@ -33,7 +33,7 @@ // Implementation of InvalidationLoggerObserver. void OnRegistrationChange( - const std::multiset<std::string>& registered_handlers) override; + const std::set<std::string>& registered_handlers) override; void OnStateChange(const invalidation::InvalidatorState& new_state, const base::Time& last_change_timestamp) override; void OnUpdatedTopics(
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc index 857696a..8caf81d 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -99,49 +99,47 @@ void AddResourcesForCartDiscountConsentV2(content::WebUIDataSource* source) { AddRawStringOrDefault( source, "modulesCartDiscountConsentContent", - ntp_features::kNtpChromeCartModuleDiscountConsentStringChangeContent - .Get(), + commerce::kNtpChromeCartModuleDiscountConsentStringChangeContent.Get(), IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT_V2); source->AddBoolean( "modulesCartConsentStepTwoDifferentColor", - ntp_features:: - kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor.Get()); + commerce::kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor + .Get()); AddRawStringOrDefault( source, "modulesCartDiscountConentTitle", - ntp_features::kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle - .Get(), + commerce::kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle.Get(), IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_TITLE); source->AddBoolean( "modulesCartStepOneUseStaticContent", - ntp_features:: - kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent.Get()); + commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent + .Get()); // This does not have a raw string resource. source->AddString( "modulesCartStepOneStaticContent", - ntp_features::kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent + commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent .Get()); AddRawStringOrDefault( source, "modulesCartConsentStepOneOneMerchantContent", - ntp_features::kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart + commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart .Get(), IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_STEP_1_WITH_MERCHANT_NAME); AddRawStringOrDefault( source, "modulesCartConsentStepOneTwoMerchantsContent", - ntp_features::kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts + commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts .Get(), IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_STEP_1_WITH_TWO_MERCHANT_NAMES); AddRawStringOrDefault( source, "modulesCartConsentStepOneThreeMerchantsContent", - ntp_features:: - kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts.Get(), + commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts + .Get(), IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_STEP_1_WITH_THREE_MERCHANT_NAMES); AddRawStringOrDefault( source, "modulesCartConsentStepTwoContent", - ntp_features::kNtpChromeCartModuleDiscountConsentNtpStepTwoContent.Get(), + commerce::kNtpChromeCartModuleDiscountConsentNtpStepTwoContent.Get(), IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT_V3); source->AddLocalizedString( @@ -150,8 +148,7 @@ source->AddBoolean( "modulesCartDiscountInlineCardShowCloseButton", - ntp_features::kNtpChromeCartModuleDiscountConsentInlineShowCloseButton - .Get()); + commerce::kNtpChromeCartModuleDiscountConsentInlineShowCloseButton.Get()); } content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) { @@ -396,7 +393,7 @@ source->AddInteger( "modulesCartDiscountConsentVariation", - ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation.Get()); + commerce::kNtpChromeCartModuleDiscountConsentNtpVariation.Get()); if (base::FeatureList::IsEnabled(commerce::kDiscountConsentV2)) { AddResourcesForCartDiscountConsentV2(source);
diff --git a/chrome/browser/ui/webui/settings/chromeos/OWNERS b/chrome/browser/ui/webui/settings/chromeos/OWNERS index 6b2398e..37ddeebc 100644 --- a/chrome/browser/ui/webui/settings/chromeos/OWNERS +++ b/chrome/browser/ui/webui/settings/chromeos/OWNERS
@@ -1,6 +1,6 @@ file://chrome/browser/resources/settings/chromeos/OWNERS per-file languages_section*=file://chrome/browser/resources/settings/chromeos/os_languages_page/OWNERS -per-file multidevice_handler*=file://chromeos/components/multidevice/OWNERS +per-file multidevice_handler*=file://ash/components/multidevice/OWNERS per-file account_manager_*=file://ash/components/account_manager/OWNERS per-file apps_section*=file://chrome/browser/ui/webui/app_management/OWNERS
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc index a809c7c..b31dbdb9 100644 --- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc +++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h" +#include "ash/components/multidevice/logging/logging.h" #include "ash/components/phonehub/util/histogram_util.h" #include "ash/components/proximity_auth/proximity_auth_pref_names.h" #include "ash/constants/ash_features.h" @@ -23,7 +24,6 @@ #include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h" -#include "chromeos/components/multidevice/logging/logging.h" #include "components/content_settings/core/common/content_settings_pattern.h" #include "components/prefs/pref_service.h" #include "content/public/browser/web_ui.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h index 01ec488..babc030c 100644 --- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h +++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_MULTIDEVICE_HANDLER_H_ #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_MULTIDEVICE_HANDLER_H_ +#include "ash/components/multidevice/remote_device_ref.h" #include "ash/components/phonehub/camera_roll_manager.h" #include "ash/components/phonehub/multidevice_feature_access_manager.h" #include "ash/components/phonehub/notification_access_setup_operation.h" @@ -16,7 +17,6 @@ #include "chrome/browser/ash/android_sms/android_sms_app_manager.h" #include "chrome/browser/ash/android_sms/android_sms_service_factory.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" -#include "chromeos/components/multidevice/remote_device_ref.h" #include "components/prefs/pref_change_registrar.h" class PrefService;
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc index 20152ad..f444464 100644 --- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/components/multidevice/remote_device_test_util.h" #include "ash/components/phonehub/fake_camera_roll_manager.h" #include "ash/components/phonehub/fake_multidevice_feature_access_manager.h" #include "ash/components/phonehub/multidevice_feature_access_manager.h" @@ -20,7 +21,6 @@ #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h" #include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h" #include "chrome/test/base/testing_profile.h" -#include "chromeos/components/multidevice/remote_device_test_util.h" #include "components/content_settings/core/common/content_settings_pattern.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h"
diff --git a/chrome/browser/url_param_filter/cross_otr_observer.cc b/chrome/browser/url_param_filter/cross_otr_observer.cc index a13eec6b..2613591e 100644 --- a/chrome/browser/url_param_filter/cross_otr_observer.cc +++ b/chrome/browser/url_param_filter/cross_otr_observer.cc
@@ -3,7 +3,9 @@ // found in the LICENSE file. #include "chrome/browser/url_param_filter/cross_otr_observer.h" + #include <memory> + #include "base/metrics/histogram_functions.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_handle.h" @@ -27,15 +29,16 @@ NavigateParams::PrivacySensitivity::CROSS_OTR && params.started_from_context_menu && !ui::PageTransitionCoreTypeIs(params.transition, - ui::PAGE_TRANSITION_AUTO_BOOKMARK) && - !web_contents->GetUserData(CrossOtrObserver::kUserDataKey)) { - web_contents->SetUserData(CrossOtrObserver::kUserDataKey, - std::make_unique<CrossOtrObserver>(web_contents)); + ui::PAGE_TRANSITION_AUTO_BOOKMARK)) { + // Inherited from WebContentsUserData and checks for an already-attached + // instance internally. + CrossOtrObserver::CreateForWebContents(web_contents); } } CrossOtrObserver::CrossOtrObserver(content::WebContents* web_contents) - : content::WebContentsObserver(web_contents) {} + : content::WebContentsObserver(web_contents), + content::WebContentsUserData<CrossOtrObserver>(*web_contents) {} void CrossOtrObserver::DidFinishNavigation( content::NavigationHandle* navigation_handle) { @@ -85,8 +88,10 @@ void CrossOtrObserver::Detach() { base::UmaHistogramCounts100(kCrossOtrRefreshCountMetricName, refresh_count_); - web_contents()->RemoveUserData(CrossOtrObserver::kUserDataKey); + web_contents()->RemoveUserData(CrossOtrObserver::UserDataKey()); // DO NOT add code past this point. `this` is destroyed. } +WEB_CONTENTS_USER_DATA_KEY_IMPL(CrossOtrObserver); + } // namespace url_param_filter
diff --git a/chrome/browser/url_param_filter/cross_otr_observer.h b/chrome/browser/url_param_filter/cross_otr_observer.h index e22f85d..6a7839e7b 100644 --- a/chrome/browser/url_param_filter/cross_otr_observer.h +++ b/chrome/browser/url_param_filter/cross_otr_observer.h
@@ -5,24 +5,21 @@ #ifndef CHROME_BROWSER_URL_PARAM_FILTER_CROSS_OTR_OBSERVER_H_ #define CHROME_BROWSER_URL_PARAM_FILTER_CROSS_OTR_OBSERVER_H_ -#include "base/supports_user_data.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" namespace url_param_filter { // Observes navigations that originate in normal browsing and move into OTR // browsing. class CrossOtrObserver : public content::WebContentsObserver, - public base::SupportsUserData::Data { + public content::WebContentsUserData<CrossOtrObserver> { public: - // The key used to associate this observer with the given WebContents. - constexpr static const char kUserDataKey[] = "CrossOtrObserver"; // Attaches the observer in cases where it should do so; leaves `web_contents` // unchanged otherwise. static void MaybeCreateForWebContents(content::WebContents* web_contents, const NavigateParams& params); - explicit CrossOtrObserver(content::WebContents* web_contents); // content::WebContentsObserver: void DidFinishNavigation( content::NavigationHandle* navigation_handle) override; @@ -31,6 +28,13 @@ void WebContentsDestroyed() override; private: + explicit CrossOtrObserver(content::WebContents* web_contents); + + friend class content::WebContentsUserData<CrossOtrObserver>; + // Inherited from content::WebContentsUserData, but should not be used outside + // this class. MaybeCreateForWebcontents must be used instead. + using content::WebContentsUserData<CrossOtrObserver>::CreateForWebContents; + // Flushes metrics and removes the observer from the WebContents. void Detach(); // Drives state machine logic; we write the cross-OTR response code metric // only for the first navigation, which is that which would have parameters @@ -39,6 +43,8 @@ // Tracks refreshes observed, which could point to an issue with param // filtering causing unexpected behavior for the user. int refresh_count_ = 0; + + WEB_CONTENTS_USER_DATA_KEY_DECL(); }; } // namespace url_param_filter
diff --git a/chrome/browser/url_param_filter/cross_otr_observer_unittest.cc b/chrome/browser/url_param_filter/cross_otr_observer_unittest.cc index 91a6423..39a7754 100644 --- a/chrome/browser/url_param_filter/cross_otr_observer_unittest.cc +++ b/chrome/browser/url_param_filter/cross_otr_observer_unittest.cc
@@ -46,7 +46,7 @@ content::WebContentsTester::CreateTestWebContents(profile(), nullptr); CrossOtrObserver::MaybeCreateForWebContents(web_contents.get(), params); - ASSERT_EQ(web_contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr); + ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents.get()), nullptr); } TEST_F(CrossOtrObserverTest, DefaultSensitivity) { NavigateParams params(profile(), GURL("https://www.foo.com"), @@ -59,7 +59,7 @@ content::WebContentsTester::CreateTestWebContents(profile(), nullptr); CrossOtrObserver::MaybeCreateForWebContents(web_contents.get(), params); - ASSERT_EQ(web_contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr); + ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents.get()), nullptr); } TEST_F(CrossOtrObserverTest, BookmarkLink) { NavigateParams params(profile(), GURL("https://www.foo.com"), @@ -72,7 +72,7 @@ content::WebContentsTester::CreateTestWebContents(profile(), nullptr); CrossOtrObserver::MaybeCreateForWebContents(web_contents.get(), params); - ASSERT_EQ(web_contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr); + ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents.get()), nullptr); } TEST_F(CrossOtrObserverTest, CreateKey) { NavigateParams params(profile(), GURL("https://www.foo.com"), @@ -83,7 +83,7 @@ content::WebContents* contents = web_contents(); CrossOtrObserver::MaybeCreateForWebContents(contents, params); - ASSERT_NE(contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr); + ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr); } TEST_F(CrossOtrObserverTest, DuplicateCreateKey) { NavigateParams params(profile(), GURL("https://www.foo.com"), @@ -95,7 +95,7 @@ CrossOtrObserver::MaybeCreateForWebContents(contents, params); CrossOtrObserver::MaybeCreateForWebContents(contents, params); - ASSERT_NE(contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr); + ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr); } TEST_F(CrossOtrObserverTest, HandleRedirects) { base::HistogramTester histogram_tester; @@ -106,8 +106,7 @@ params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR; content::WebContents* contents = web_contents(); CrossOtrObserver::MaybeCreateForWebContents(contents, params); - CrossOtrObserver* observer = static_cast<CrossOtrObserver*>( - contents->GetUserData(CrossOtrObserver::kUserDataKey)); + CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents); ASSERT_NE(observer, nullptr); std::unique_ptr<content::MockNavigationHandle> handle = std::make_unique<NiceMock<content::MockNavigationHandle>>(contents); @@ -131,8 +130,7 @@ params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR; content::WebContents* contents = web_contents(); CrossOtrObserver::MaybeCreateForWebContents(contents, params); - CrossOtrObserver* observer = static_cast<CrossOtrObserver*>( - contents->GetUserData(CrossOtrObserver::kUserDataKey)); + CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents); ASSERT_NE(observer, nullptr); std::unique_ptr<content::MockNavigationHandle> handle = std::make_unique<NiceMock<content::MockNavigationHandle>>(contents); @@ -155,8 +153,7 @@ params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR; content::WebContents* contents = web_contents(); CrossOtrObserver::MaybeCreateForWebContents(contents, params); - CrossOtrObserver* observer = static_cast<CrossOtrObserver*>( - contents->GetUserData(CrossOtrObserver::kUserDataKey)); + CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents); ASSERT_NE(observer, nullptr); std::unique_ptr<content::MockNavigationHandle> handle = std::make_unique<NiceMock<content::MockNavigationHandle>>(contents); @@ -174,8 +171,7 @@ params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR; content::WebContents* contents = web_contents(); CrossOtrObserver::MaybeCreateForWebContents(contents, params); - CrossOtrObserver* observer = static_cast<CrossOtrObserver*>( - contents->GetUserData(CrossOtrObserver::kUserDataKey)); + CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents); ASSERT_NE(observer, nullptr); std::unique_ptr<content::MockNavigationHandle> handle = std::make_unique<NiceMock<content::MockNavigationHandle>>(contents); @@ -187,7 +183,7 @@ // The observer should not cease observation after first load, regardless of // whether the headers include a response code. We still want to see // the refresh count. - ASSERT_NE(contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr); + ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr); } TEST_F(CrossOtrObserverTest, RefreshedAfterNavigation) { base::HistogramTester histogram_tester; @@ -198,8 +194,7 @@ params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR; content::WebContents* contents = web_contents(); CrossOtrObserver::MaybeCreateForWebContents(contents, params); - CrossOtrObserver* observer = static_cast<CrossOtrObserver*>( - contents->GetUserData(CrossOtrObserver::kUserDataKey)); + CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents); ASSERT_NE(observer, nullptr); std::unique_ptr<content::MockNavigationHandle> handle = std::make_unique<NiceMock<content::MockNavigationHandle>>(contents); @@ -229,8 +224,7 @@ params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR; content::WebContents* contents = web_contents(); CrossOtrObserver::MaybeCreateForWebContents(contents, params); - CrossOtrObserver* observer = static_cast<CrossOtrObserver*>( - contents->GetUserData(CrossOtrObserver::kUserDataKey)); + CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents); ASSERT_NE(observer, nullptr); std::unique_ptr<content::MockNavigationHandle> handle = std::make_unique<NiceMock<content::MockNavigationHandle>>(contents); @@ -271,8 +265,7 @@ params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR; content::WebContents* contents = web_contents(); CrossOtrObserver::MaybeCreateForWebContents(contents, params); - CrossOtrObserver* observer = static_cast<CrossOtrObserver*>( - contents->GetUserData(CrossOtrObserver::kUserDataKey)); + CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents); ASSERT_NE(observer, nullptr); std::unique_ptr<content::MockNavigationHandle> handle = std::make_unique<NiceMock<content::MockNavigationHandle>>(contents); @@ -294,7 +287,7 @@ handle->set_has_committed(true); observer->DidFinishNavigation(handle.get()); - ASSERT_EQ(contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr); + ASSERT_EQ(CrossOtrObserver::FromWebContents(contents), nullptr); histogram_tester.ExpectTotalCount(kCrossOtrRefreshCountMetricName, 1); ASSERT_EQ(histogram_tester.GetTotalSum(kCrossOtrRefreshCountMetricName), 2); @@ -312,8 +305,7 @@ params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR; content::WebContents* contents = web_contents(); CrossOtrObserver::MaybeCreateForWebContents(contents, params); - CrossOtrObserver* observer = static_cast<CrossOtrObserver*>( - contents->GetUserData(CrossOtrObserver::kUserDataKey)); + CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents); ASSERT_NE(observer, nullptr); std::unique_ptr<content::MockNavigationHandle> handle = std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc index 0c2d0c38..37911fa 100644 --- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc +++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
@@ -4,14 +4,27 @@ #include "chrome/browser/web_applications/app_service/web_app_publisher_helper.h" +#include <atomic> +#include <ostream> +#include <set> +#include <utility> + #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/check.h" +#include "base/check_op.h" #include "base/containers/contains.h" #include "base/containers/extend.h" #include "base/feature_list.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/metrics/histogram_base.h" #include "base/metrics/histogram_macros.h" +#include "base/notreached.h" +#include "base/time/time.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" +#include "chrome/browser/apps/app_service/app_launch_params.h" #include "chrome/browser/apps/app_service/intent_util.h" #include "chrome/browser/apps/app_service/launch_utils.h" #include "chrome/browser/apps/app_service/publishers/app_publisher.h" @@ -26,13 +39,12 @@ #include "chrome/browser/ui/web_applications/web_app_launch_manager.h" #include "chrome/browser/ui/web_applications/web_app_ui_manager_impl.h" #include "chrome/browser/web_applications/commands/run_on_os_login_command.h" +#include "chrome/browser/web_applications/os_integration/os_integration_manager.h" #include "chrome/browser/web_applications/policy/web_app_policy_manager.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/browser/web_applications/web_app_constants.h" #include "chrome/browser/web_applications/web_app_helpers.h" -#include "chrome/browser/web_applications/web_app_id_constants.h" #include "chrome/browser/web_applications/web_app_install_finalizer.h" -#include "chrome/browser/web_applications/web_app_prefs_utils.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_registrar.h" #include "chrome/browser/web_applications/web_app_sync_bridge.h" @@ -45,6 +57,7 @@ #include "components/services/app_service/public/cpp/intent_util.h" #include "components/services/app_service/public/cpp/publisher_base.h" #include "components/services/app_service/public/cpp/run_on_os_login_types.h" +#include "components/services/app_service/public/mojom/types.mojom.h" #include "content/public/browser/clear_site_data_utils.h" #include "third_party/blink/public/mojom/manifest/capture_links.mojom.h" #include "ui/base/window_open_disposition.h" @@ -66,6 +79,7 @@ #include "ash/constants/ash_features.h" #include "chrome/browser/ash/crostini/crostini_terminal.h" #include "chrome/browser/ash/crostini/crostini_util.h" +#include "chrome/browser/ash/file_manager/app_id.h" #include "chrome/browser/ash/login/demo_mode/demo_session.h" #include "chrome/browser/chromeos/arc/arc_web_contents_data.h" #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h" @@ -82,8 +96,14 @@ using apps::IconEffects; +namespace content { +class BrowserContext; +} + namespace web_app { +class WebAppInstallManager; + namespace { // Only supporting important permissions for now. @@ -499,18 +519,8 @@ DCHECK_EQ(web_app->IsSystemApp(), app->install_reason == apps::InstallReason::kSystem); - GURL install_url; - if (registrar().HasExternalAppWithInstallSource( - web_app->app_id(), ExternalInstallSource::kExternalPolicy)) { - std::map<AppId, GURL> installed_apps = - registrar().GetExternallyInstalledApps( - ExternalInstallSource::kExternalPolicy); - auto it = installed_apps.find(web_app->app_id()); - if (it != installed_apps.end()) { - install_url = it->second; - } - } - app->policy_id = install_url.spec(); + app->policy_id = GetPolicyId(*web_app); + app->permissions = CreatePermissions(web_app); SetWebAppShowInFields(web_app, *app); @@ -581,18 +591,7 @@ app->install_source = ConvertInstallSourceToMojom( provider_->registrar().GetAppInstallSourceForMetrics(web_app->app_id())); - GURL install_url; - if (registrar().HasExternalAppWithInstallSource( - web_app->app_id(), ExternalInstallSource::kExternalPolicy)) { - std::map<AppId, GURL> installed_apps = - registrar().GetExternallyInstalledApps( - ExternalInstallSource::kExternalPolicy); - auto it = installed_apps.find(web_app->app_id()); - if (it != installed_apps.end()) { - install_url = it->second; - } - } - app->policy_id = install_url.spec(); + app->policy_id = GetPolicyId(*web_app); // For system web apps (only), the install source is |kSystem|. DCHECK_EQ(web_app->IsSystemApp(), @@ -1606,6 +1605,29 @@ std::move(callback).Run(LaunchAppWithParams(std::move(params))); } +std::string WebAppPublisherHelper::GetPolicyId(const WebApp& web_app) { +#if BUILDFLAG(IS_CHROMEOS_ASH) + // File Manager SWA uses File Manager Extension's ID for policy. + if (chromeos::features::IsFileManagerSwaEnabled() && + web_app.app_id() == file_manager::kFileManagerSwaAppId) { + return file_manager::kFileManagerAppId; + } +#endif + + GURL install_url; + if (registrar().HasExternalAppWithInstallSource( + web_app.app_id(), ExternalInstallSource::kExternalPolicy)) { + std::map<AppId, GURL> installed_apps = + registrar().GetExternallyInstalledApps( + ExternalInstallSource::kExternalPolicy); + auto it = installed_apps.find(web_app.app_id()); + if (it != installed_apps.end()) { + install_url = it->second; + } + } + return install_url.spec(); +} + #if BUILDFLAG(IS_CHROMEOS) void WebAppPublisherHelper::UpdateAppDisabledMode(apps::App& app) { if (provider_->policy_manager().IsDisabledAppsModeHidden()) {
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.h b/chrome/browser/web_applications/app_service/web_app_publisher_helper.h index 7ab8162d..d9b19d1 100644 --- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.h +++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
@@ -5,22 +5,28 @@ #ifndef CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_WEB_APP_PUBLISHER_HELPER_H_ #define CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_WEB_APP_PUBLISHER_HELPER_H_ +#include <stdint.h> #include <map> #include <memory> #include <string> #include <vector> +#include "base/callback.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/scoped_observation.h" +#include "base/strings/string_piece_forward.h" #include "base/types/id_type.h" #include "build/build_config.h" +#include "build/buildflag.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/apps/app_service/app_icon/app_icon_factory.h" #include "chrome/browser/apps/app_service/app_icon/icon_key_util.h" -#include "chrome/browser/apps/app_service/app_launch_params.h" #include "chrome/browser/apps/app_service/paused_apps.h" #include "chrome/browser/web_applications/app_registrar_observer.h" +#include "chrome/browser/web_applications/web_app_constants.h" +#include "chrome/browser/web_applications/web_app_id.h" +#include "chrome/browser/web_applications/web_app_install_info.h" #include "chrome/browser/web_applications/web_app_install_manager.h" #include "chrome/browser/web_applications/web_app_install_manager_observer.h" #include "chrome/browser/web_applications/web_app_registrar.h" @@ -30,9 +36,13 @@ #include "components/services/app_service/public/cpp/app_types.h" #include "components/services/app_service/public/cpp/icon_types.h" #include "components/services/app_service/public/cpp/permission.h" +#include "components/services/app_service/public/cpp/run_on_os_login_types.h" #include "components/services/app_service/public/mojom/app_service.mojom.h" -#include "components/services/app_service/public/mojom/types.mojom.h" +#include "components/services/app_service/public/mojom/types.mojom-forward.h" +#include "components/services/app_service/public/mojom/types.mojom-shared.h" #include "components/webapps/browser/installable/installable_metrics.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h" #include "ui/gfx/native_widget_types.h" #if BUILDFLAG(IS_CHROMEOS) @@ -44,11 +54,24 @@ #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" #include "chrome/browser/notifications/notification_common.h" #include "chrome/browser/notifications/notification_display_service.h" +#include "content/public/browser/media_request_state.h" +#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h" #include "ui/message_center/public/cpp/notification.h" #endif +class ContentSettingsPattern; +class ContentSettingsTypeSet; +class GURL; class Profile; +namespace apps { +struct AppLaunchParams; +} + +namespace base { +class Time; +} + namespace content { class WebContents; } @@ -57,7 +80,6 @@ class WebApp; class WebAppProvider; -class WebAppRegistrar; class WebAppLaunchManager; struct ShortcutIdTypeMarker {}; @@ -360,6 +382,10 @@ int64_t display_id, base::OnceCallback<void(content::WebContents*)> callback); + // Get the identifier for the app that will be used in policy controls, such + // as force-installation and pinning. May be empty. + std::string GetPolicyId(const WebApp& web_app); + #if BUILDFLAG(IS_CHROMEOS) // Updates app visibility. void UpdateAppDisabledMode(apps::App& app);
diff --git a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc index c70166c..59696c6 100644 --- a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc +++ b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
@@ -29,7 +29,6 @@ #include "third_party/skia/include/core/SkColor.h" #include "ui/base/window_open_disposition.h" #include "ui/gfx/image/image_skia.h" -#include "ui/views/image_model_utils.h" #include "url/gurl.h" namespace web_app { @@ -135,17 +134,15 @@ controller->SetReadIconCallbackForTesting( base::BindLambdaForTesting([controller, &image_skia, &run_loop, this]() { EXPECT_TRUE(app_service_test().AreIconImageEqual( - image_skia, views::GetImageSkiaFromImageModel( - controller->GetWindowAppIcon(), nullptr))); + image_skia, controller->GetWindowAppIcon().Rasterize(nullptr))); run_loop.Quit(); })); run_loop.Run(); #else controller->SetReadIconCallbackForTesting( base::BindLambdaForTesting([controller, &run_loop]() { - const SkBitmap* bitmap = views::GetImageSkiaFromImageModel( - controller->GetWindowAppIcon(), nullptr) - .bitmap(); + const SkBitmap* bitmap = + controller->GetWindowAppIcon().Rasterize(nullptr).bitmap(); EXPECT_EQ(SK_ColorBLUE, bitmap->getColor(0, 0)); EXPECT_EQ(32, bitmap->width()); EXPECT_EQ(32, bitmap->height());
diff --git a/chrome/browser/win/chrome_elf_init.cc b/chrome/browser/win/chrome_elf_init.cc index 1f77232..e9c6998 100644 --- a/chrome/browser/win/chrome_elf_init.cc +++ b/chrome/browser/win/chrome_elf_init.cc
@@ -26,8 +26,8 @@ #include "content/public/common/content_features.h" #include "sandbox/policy/features.h" -const char kBrowserBlacklistTrialName[] = "BrowserBlacklist"; -const char kBrowserBlacklistTrialDisabledGroupName[] = "NoBlacklist"; +const char kBrowserBlocklistTrialName[] = "BrowserBlocklist"; +const char kBrowserBlocklistTrialDisabledGroupName[] = "NoBlocklist"; namespace { @@ -35,40 +35,40 @@ // Hence, // (a) existing enumerated constants should never be deleted or reordered, and // (b) new constants should only be appended in front of -// BLACKLIST_SETUP_EVENT_MAX. -enum BlacklistSetupEventType { - // The blacklist beacon has placed to enable the browser blacklisting. - BLACKLIST_SETUP_ENABLED = 0, +// BLOCKLIST_SETUP_EVENT_MAX. +enum BlocklistSetupEventType { + // The blocklist beacon has placed to enable the browser blocklisting. + BLOCKLIST_SETUP_ENABLED = 0, - // The blacklist was successfully enabled. - BLACKLIST_SETUP_RAN_SUCCESSFULLY, + // The blocklist was successfully enabled. + BLOCKLIST_SETUP_RAN_SUCCESSFULLY, - // The blacklist setup code failed to execute. - BLACKLIST_SETUP_FAILED, + // The blocklist setup code failed to execute. + BLOCKLIST_SETUP_FAILED, - // The blacklist thunk setup code failed. This is probably an indication + // The blocklist thunk setup code failed. This is probably an indication // that something else patched that code first. - BLACKLIST_THUNK_SETUP_FAILED, + BLOCKLIST_THUNK_SETUP_FAILED, - // Deprecated. The blacklist interception code failed to execute. - BLACKLIST_INTERCEPTION_FAILED, + // Deprecated. The blocklist interception code failed to execute. + BLOCKLIST_INTERCEPTION_FAILED, - // The blacklist was disabled for this run (after it failed too many times). - BLACKLIST_SETUP_DISABLED, + // The blocklist was disabled for this run (after it failed too many times). + BLOCKLIST_SETUP_DISABLED, // Always keep this at the end. - BLACKLIST_SETUP_EVENT_MAX, + BLOCKLIST_SETUP_EVENT_MAX, }; -void RecordBlacklistSetupEvent(BlacklistSetupEventType blacklist_setup_event) { +void RecordBlocklistSetupEvent(BlocklistSetupEventType blocklist_setup_event) { base::UmaHistogramEnumeration("ChromeElf.Beacon.SetupStatus", - blacklist_setup_event, - BLACKLIST_SETUP_EVENT_MAX); + blocklist_setup_event, + BLOCKLIST_SETUP_EVENT_MAX); } std::wstring GetBeaconRegistryPath() { return install_static::GetRegistryPath().append( - blacklist::kRegistryBeaconKeyName); + blocklist::kRegistryBeaconKeyName); } // This enum is used to define the buckets for an enumerated UMA histogram. @@ -119,13 +119,13 @@ } // namespace void InitializeChromeElf() { - if (base::FieldTrialList::FindFullName(kBrowserBlacklistTrialName) == - kBrowserBlacklistTrialDisabledGroupName) { - // Disable the blacklist for all future runs by removing the beacon. - base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER); - blacklist_registry_key.DeleteKey(GetBeaconRegistryPath().c_str()); + if (base::FieldTrialList::FindFullName(kBrowserBlocklistTrialName) == + kBrowserBlocklistTrialDisabledGroupName) { + // Disable the blocklist for all future runs by removing the beacon. + base::win::RegKey blocklist_registry_key(HKEY_CURRENT_USER); + blocklist_registry_key.DeleteKey(GetBeaconRegistryPath().c_str()); } else { - BrowserBlacklistBeaconSetup(); + BrowserBlocklistBeaconSetup(); } // Make sure the registry key we read earlier in startup @@ -157,69 +157,69 @@ } } -void BrowserBlacklistBeaconSetup() { - base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER, +void BrowserBlocklistBeaconSetup() { + base::win::RegKey blocklist_registry_key(HKEY_CURRENT_USER, GetBeaconRegistryPath().c_str(), KEY_QUERY_VALUE | KEY_SET_VALUE); // No point in trying to continue if the registry key isn't valid. - if (!blacklist_registry_key.Valid()) + if (!blocklist_registry_key.Valid()) return; - // Record the results of the last blacklist setup. - DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX; - blacklist_registry_key.ReadValueDW(blacklist::kBeaconState, &blacklist_state); + // Record the results of the last blocklist setup. + DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX; + blocklist_registry_key.ReadValueDW(blocklist::kBeaconState, &blocklist_state); - if (blacklist_state == blacklist::BLACKLIST_ENABLED) { - // The blacklist setup didn't crash, so we report if it was enabled or not. + if (blocklist_state == blocklist::BLOCKLIST_ENABLED) { + // The blocklist setup didn't crash, so we report if it was enabled or not. if (IsThirdPartyInitialized()) { - RecordBlacklistSetupEvent(BLACKLIST_SETUP_RAN_SUCCESSFULLY); + RecordBlocklistSetupEvent(BLOCKLIST_SETUP_RAN_SUCCESSFULLY); } else { - // The only way for the blacklist to be enabled, but not fully - // initialized is if the thunk setup failed. See blacklist.cc + // The only way for the blocklist to be enabled, but not fully + // initialized is if the thunk setup failed. See blocklist.cc // for more details. - RecordBlacklistSetupEvent(BLACKLIST_THUNK_SETUP_FAILED); + RecordBlocklistSetupEvent(BLOCKLIST_THUNK_SETUP_FAILED); } - // Regardless of if the blacklist was fully enabled or not, report how many + // Regardless of if the blocklist was fully enabled or not, report how many // times we had to try to set it up. DWORD attempt_count = 0; - blacklist_registry_key.ReadValueDW(blacklist::kBeaconAttemptCount, + blocklist_registry_key.ReadValueDW(blocklist::kBeaconAttemptCount, &attempt_count); base::UmaHistogramCounts100("ChromeElf.Beacon.RetryAttemptsBeforeSuccess", attempt_count); - } else if (blacklist_state == blacklist::BLACKLIST_SETUP_FAILED) { + } else if (blocklist_state == blocklist::BLOCKLIST_SETUP_FAILED) { // We can set the state to disabled without checking that the maximum number - // of attempts was exceeded because blacklist.cc has already done this. - RecordBlacklistSetupEvent(BLACKLIST_SETUP_FAILED); - blacklist_registry_key.WriteValue(blacklist::kBeaconState, - blacklist::BLACKLIST_DISABLED); - } else if (blacklist_state == blacklist::BLACKLIST_DISABLED) { - RecordBlacklistSetupEvent(BLACKLIST_SETUP_DISABLED); + // of attempts was exceeded because blocklist.cc has already done this. + RecordBlocklistSetupEvent(BLOCKLIST_SETUP_FAILED); + blocklist_registry_key.WriteValue(blocklist::kBeaconState, + blocklist::BLOCKLIST_DISABLED); + } else if (blocklist_state == blocklist::BLOCKLIST_DISABLED) { + RecordBlocklistSetupEvent(BLOCKLIST_SETUP_DISABLED); } - // Find the last recorded blacklist version. - std::wstring blacklist_version; - blacklist_registry_key.ReadValue(blacklist::kBeaconVersion, - &blacklist_version); + // Find the last recorded blocklist version. + std::wstring blocklist_version; + blocklist_registry_key.ReadValue(blocklist::kBeaconVersion, + &blocklist_version); - if (blacklist_version != TEXT(CHROME_VERSION_STRING)) { - // The blacklist hasn't been enabled for this version yet, so enable it + if (blocklist_version != TEXT(CHROME_VERSION_STRING)) { + // The blocklist hasn't been enabled for this version yet, so enable it // and reset the failure count to zero. - LONG set_version = blacklist_registry_key.WriteValue( - blacklist::kBeaconVersion, + LONG set_version = blocklist_registry_key.WriteValue( + blocklist::kBeaconVersion, TEXT(CHROME_VERSION_STRING)); - LONG set_state = blacklist_registry_key.WriteValue( - blacklist::kBeaconState, - blacklist::BLACKLIST_ENABLED); + LONG set_state = blocklist_registry_key.WriteValue( + blocklist::kBeaconState, + blocklist::BLOCKLIST_ENABLED); - blacklist_registry_key.WriteValue(blacklist::kBeaconAttemptCount, + blocklist_registry_key.WriteValue(blocklist::kBeaconAttemptCount, static_cast<DWORD>(0)); - // Only report the blacklist as getting setup when both registry writes - // succeed, since otherwise the blacklist wasn't properly setup. + // Only report the blocklist as getting setup when both registry writes + // succeed, since otherwise the blocklist wasn't properly setup. if (set_version == ERROR_SUCCESS && set_state == ERROR_SUCCESS) - RecordBlacklistSetupEvent(BLACKLIST_SETUP_ENABLED); + RecordBlocklistSetupEvent(BLOCKLIST_SETUP_ENABLED); } }
diff --git a/chrome/browser/win/chrome_elf_init.h b/chrome/browser/win/chrome_elf_init.h index f598828..19660b7e 100644 --- a/chrome/browser/win/chrome_elf_init.h +++ b/chrome/browser/win/chrome_elf_init.h
@@ -6,14 +6,14 @@ #define CHROME_BROWSER_WIN_CHROME_ELF_INIT_H_ // Field trial name and full name for the blacklist disabled group. -extern const char kBrowserBlacklistTrialName[]; -extern const char kBrowserBlacklistTrialDisabledGroupName[]; +extern const char kBrowserBlocklistTrialName[]; +extern const char kBrowserBlocklistTrialDisabledGroupName[]; // Prepare any initialization code for Chrome Elf's setup (This will generally // only affect future runs since Chrome Elf is already setup by this point). void InitializeChromeElf(); // Set the required state for an enabled browser blacklist. -void BrowserBlacklistBeaconSetup(); +void BrowserBlocklistBeaconSetup(); #endif // CHROME_BROWSER_WIN_CHROME_ELF_INIT_H_
diff --git a/chrome/browser/win/chrome_elf_init_unittest.cc b/chrome/browser/win/chrome_elf_init_unittest.cc index 35f101c..25490b3 100644 --- a/chrome/browser/win/chrome_elf_init_unittest.cc +++ b/chrome/browser/win/chrome_elf_init_unittest.cc
@@ -20,14 +20,14 @@ namespace { -class ChromeBlacklistTrialTest : public testing::Test { +class ChromeBlocklistTrialTest : public testing::Test { public: - ChromeBlacklistTrialTest(const ChromeBlacklistTrialTest&) = delete; - ChromeBlacklistTrialTest& operator=(const ChromeBlacklistTrialTest&) = delete; + ChromeBlocklistTrialTest(const ChromeBlocklistTrialTest&) = delete; + ChromeBlocklistTrialTest& operator=(const ChromeBlocklistTrialTest&) = delete; protected: - ChromeBlacklistTrialTest() {} - ~ChromeBlacklistTrialTest() override {} + ChromeBlocklistTrialTest() {} + ~ChromeBlocklistTrialTest() override {} void SetUp() override { testing::Test::SetUp(); @@ -35,125 +35,125 @@ ASSERT_NO_FATAL_FAILURE( override_manager_.OverrideRegistry(HKEY_CURRENT_USER)); - blacklist_registry_key_ = std::make_unique<base::win::RegKey>( + blocklist_registry_key_ = std::make_unique<base::win::RegKey>( HKEY_CURRENT_USER, install_static::GetRegistryPath() - .append(blacklist::kRegistryBeaconKeyName) + .append(blocklist::kRegistryBeaconKeyName) .c_str(), KEY_QUERY_VALUE | KEY_SET_VALUE); } - DWORD GetBlacklistState() { - DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX; - blacklist_registry_key_->ReadValueDW(blacklist::kBeaconState, - &blacklist_state); + DWORD GetBlocklistState() { + DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX; + blocklist_registry_key_->ReadValueDW(blocklist::kBeaconState, + &blocklist_state); - return blacklist_state; + return blocklist_state; } - std::wstring GetBlacklistVersion() { - std::wstring blacklist_version; - blacklist_registry_key_->ReadValue(blacklist::kBeaconVersion, - &blacklist_version); + std::wstring GetBlocklistVersion() { + std::wstring blocklist_version; + blocklist_registry_key_->ReadValue(blocklist::kBeaconVersion, + &blocklist_version); - return blacklist_version; + return blocklist_version; } - std::unique_ptr<base::win::RegKey> blacklist_registry_key_; + std::unique_ptr<base::win::RegKey> blocklist_registry_key_; registry_util::RegistryOverrideManager override_manager_; content::BrowserTaskEnvironment task_environment_; }; -// Ensure that the default trial sets up the blacklist beacons. -TEST_F(ChromeBlacklistTrialTest, DefaultRun) { +// Ensure that the default trial sets up the blocklist beacons. +TEST_F(ChromeBlocklistTrialTest, DefaultRun) { // Set some dummy values as beacons. - blacklist_registry_key_->WriteValue(blacklist::kBeaconState, - blacklist::BLACKLIST_DISABLED); - blacklist_registry_key_->WriteValue(blacklist::kBeaconVersion, L"Data"); + blocklist_registry_key_->WriteValue(blocklist::kBeaconState, + blocklist::BLOCKLIST_DISABLED); + blocklist_registry_key_->WriteValue(blocklist::kBeaconVersion, L"Data"); // This setup code should result in the default group, which should have - // the blacklist set up. + // the blocklist set up. InitializeChromeElf(); // Ensure the beacon values are now correct, indicating the - // blacklist beacon was setup. - ASSERT_EQ(static_cast<DWORD>(blacklist::BLACKLIST_ENABLED), - GetBlacklistState()); + // blocklist beacon was setup. + ASSERT_EQ(static_cast<DWORD>(blocklist::BLOCKLIST_ENABLED), + GetBlocklistState()); std::wstring version(base::UTF8ToWide(version_info::GetVersionNumber())); - ASSERT_EQ(version, GetBlacklistVersion()); + ASSERT_EQ(version, GetBlocklistVersion()); } -// Ensure that the blacklist is disabled for any users in the -// "BlacklistDisabled" finch group. -TEST_F(ChromeBlacklistTrialTest, BlacklistDisabledRun) { +// Ensure that the blocklist is disabled for any users in the +// "BlocklistDisabled" finch group. +TEST_F(ChromeBlocklistTrialTest, BlocklistDisabledRun) { // Set the beacons to enabled values. - blacklist_registry_key_->WriteValue(blacklist::kBeaconState, - blacklist::BLACKLIST_ENABLED); - blacklist_registry_key_->WriteValue(blacklist::kBeaconVersion, L"Data"); + blocklist_registry_key_->WriteValue(blocklist::kBeaconState, + blocklist::BLOCKLIST_ENABLED); + blocklist_registry_key_->WriteValue(blocklist::kBeaconVersion, L"Data"); scoped_refptr<base::FieldTrial> trial( base::FieldTrialList::CreateFieldTrial( - kBrowserBlacklistTrialName, kBrowserBlacklistTrialDisabledGroupName)); + kBrowserBlocklistTrialName, kBrowserBlocklistTrialDisabledGroupName)); - // This setup code should now delete any existing blacklist beacons. + // This setup code should now delete any existing blocklist beacons. InitializeChromeElf(); // Ensure invalid values are returned to indicate that the beacon // values are indeed gone. - ASSERT_EQ(static_cast<DWORD>(blacklist::BLACKLIST_STATE_MAX), - GetBlacklistState()); - ASSERT_EQ(std::wstring(), GetBlacklistVersion()); + ASSERT_EQ(static_cast<DWORD>(blocklist::BLOCKLIST_STATE_MAX), + GetBlocklistState()); + ASSERT_EQ(std::wstring(), GetBlocklistVersion()); } -TEST_F(ChromeBlacklistTrialTest, VerifyFirstRun) { - BrowserBlacklistBeaconSetup(); +TEST_F(ChromeBlocklistTrialTest, VerifyFirstRun) { + BrowserBlocklistBeaconSetup(); // Verify the state is properly set after the first run. - ASSERT_EQ(static_cast<DWORD>(blacklist::BLACKLIST_ENABLED), - GetBlacklistState()); + ASSERT_EQ(static_cast<DWORD>(blocklist::BLOCKLIST_ENABLED), + GetBlocklistState()); std::wstring version(base::UTF8ToWide(version_info::GetVersionNumber())); - ASSERT_EQ(version, GetBlacklistVersion()); + ASSERT_EQ(version, GetBlocklistVersion()); } -TEST_F(ChromeBlacklistTrialTest, BlacklistFailed) { - // Ensure when the blacklist set up failed we set the state to disabled for +TEST_F(ChromeBlocklistTrialTest, BlocklistFailed) { + // Ensure when the blocklist set up failed we set the state to disabled for // future runs. - blacklist_registry_key_->WriteValue(blacklist::kBeaconVersion, + blocklist_registry_key_->WriteValue(blocklist::kBeaconVersion, TEXT(CHROME_VERSION_STRING)); - blacklist_registry_key_->WriteValue(blacklist::kBeaconState, - blacklist::BLACKLIST_SETUP_FAILED); + blocklist_registry_key_->WriteValue(blocklist::kBeaconState, + blocklist::BLOCKLIST_SETUP_FAILED); - BrowserBlacklistBeaconSetup(); + BrowserBlocklistBeaconSetup(); - ASSERT_EQ(static_cast<DWORD>(blacklist::BLACKLIST_DISABLED), - GetBlacklistState()); + ASSERT_EQ(static_cast<DWORD>(blocklist::BLOCKLIST_DISABLED), + GetBlocklistState()); } -TEST_F(ChromeBlacklistTrialTest, VersionChanged) { - // Mark the blacklist as disabled for an older version, it should +TEST_F(ChromeBlocklistTrialTest, VersionChanged) { + // Mark the blocklist as disabled for an older version, it should // get enabled for this new version. Also record a non-zero number of // setup failures, which should be reset to zero. - blacklist_registry_key_->WriteValue(blacklist::kBeaconVersion, + blocklist_registry_key_->WriteValue(blocklist::kBeaconVersion, L"old_version"); - blacklist_registry_key_->WriteValue(blacklist::kBeaconState, - blacklist::BLACKLIST_DISABLED); - blacklist_registry_key_->WriteValue(blacklist::kBeaconAttemptCount, - blacklist::kBeaconMaxAttempts); + blocklist_registry_key_->WriteValue(blocklist::kBeaconState, + blocklist::BLOCKLIST_DISABLED); + blocklist_registry_key_->WriteValue(blocklist::kBeaconAttemptCount, + blocklist::kBeaconMaxAttempts); - BrowserBlacklistBeaconSetup(); + BrowserBlocklistBeaconSetup(); // The beacon should now be marked as enabled for the current version. - ASSERT_EQ(static_cast<DWORD>(blacklist::BLACKLIST_ENABLED), - GetBlacklistState()); + ASSERT_EQ(static_cast<DWORD>(blocklist::BLOCKLIST_ENABLED), + GetBlocklistState()); std::wstring expected_version( base::UTF8ToWide(version_info::GetVersionNumber())); - ASSERT_EQ(expected_version, GetBlacklistVersion()); + ASSERT_EQ(expected_version, GetBlocklistVersion()); // The counter should be reset. - DWORD attempt_count = blacklist::kBeaconMaxAttempts; - blacklist_registry_key_->ReadValueDW(blacklist::kBeaconAttemptCount, + DWORD attempt_count = blocklist::kBeaconMaxAttempts; + blocklist_registry_key_->ReadValueDW(blocklist::kBeaconAttemptCount, &attempt_count); ASSERT_EQ(static_cast<DWORD>(0), attempt_count); }
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index 222d329..7d7c79f 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1647539747-03593157d39498fa2c27de59ce8a69f2ad2dc181.profdata +chrome-linux-main-1647604379-4659adbcb89e3313f9b9bb5d6a35e7f4c78d7573.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 072a88e..d1af8a3 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1647518309-2f3a06193beb03804c4ef7e2b1de409100653ccf.profdata +chrome-mac-arm-main-1647604379-78700c750a15a2472b8201eff336583a43ad72ff.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index b9b1ccd0..da0b99d 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1647518309-3ba1408f248e578199edd78d33c40ef3480f390a.profdata +chrome-mac-main-1647604379-24bb3d5266972ce87702b39862f29f6a869a3062.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index d75b41d..c40c12e 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1647529177-0c0f4c7a803ac1e2ee193e2ed924338211e72623.profdata +chrome-win32-main-1647561516-884869d651bf583160193923673428f3dc81c1bc.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 4fd3b21a..a35249b 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1647529177-8d2c74d53e6f0c6ecd578ad9eab3ddf9d6e0bb1f.profdata +chrome-win64-main-1647561516-63c50e8a42320ad1820259d4ee943133030ba3e7.profdata
diff --git a/chrome/chrome_elf/chrome_elf_constants.cc b/chrome/chrome_elf/chrome_elf_constants.cc index 91ea5c4f..0f720ac8 100644 --- a/chrome/chrome_elf/chrome_elf_constants.cc +++ b/chrome/chrome_elf/chrome_elf_constants.cc
@@ -4,7 +4,7 @@ #include "chrome/chrome_elf/chrome_elf_constants.h" -namespace blacklist { +namespace blocklist { const wchar_t kRegistryBeaconKeyName[] = L"\\BLBeacon"; const wchar_t kBeaconVersion[] = L"version"; @@ -13,7 +13,7 @@ const DWORD kBeaconMaxAttempts = 2; -} // namespace blacklist +} // namespace blocklist namespace elf_sec {
diff --git a/chrome/chrome_elf/chrome_elf_constants.h b/chrome/chrome_elf/chrome_elf_constants.h index d10904b..c3e1311 100644 --- a/chrome/chrome_elf/chrome_elf_constants.h +++ b/chrome/chrome_elf/chrome_elf_constants.h
@@ -9,34 +9,34 @@ #include <windows.h> -namespace blacklist { +namespace blocklist { -// The name of the blacklist beacon registry key. +// The name of the blocklist beacon registry key. extern const wchar_t kRegistryBeaconKeyName[]; -// The properties for the blacklist beacon. +// The properties for the blocklist beacon. extern const wchar_t kBeaconVersion[]; extern const wchar_t kBeaconState[]; extern const wchar_t kBeaconAttemptCount[]; // The number of failures that can occur on startup with the beacon enabled -// before we give up and turn off the blacklist. +// before we give up and turn off the blocklist. extern const DWORD kBeaconMaxAttempts; -// The states for the blacklist setup code. -enum BlacklistState { - BLACKLIST_DISABLED = 0, - BLACKLIST_ENABLED, - // The blacklist setup code is running. If this is the state at startup, it +// The states for the blocklist setup code. +enum BlocklistState { + BLOCKLIST_DISABLED = 0, + BLOCKLIST_ENABLED, + // The blocklist setup code is running. If this is the state at startup, it // means the last setup crashed. - BLACKLIST_SETUP_RUNNING, + BLOCKLIST_SETUP_RUNNING, // If the last setup crashed, we reassign the state to failed. - BLACKLIST_SETUP_FAILED, + BLOCKLIST_SETUP_FAILED, // Always keep this at the end. - BLACKLIST_STATE_MAX, + BLOCKLIST_STATE_MAX, }; -} // namespace blacklist +} // namespace blocklist namespace elf_sec {
diff --git a/chrome/chrome_elf/third_party_dlls/beacon.cc b/chrome/chrome_elf/third_party_dlls/beacon.cc index 41a7bf11..0970103 100644 --- a/chrome/chrome_elf/third_party_dlls/beacon.cc +++ b/chrome/chrome_elf/third_party_dlls/beacon.cc
@@ -15,16 +15,16 @@ if (!nt::CreateRegKey(nt::HKCU, install_static::GetRegistryPath() - .append(blacklist::kRegistryBeaconKeyName) + .append(blocklist::kRegistryBeaconKeyName) .c_str(), KEY_QUERY_VALUE | KEY_SET_VALUE, &key_handle)) { return false; } - DWORD blocking_state = blacklist::BLACKLIST_STATE_MAX; - if (!nt::QueryRegValueDWORD(key_handle, blacklist::kBeaconState, + DWORD blocking_state = blocklist::BLOCKLIST_STATE_MAX; + if (!nt::QueryRegValueDWORD(key_handle, blocklist::kBeaconState, &blocking_state) || - blocking_state == blacklist::BLACKLIST_DISABLED) { + blocking_state == blocklist::BLOCKLIST_DISABLED) { nt::CloseRegKey(key_handle); return false; } @@ -32,30 +32,30 @@ // Handle attempt count. // Only return true if BL is enabled and succeeded on previous run. bool success = false; - if (blocking_state == blacklist::BLACKLIST_ENABLED) { + if (blocking_state == blocklist::BLOCKLIST_ENABLED) { // If the blocking was successfully initialized on the previous run, reset // the failure counter. Then update the beacon state. - if (nt::SetRegValueDWORD(key_handle, blacklist::kBeaconAttemptCount, + if (nt::SetRegValueDWORD(key_handle, blocklist::kBeaconAttemptCount, static_cast<DWORD>(0))) { - if (nt::SetRegValueDWORD(key_handle, blacklist::kBeaconState, - blacklist::BLACKLIST_SETUP_RUNNING)) + if (nt::SetRegValueDWORD(key_handle, blocklist::kBeaconState, + blocklist::BLOCKLIST_SETUP_RUNNING)) success = true; } } else { // Some part of the blocking setup failed last time. If this has occurred - // blacklist::kBeaconMaxAttempts times in a row, we switch the state to + // blocklist::kBeaconMaxAttempts times in a row, we switch the state to // failed and skip setting up the blocking. DWORD attempt_count = 0; - nt::QueryRegValueDWORD(key_handle, blacklist::kBeaconAttemptCount, + nt::QueryRegValueDWORD(key_handle, blocklist::kBeaconAttemptCount, &attempt_count); ++attempt_count; - nt::SetRegValueDWORD(key_handle, blacklist::kBeaconAttemptCount, + nt::SetRegValueDWORD(key_handle, blocklist::kBeaconAttemptCount, attempt_count); - if (attempt_count >= blacklist::kBeaconMaxAttempts) { - blocking_state = blacklist::BLACKLIST_SETUP_FAILED; - nt::SetRegValueDWORD(key_handle, blacklist::kBeaconState, blocking_state); + if (attempt_count >= blocklist::kBeaconMaxAttempts) { + blocking_state = blocklist::BLOCKLIST_SETUP_FAILED; + nt::SetRegValueDWORD(key_handle, blocklist::kBeaconState, blocking_state); } } @@ -68,14 +68,14 @@ if (!nt::CreateRegKey(nt::HKCU, install_static::GetRegistryPath() - .append(blacklist::kRegistryBeaconKeyName) + .append(blocklist::kRegistryBeaconKeyName) .c_str(), KEY_QUERY_VALUE | KEY_SET_VALUE, &key_handle)) { return false; } - DWORD blocking_state = blacklist::BLACKLIST_STATE_MAX; - if (!nt::QueryRegValueDWORD(key_handle, blacklist::kBeaconState, + DWORD blocking_state = blocklist::BLOCKLIST_STATE_MAX; + if (!nt::QueryRegValueDWORD(key_handle, blocklist::kBeaconState, &blocking_state)) { nt::CloseRegKey(key_handle); return false; @@ -84,9 +84,9 @@ // Reaching this point with the setup running state means the setup did not // crash, so we reset to enabled. Any other state indicates that setup was // skipped; in that case we leave the state alone for later recording. - if (blocking_state == blacklist::BLACKLIST_SETUP_RUNNING) { - if (!nt::SetRegValueDWORD(key_handle, blacklist::kBeaconState, - blacklist::BLACKLIST_ENABLED)) { + if (blocking_state == blocklist::BLOCKLIST_SETUP_RUNNING) { + if (!nt::SetRegValueDWORD(key_handle, blocklist::kBeaconState, + blocklist::BLOCKLIST_ENABLED)) { nt::CloseRegKey(key_handle); return false; }
diff --git a/chrome/chrome_elf/third_party_dlls/beacon_unittest.cc b/chrome/chrome_elf/third_party_dlls/beacon_unittest.cc index ee73e0d..4767087 100644 --- a/chrome/chrome_elf/third_party_dlls/beacon_unittest.cc +++ b/chrome/chrome_elf/third_party_dlls/beacon_unittest.cc
@@ -37,7 +37,7 @@ beacon_registry_key_ = std::make_unique<base::win::RegKey>( HKEY_CURRENT_USER, install_static::GetRegistryPath() - .append(blacklist::kRegistryBeaconKeyName) + .append(blocklist::kRegistryBeaconKeyName) .c_str(), KEY_QUERY_VALUE | KEY_SET_VALUE); } @@ -56,10 +56,10 @@ // Ensure that the beacon state starts off 'running' if a version is specified. TEST_F(BeaconTest, Beacon) { LONG result = beacon_registry_key_->WriteValue( - blacklist::kBeaconState, blacklist::BLACKLIST_SETUP_RUNNING); + blocklist::kBeaconState, blocklist::BLOCKLIST_SETUP_RUNNING); EXPECT_EQ(ERROR_SUCCESS, result); - result = beacon_registry_key_->WriteValue(blacklist::kBeaconVersion, + result = beacon_registry_key_->WriteValue(blocklist::kBeaconVersion, L"beacon_version"); EXPECT_EQ(ERROR_SUCCESS, result); @@ -73,76 +73,76 @@ void TestResetBeacon(std::unique_ptr<base::win::RegKey>& key, DWORD input_state, DWORD expected_output_state) { - LONG result = key->WriteValue(blacklist::kBeaconState, input_state); + LONG result = key->WriteValue(blocklist::kBeaconState, input_state); EXPECT_EQ(ERROR_SUCCESS, result); EXPECT_TRUE(ResetBeacon()); - DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX; - result = key->ReadValueDW(blacklist::kBeaconState, &blacklist_state); + DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX; + result = key->ReadValueDW(blocklist::kBeaconState, &blocklist_state); EXPECT_EQ(ERROR_SUCCESS, result); - EXPECT_EQ(expected_output_state, blacklist_state); + EXPECT_EQ(expected_output_state, blocklist_state); } TEST_F(BeaconTest, ResetBeacon) { // Ensure that ResetBeacon resets properly on successful runs and not on // failed or disabled runs. - TestResetBeacon(beacon_registry_key_, blacklist::BLACKLIST_SETUP_RUNNING, - blacklist::BLACKLIST_ENABLED); + TestResetBeacon(beacon_registry_key_, blocklist::BLOCKLIST_SETUP_RUNNING, + blocklist::BLOCKLIST_ENABLED); - TestResetBeacon(beacon_registry_key_, blacklist::BLACKLIST_SETUP_FAILED, - blacklist::BLACKLIST_SETUP_FAILED); + TestResetBeacon(beacon_registry_key_, blocklist::BLOCKLIST_SETUP_FAILED, + blocklist::BLOCKLIST_SETUP_FAILED); - TestResetBeacon(beacon_registry_key_, blacklist::BLACKLIST_DISABLED, - blacklist::BLACKLIST_DISABLED); + TestResetBeacon(beacon_registry_key_, blocklist::BLOCKLIST_DISABLED, + blocklist::BLOCKLIST_DISABLED); } TEST_F(BeaconTest, SetupFailed) { // Ensure that when the number of failed tries reaches the maximum allowed, - // the blacklist state is set to failed. + // the blocklist state is set to failed. LONG result = beacon_registry_key_->WriteValue( - blacklist::kBeaconState, blacklist::BLACKLIST_SETUP_RUNNING); + blocklist::kBeaconState, blocklist::BLOCKLIST_SETUP_RUNNING); EXPECT_EQ(ERROR_SUCCESS, result); - // Set the attempt count so that on the next failure the blacklist is + // Set the attempt count so that on the next failure the blocklist is // disabled. - result = beacon_registry_key_->WriteValue(blacklist::kBeaconAttemptCount, - blacklist::kBeaconMaxAttempts - 1); + result = beacon_registry_key_->WriteValue(blocklist::kBeaconAttemptCount, + blocklist::kBeaconMaxAttempts - 1); EXPECT_EQ(ERROR_SUCCESS, result); EXPECT_FALSE(LeaveSetupBeacon()); DWORD attempt_count = 0; - beacon_registry_key_->ReadValueDW(blacklist::kBeaconAttemptCount, + beacon_registry_key_->ReadValueDW(blocklist::kBeaconAttemptCount, &attempt_count); - EXPECT_EQ(attempt_count, blacklist::kBeaconMaxAttempts); + EXPECT_EQ(attempt_count, blocklist::kBeaconMaxAttempts); - DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX; - result = beacon_registry_key_->ReadValueDW(blacklist::kBeaconState, - &blacklist_state); + DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX; + result = beacon_registry_key_->ReadValueDW(blocklist::kBeaconState, + &blocklist_state); EXPECT_EQ(ERROR_SUCCESS, result); - EXPECT_EQ(blacklist_state, - static_cast<DWORD>(blacklist::BLACKLIST_SETUP_FAILED)); + EXPECT_EQ(blocklist_state, + static_cast<DWORD>(blocklist::BLOCKLIST_SETUP_FAILED)); } TEST_F(BeaconTest, SetupSucceeded) { // Starting with the enabled beacon should result in the setup running state // and the attempt counter reset to zero. - LONG result = beacon_registry_key_->WriteValue(blacklist::kBeaconState, - blacklist::BLACKLIST_ENABLED); + LONG result = beacon_registry_key_->WriteValue(blocklist::kBeaconState, + blocklist::BLOCKLIST_ENABLED); EXPECT_EQ(ERROR_SUCCESS, result); - result = beacon_registry_key_->WriteValue(blacklist::kBeaconAttemptCount, - blacklist::kBeaconMaxAttempts); + result = beacon_registry_key_->WriteValue(blocklist::kBeaconAttemptCount, + blocklist::kBeaconMaxAttempts); EXPECT_EQ(ERROR_SUCCESS, result); EXPECT_TRUE(LeaveSetupBeacon()); - DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX; - beacon_registry_key_->ReadValueDW(blacklist::kBeaconState, &blacklist_state); - EXPECT_EQ(blacklist_state, - static_cast<DWORD>(blacklist::BLACKLIST_SETUP_RUNNING)); + DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX; + beacon_registry_key_->ReadValueDW(blocklist::kBeaconState, &blocklist_state); + EXPECT_EQ(blocklist_state, + static_cast<DWORD>(blocklist::BLOCKLIST_SETUP_RUNNING)); - DWORD attempt_count = blacklist::kBeaconMaxAttempts; - beacon_registry_key_->ReadValueDW(blacklist::kBeaconAttemptCount, + DWORD attempt_count = blocklist::kBeaconMaxAttempts; + beacon_registry_key_->ReadValueDW(blocklist::kBeaconAttemptCount, &attempt_count); EXPECT_EQ(static_cast<DWORD>(0), attempt_count); }
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index b3f10c3..2ce4816f 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -639,11 +639,6 @@ base::FEATURE_DISABLED_BY_DEFAULT}; #endif -#if !BUILDFLAG(IS_ANDROID) -const base::Feature kLinkCapturingUiUpdate{"LinkCapturingUiUpdate", - base::FEATURE_DISABLED_BY_DEFAULT}; -#endif - #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) COMPONENT_EXPORT(CHROME_FEATURES) const base::Feature kLinuxLowMemoryMonitor{"LinuxLowMemoryMonitor",
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 6baaf07..402b942a 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -440,11 +440,6 @@ extern const base::Feature kKernelnextVMs; #endif -#if !BUILDFLAG(IS_ANDROID) -COMPONENT_EXPORT(CHROME_FEATURES) -extern const base::Feature kLinkCapturingUiUpdate; -#endif - #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kLinuxLowMemoryMonitor;
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl index 0ca172f..c352572 100644 --- a/chrome/common/extensions/api/file_manager_private.idl +++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -49,7 +49,7 @@ // Type of the mounted volume. enum VolumeType { drive, downloads, removable, archive, provided, mtp, media_view, crostini, android_files, documents_provider, - testing, smb, system_internal }; + testing, smb, system_internal, guest_os }; // Device type. Available if this is removable volume. enum DeviceType { usb, sd, optical, mobile, unknown };
diff --git a/chrome/common/extensions/api/scripting.idl b/chrome/common/extensions/api/scripting.idl index 550a519..e0e3532 100644 --- a/chrome/common/extensions/api/scripting.idl +++ b/chrome/common/extensions/api/scripting.idl
@@ -31,6 +31,11 @@ // of specific frames to inject into. long[]? frameIds; + // The <a href="https://developer.chrome.com/extensions/webNavigation#document_ids">IDs</a> + // of specific documentIds to inject into. This must not be set if + // <code>frameIds</code> is set. + [nodoc] DOMString[]? documentIds; + // Whether the script should inject into all frames within the tab. Defaults // to false. // This must not be true if <code>frameIds</code> is specified. @@ -96,6 +101,9 @@ // The frame associated with the injection. long frameId; + + // The document associated with the injection. + [nodoc] DOMString documentId; }; // Describes a content script to be injected into a web page registered
diff --git a/chrome/common/media/cdm_registration.cc b/chrome/common/media/cdm_registration.cc index 2fd0dc6..7a1d144 100644 --- a/chrome/common/media/cdm_registration.cc +++ b/chrome/common/media/cdm_registration.cc
@@ -32,10 +32,14 @@ #include "base/no_destructor.h" #include "components/cdm/common/cdm_manifest.h" #include "media/cdm/supported_audio_codecs.h" -// TODO(crbug.com/663554): Needed for WIDEVINE_CDM_VERSION_STRING. Support -// component updated CDM on all desktop platforms and remove this. -// This file is In SHARED_INTERMEDIATE_DIR. +// Needed for WIDEVINE_CDM_MIN_GLIBC_VERSION. This file is in +// SHARED_INTERMEDIATE_DIR. #include "widevine_cdm_version.h" // nogncheck +// The following must be after widevine_cdm_version.h. +#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) +#include <gnu/libc-version.h> +#include "base/version.h" +#endif // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) #if !BUILDFLAG(IS_CHROMEOS_ASH) #include "chrome/common/media/component_widevine_cdm_hint_file_linux.h" #endif // !BUILDFLAG(IS_CHROMEOS_ASH) @@ -138,6 +142,15 @@ void AddSoftwareSecureWidevine(std::vector<content::CdmInfo>* cdms) { #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) + base::Version glibc_version(gnu_get_libc_version()); + DCHECK(glibc_version.IsValid()); + if (glibc_version < base::Version(WIDEVINE_CDM_MIN_GLIBC_VERSION)) { + LOG(WARNING) << "Widevine not registered because glibc version is too low"; + return; + } +#endif // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) + // The Widevine CDM on Linux needs to be registered (and loaded) before the // zygote is locked down. The CDM can be found from the version bundled with // Chrome (if BUNDLE_WIDEVINE_CDM = true) and/or the version downloaded by
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc index 312ca326..9d36967 100644 --- a/chrome/common/webui_url_constants.cc +++ b/chrome/common/webui_url_constants.cc
@@ -245,6 +245,7 @@ const char kChromeUIReadLaterHost[] = "read-later.top-chrome"; const char kChromeUIReadLaterURL[] = "chrome://read-later.top-chrome/"; const char kChromeUIWebAppInternalsHost[] = "web-app-internals"; +const char kChromeUIWebUITestHost[] = "webui-test"; #endif #if BUILDFLAG(PLATFORM_CFM)
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h index 54dc618..f6d491d 100644 --- a/chrome/common/webui_url_constants.h +++ b/chrome/common/webui_url_constants.h
@@ -233,6 +233,7 @@ extern const char kChromeUIReadLaterHost[]; extern const char kChromeUIReadLaterURL[]; extern const char kChromeUIWebAppInternalsHost[]; +extern const char kChromeUIWebUITestHost[]; #endif // BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/installer/linux/BUILD.gn b/chrome/installer/linux/BUILD.gn index d364d2d..6fc55684 100644 --- a/chrome/installer/linux/BUILD.gn +++ b/chrome/installer/linux/BUILD.gn
@@ -275,6 +275,7 @@ "common/postinst.include", "common/prerm.include", "common/repo.cron", + "common/repo_variables.include", "common/rpm.include", "common/rpmrepo.cron", "common/symlinks.include",
diff --git a/chrome/installer/linux/common/apt.include b/chrome/installer/linux/common/apt.include index a9bfd76..5855311 100644 --- a/chrome/installer/linux/common/apt.include +++ b/chrome/installer/linux/common/apt.include
@@ -1,4 +1,4 @@ -@@include@@variables.include +@@include@@repo_variables.include APT_GET="`command -v apt-get 2> /dev/null`" APT_CONFIG="`command -v apt-config 2> /dev/null`"
diff --git a/chrome/installer/linux/common/postinst.include b/chrome/installer/linux/common/postinst.include index 3a35f74..4e3023e 100644 --- a/chrome/installer/linux/common/postinst.include +++ b/chrome/installer/linux/common/postinst.include
@@ -22,9 +22,9 @@ update_defaults_list() { # $1: name of the .desktop file - local DEFAULTS_FILE="/usr/share/applications/defaults.list" + local DEFAULTS_LIST="/usr/share/applications/defaults.list" - if [ ! -f "${DEFAULTS_FILE}" ]; then + if [ ! -f "${DEFAULTS_LIST}" ]; then return fi @@ -35,17 +35,17 @@ cut -d '=' -f 2- | tr ';' ' ')" for mime_type in ${mime_types}; do - if egrep -q "^${mime_type}=" "${DEFAULTS_FILE}"; then - if ! egrep -q "^${mime_type}=.*${1}" "${DEFAULTS_FILE}"; then - default_apps="$(grep ${mime_type}= "${DEFAULTS_FILE}" | + if egrep -q "^${mime_type}=" "${DEFAULTS_LIST}"; then + if ! egrep -q "^${mime_type}=.*${1}" "${DEFAULTS_LIST}"; then + default_apps="$(grep ${mime_type}= "${DEFAULTS_LIST}" | cut -d '=' -f 2-)" - egrep -v "^${mime_type}=" "${DEFAULTS_FILE}" > "${DEFAULTS_FILE}.new" - echo "${mime_type}=${default_apps};${1}" >> "${DEFAULTS_FILE}.new" - mv "${DEFAULTS_FILE}.new" "${DEFAULTS_FILE}" + egrep -v "^${mime_type}=" "${DEFAULTS_LIST}" > "${DEFAULTS_LIST}.new" + echo "${mime_type}=${default_apps};${1}" >> "${DEFAULTS_LIST}.new" + mv "${DEFAULTS_LIST}.new" "${DEFAULTS_LIST}" fi else # If there's no mention of the mime type in the file, add it. - echo "${mime_type}=${1};" >> "${DEFAULTS_FILE}" + echo "${mime_type}=${1};" >> "${DEFAULTS_LIST}" fi done }
diff --git a/chrome/installer/linux/common/repo.cron b/chrome/installer/linux/common/repo.cron index 2750e49..59404018 100755 --- a/chrome/installer/linux/common/repo.cron +++ b/chrome/installer/linux/common/repo.cron
@@ -17,10 +17,11 @@ # "false" as desired. An empty $DEFAULTS_FILE is the same as setting both values # to "false". +@@include@@../common/variables.include + @@include@@apt.include ## MAIN ## -DEFAULTS_FILE="/etc/default/@@PACKAGE@@" if [ -r "$DEFAULTS_FILE" ]; then . "$DEFAULTS_FILE" fi
diff --git a/chrome/installer/linux/common/repo_variables.include b/chrome/installer/linux/common/repo_variables.include new file mode 100644 index 0000000..06b20b7e --- /dev/null +++ b/chrome/installer/linux/common/repo_variables.include
@@ -0,0 +1,3 @@ +# sources.list setting for @@PACKAGE@@ updates. +REPOCONFIG="@@REPOCONFIG@@" +REPOCONFIGREGEX="@@REPOCONFIGREGEX@@"
diff --git a/chrome/installer/linux/common/rpm.include b/chrome/installer/linux/common/rpm.include index 4931afe..3b959f99 100644 --- a/chrome/installer/linux/common/rpm.include +++ b/chrome/installer/linux/common/rpm.include
@@ -1,4 +1,4 @@ -@@include@@variables.include +@@include@@repo_variables.include # Install the repository signing key (see also: # https://www.google.com/linuxrepositories/)
diff --git a/chrome/installer/linux/common/rpmrepo.cron b/chrome/installer/linux/common/rpmrepo.cron index 1a9344f..778f43a 100755 --- a/chrome/installer/linux/common/rpmrepo.cron +++ b/chrome/installer/linux/common/rpmrepo.cron
@@ -14,10 +14,11 @@ # setting "repo_add_once" to "true" or "false" as desired. An empty # $DEFAULTS_FILE is the same as setting the value to "false". +@@include@@../common/variables.include + @@include@@rpm.include ## MAIN ## -DEFAULTS_FILE="/etc/default/@@PACKAGE@@" if [ -r "$DEFAULTS_FILE" ]; then . "$DEFAULTS_FILE" fi
diff --git a/chrome/installer/linux/common/variables.include b/chrome/installer/linux/common/variables.include index be94314..5533249 100644 --- a/chrome/installer/linux/common/variables.include +++ b/chrome/installer/linux/common/variables.include
@@ -1,6 +1,2 @@ # System-wide package configuration. DEFAULTS_FILE="/etc/default/@@PACKAGE@@" - -# sources.list setting for @@PACKAGE@@ updates. -REPOCONFIG="@@REPOCONFIG@@" -REPOCONFIGREGEX="@@REPOCONFIGREGEX@@"
diff --git a/chrome/installer/linux/debian/postinst b/chrome/installer/linux/debian/postinst index b284f042..34c6c20 100755 --- a/chrome/installer/linux/debian/postinst +++ b/chrome/installer/linux/debian/postinst
@@ -6,6 +6,8 @@ set -e +@@include@@../common/variables.include + @@include@@../common/postinst.include # Add to the alternatives system
diff --git a/chrome/installer/linux/debian/postrm b/chrome/installer/linux/debian/postrm index c55d98e..705bb9f 100755 --- a/chrome/installer/linux/debian/postrm +++ b/chrome/installer/linux/debian/postrm
@@ -13,6 +13,8 @@ exit 0 fi +@@include@@../common/variables.include + @@include@@../common/apt.include @@include@@../common/symlinks.include
diff --git a/chrome/installer/linux/rpm/chrome.spec.template b/chrome/installer/linux/rpm/chrome.spec.template index c755e4c..e968b5f 100644 --- a/chrome/installer/linux/rpm/chrome.spec.template +++ b/chrome/installer/linux/rpm/chrome.spec.template
@@ -111,6 +111,8 @@ #------------------------------------------------------------------------------ %post +@@include@@../common/variables.include + @@include@@../common/postinst.include @@include@@../common/rpm.include @@ -122,7 +124,6 @@ remove_udev_symlinks -DEFAULTS_FILE="/etc/default/@@PACKAGE@@" if [ ! -e "$DEFAULTS_FILE" ]; then echo 'repo_add_once="true"' > "$DEFAULTS_FILE" fi
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc index 51e6723a..d92390e 100644 --- a/chrome/installer/setup/uninstall.cc +++ b/chrome/installer/setup/uninstall.cc
@@ -523,21 +523,21 @@ base::BindRepeating(&DeleteUserRegistryKeys, base::Unretained(&paths))); } -// Removes the persistent blacklist state for the current user. Note: this will +// Removes the persistent blocklist state for the current user. Note: this will // not remove the state for users other than the one uninstalling Chrome on a // system-level install (http://crbug.com/388725). Doing so would require // extracting the per-user registry hive iteration from // UninstallActiveSetupEntries so that it could service multiple tasks. -void RemoveBlacklistState() { +void RemoveBlocklistState() { DeleteRegistryKey(HKEY_CURRENT_USER, install_static::GetRegistryPath().append( - blacklist::kRegistryBeaconKeyName), + blocklist::kRegistryBeaconKeyName), 0); // wow64_access } // Removes the browser's persistent state in the Windows registry for the // current user. Note: this will not remove the state for users other than the -// one uninstalling Chrome on a system-level install; see RemoveBlacklistState +// one uninstalling Chrome on a system-level install; see RemoveBlocklistState // for details. void RemoveDistributionRegistryState() { // Delete the contents of the distribution key except for those parts used by @@ -951,7 +951,7 @@ UninstallFirewallRules(chrome_exe); - RemoveBlacklistState(); + RemoveBlocklistState(); // Notify the shell that associations have changed since Chrome was likely // unregistered.
diff --git a/chrome/installer/util/google_update_util.cc b/chrome/installer/util/google_update_util.cc index fa1d369..063d81c 100644 --- a/chrome/installer/util/google_update_util.cc +++ b/chrome/installer/util/google_update_util.cc
@@ -89,10 +89,12 @@ base::LaunchOptions launch_options; launch_options.force_breakaway_from_job_ = true; - if (base::win::UserAccountControlIsEnabled()) + if (base::win::UserAccountControlIsEnabled()) { + launch_options.elevated = true; base::LaunchElevatedProcess(cmd, launch_options); - else + } else { base::LaunchProcess(cmd, launch_options); + } } } // namespace google_update
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn index ef8b021a..1528c9f 100644 --- a/chrome/renderer/BUILD.gn +++ b/chrome/renderer/BUILD.gn
@@ -69,8 +69,6 @@ "loadtimes_extension_bindings.h", "media/chrome_key_systems.cc", "media/chrome_key_systems.h", - "media/chrome_key_systems_provider.cc", - "media/chrome_key_systems_provider.h", "media/flash_embed_rewrite.cc", "media/flash_embed_rewrite.h", "media/media_feeds.cc", @@ -372,8 +370,6 @@ sources += [ "cart/commerce_hint_agent.cc", "cart/commerce_hint_agent.h", - "cart/commerce_renderer_feature_list.cc", - "cart/commerce_renderer_feature_list.h", "media/chrome_speech_recognition_client.cc", "media/chrome_speech_recognition_client.h", "searchbox/searchbox.cc", @@ -385,6 +381,7 @@ deps += [ "//chrome/common/cart:mojo_bindings", "//components/commerce/core:commerce_heuristics_data", + "//components/commerce/core:feature_list", "//components/crx_file", "//components/search:search", ]
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS index 96b7dcbb..90a20da 100644 --- a/chrome/renderer/DEPS +++ b/chrome/renderer/DEPS
@@ -51,6 +51,7 @@ "+components/printing/renderer", "+components/search/ntp_features.h", "+components/commerce/core/commerce_heuristics_data.h", + "+components/commerce/core/commerce_feature_list.h", "+components/safe_browsing/buildflags.h", "+components/safe_browsing/content/renderer", "+components/safe_browsing/content/common",
diff --git a/chrome/renderer/cart/commerce_hint_agent.cc b/chrome/renderer/cart/commerce_hint_agent.cc index 26ab21c..0382bdca 100644 --- a/chrome/renderer/cart/commerce_hint_agent.cc +++ b/chrome/renderer/cart/commerce_hint_agent.cc
@@ -16,7 +16,7 @@ #include "base/values.h" #include "chrome/common/chrome_isolated_world_ids.h" #include "chrome/grit/renderer_resources.h" -#include "chrome/renderer/cart/commerce_renderer_feature_list.h" +#include "components/commerce/core/commerce_feature_list.h" #include "components/commerce/core/commerce_heuristics_data.h" #include "components/search/ntp_features.h" #include "content/public/renderer/render_frame.h" @@ -175,8 +175,7 @@ ""}; constexpr base::FeatureParam<std::string> kCouponProductIdPatternMapping{ - &commerce_renderer_feature::kRetailCoupons, - "coupon-product-id-pattern-mapping", + &commerce::kRetailCoupons, "coupon-product-id-pattern-mapping", // Empty JSON string. ""}; @@ -318,6 +317,13 @@ } const re2::RE2& GetAddToCartPattern() { + auto* pattern_from_component = + commerce_heuristics::CommerceHeuristicsData::GetInstance() + .GetAddToCartRequestPattern(); + if (pattern_from_component && + kAddToCartPattern.Get() == kAddToCartPattern.default_value) { + return *pattern_from_component; + } re2::RE2::Options options; options.set_case_sensitive(false); static base::NoDestructor<re2::RE2> instance(kAddToCartPattern.Get(), @@ -353,6 +359,13 @@ options.set_case_sensitive(false); const std::string& domain = eTLDPlusOne(url); if (heuristic_string_map->find(domain) == heuristic_string_map->end()) { + auto* pattern_from_component = + commerce_heuristics::CommerceHeuristicsData::GetInstance() + .GetCartPageURLPattern(); + if (pattern_from_component && + kCartPattern.Get() == kCartPattern.default_value) { + return *pattern_from_component; + } static base::NoDestructor<re2::RE2> instance(kCartPattern.Get(), options); return *instance; } @@ -366,6 +379,13 @@ // TODO(crbug/1164236): cover more shopping sites. const re2::RE2& GetVisitCheckoutPattern() { + auto* pattern_from_component = + commerce_heuristics::CommerceHeuristicsData::GetInstance() + .GetCheckoutPageURLPattern(); + if (pattern_from_component && + kCheckoutPattern.Get() == kCheckoutPattern.default_value) { + return *pattern_from_component; + } re2::RE2::Options options; options.set_case_sensitive(false); static base::NoDestructor<re2::RE2> instance(kCheckoutPattern.Get(), options); @@ -391,6 +411,13 @@ // TODO(crbug/1164236): need i18n. const re2::RE2& GetPurchaseTextPattern() { + auto* pattern_from_component = + commerce_heuristics::CommerceHeuristicsData::GetInstance() + .GetPurchaseButtonTextPattern(); + if (pattern_from_component && + kPurchaseButtonPattern.Get() == kPurchaseButtonPattern.default_value) { + return *pattern_from_component; + } re2::RE2::Options options; options.set_case_sensitive(false); static base::NoDestructor<re2::RE2> instance(kPurchaseButtonPattern.Get(), @@ -517,7 +544,7 @@ } if (is_add_to_cart) { std::string url_product_id; - if (commerce_renderer_feature::IsPartnerMerchant(navigation_url)) { + if (commerce::IsPartnerMerchant(navigation_url)) { GetProductIdFromRequest(url.spec().substr(0, kLengthLimit), &url_product_id); } @@ -577,7 +604,7 @@ if (CommerceHintAgent::IsAddToCart(str, skip_length_limit)) { std::string product_id; - if (commerce_renderer_feature::IsPartnerMerchant(url)) { + if (commerce::IsPartnerMerchant(url)) { GetProductIdFromRequest(str.substr(0, kLengthLimit), &product_id); } RecordCommerceEvent(CommerceEvent::kAddToCartByForm); @@ -862,7 +889,7 @@ // that the cart is not loaded. if (!extracted_products->is_list()) return; - bool is_partner = commerce_renderer_feature::IsPartnerMerchant( + bool is_partner = commerce::IsPartnerMerchant( GURL(render_frame()->GetWebFrame()->GetDocument().Url())); std::vector<mojom::ProductPtr> products; for (const auto& product : extracted_products->GetListDeprecated()) { @@ -939,7 +966,7 @@ void CommerceHintAgent::OnNavigation(const GURL& url, OnNavigationCallback callback) { - if (!commerce_renderer_feature::kOptimizeRendererSignal.Get()) { + if (!commerce::kOptimizeRendererSignal.Get()) { std::move(callback).Run(false); return; }
diff --git a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc index dc2e5d3..9870b8f 100644 --- a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc +++ b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
@@ -16,8 +16,8 @@ #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/common/chrome_features.h" #include "chrome/common/pref_names.h" -#include "chrome/renderer/cart/commerce_renderer_feature_list.h" #include "chrome/test/base/chrome_test_utils.h" +#include "components/commerce/core/commerce_feature_list.h" #include "components/metrics/content/subprocess_metrics_provider.h" #include "components/network_session_configurator/common/network_switches.h" #include "components/optimization_guide/core/optimization_guide_features.h" @@ -698,7 +698,7 @@ )###"}, // Extend timeout to avoid flakiness. {"cart-extraction-timeout", "1m"}}}, - {commerce_renderer_feature::kRetailCoupons, + {commerce::kRetailCoupons, {{"coupon-partner-merchant-pattern", "(eee.com)"}, {"coupon-product-id-pattern-mapping", R"###(
diff --git a/chrome/renderer/cart/commerce_hint_agent_unittest.cc b/chrome/renderer/cart/commerce_hint_agent_unittest.cc index 7a23dfc..3ed0b98 100644 --- a/chrome/renderer/cart/commerce_hint_agent_unittest.cc +++ b/chrome/renderer/cart/commerce_hint_agent_unittest.cc
@@ -776,16 +776,47 @@ using cart::CommerceHintAgent; -TEST(CommerceHintAgentTest, IsAddToCart) { +class CommerceHintAgentUnitTest : public testing::Test { + public: + void TearDown() override { + // Clear out heuristics data that is set up during testing. + commerce_heuristics::CommerceHeuristicsData::GetInstance() + .PopulateDataFromComponent( + /*hint_json_data=*/"{}", /*global_json_data=*/"{}", + /*product_id_json_data=*/"{}", /*cart_extraction_script=*/""); + } +}; + +TEST_F(CommerceHintAgentUnitTest, IsAddToCart) { + // Heuristics from feature param default value. for (auto* str : kAddToCart) { EXPECT_TRUE(CommerceHintAgent::IsAddToCart(str)) << str; } for (auto* str : kNotAddToCart) { EXPECT_FALSE(CommerceHintAgent::IsAddToCart(str)) << str; } + + // Heuristics from component. + const std::string& component_pattern = R"###( + { + "add_to_cart_request_regex": "bar" + } + )###"; + EXPECT_TRUE(commerce_heuristics::CommerceHeuristicsData::GetInstance() + .PopulateDataFromComponent("{}", component_pattern, "", "")); + EXPECT_TRUE(CommerceHintAgent::IsAddToCart("request_bar")); + for (auto* str : kAddToCart) { + EXPECT_FALSE(CommerceHintAgent::IsAddToCart(str)) << str; + } + + // Feature param has a higher priority. + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeatureWithParameters( + ntp_features::kNtpChromeCartModule, {{"add-to-cart-pattern", "foo"}}); + EXPECT_FALSE(CommerceHintAgent::IsAddToCart("request_bar")); } -TEST(CommerceHintAgentTest, IsAddToCart_SkipLengthLimit) { +TEST_F(CommerceHintAgentUnitTest, IsAddToCart_SkipLengthLimit) { std::string str = "a"; for (int i = 0; i < 12; ++i) { str += str; @@ -798,25 +829,60 @@ EXPECT_TRUE(CommerceHintAgent::IsAddToCart(str, true)); } -TEST(CommerceHintAgentTest, IsVisitCart) { +TEST_F(CommerceHintAgentUnitTest, IsVisitCart) { + // Heuristics from feature param default value. for (auto* str : kVisitCart) { EXPECT_TRUE(CommerceHintAgent::IsVisitCart(GURL(str))) << str; } for (auto* str : kNotVisitCart) { EXPECT_FALSE(CommerceHintAgent::IsVisitCart(GURL(str))) << str; } + + // Heuristics from component. + const std::string& component_pattern = R"###( + { + "cart_page_url_regex": "bar" + } + )###"; + EXPECT_TRUE(commerce_heuristics::CommerceHeuristicsData::GetInstance() + .PopulateDataFromComponent("{}", component_pattern, "", "")); + EXPECT_TRUE(CommerceHintAgent::IsVisitCart(GURL("https://wwww.bar.com"))); + + // Feature param has a higher priority. + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeatureWithParameters( + ntp_features::kNtpChromeCartModule, {{"cart-pattern", "foo"}}); + EXPECT_FALSE(CommerceHintAgent::IsVisitCart(GURL("https://wwww.bar.com"))); } -TEST(CommerceHintAgentTest, IsVisitCheckout) { +TEST_F(CommerceHintAgentUnitTest, IsVisitCheckout) { + // Heuristics from feature param default value. for (auto* str : kVisitCheckout) { EXPECT_TRUE(CommerceHintAgent::IsVisitCheckout(GURL(str))) << str; } for (auto* str : kNotVisitCheckout) { EXPECT_FALSE(CommerceHintAgent::IsVisitCheckout(GURL(str))) << str; } + + // Heuristics from component. + const std::string& component_pattern = R"###( + { + "checkout_page_url_regex": "bar" + } + )###"; + EXPECT_TRUE(commerce_heuristics::CommerceHeuristicsData::GetInstance() + .PopulateDataFromComponent("{}", component_pattern, "", "")); + EXPECT_TRUE(CommerceHintAgent::IsVisitCheckout(GURL("https://wwww.bar.com"))); + + // Feature param has a higher priority. + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeatureWithParameters( + ntp_features::kNtpChromeCartModule, {{"checkout-pattern", "foo"}}); + EXPECT_FALSE( + CommerceHintAgent::IsVisitCheckout(GURL("https://wwww.bar.com"))); } -TEST(CommerceHintAgentTest, IsPurchaseByURL) { +TEST_F(CommerceHintAgentUnitTest, IsPurchaseByURL) { for (auto* str : kPurchaseURL) { EXPECT_TRUE(CommerceHintAgent::IsPurchase(GURL(str))) << str; } @@ -825,16 +891,36 @@ } } -TEST(CommerceHintAgentTest, IsPurchaseByForm) { +TEST_F(CommerceHintAgentUnitTest, IsPurchaseByForm) { + // Heuristics from feature param default value. for (auto* str : kPurchaseText) { EXPECT_TRUE(CommerceHintAgent::IsPurchase(GURL(), str)) << str; } for (auto* str : kNotPurchaseText) { EXPECT_FALSE(CommerceHintAgent::IsPurchase(GURL(), str)) << str; } + + // Heuristics from component. + const std::string& component_pattern = R"###( + { + "purchase_button_text_regex": "bar" + } + )###"; + EXPECT_TRUE(commerce_heuristics::CommerceHeuristicsData::GetInstance() + .PopulateDataFromComponent("{}", component_pattern, "", "")); + EXPECT_TRUE(CommerceHintAgent::IsPurchase(GURL(), "bar")); + for (auto* str : kPurchaseText) { + EXPECT_FALSE(CommerceHintAgent::IsPurchase(GURL(), str)) << str; + } + + // Feature param has a higher priority. + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeatureWithParameters( + ntp_features::kNtpChromeCartModule, {{"purchase-button-pattern", "foo"}}); + EXPECT_FALSE(CommerceHintAgent::IsPurchase(GURL(), "bar")); } -TEST(CommerceHintAgentTest, ShouldSkipFromFeatureParam) { +TEST_F(CommerceHintAgentUnitTest, ShouldSkipFromFeatureParam) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeatureWithParameters( ntp_features::kNtpChromeCartModule, kSkipParams); @@ -847,7 +933,7 @@ } } -TEST(CommerceHintAgentTest, ShouldSkipFromComponent) { +TEST_F(CommerceHintAgentUnitTest, ShouldSkipFromComponent) { EXPECT_TRUE( commerce_heuristics::CommerceHeuristicsData::GetInstance() .PopulateDataFromComponent("{}", kGlobalHeuristicsJSONData, "", "")); @@ -860,7 +946,7 @@ } } -TEST(CommerceHintAgentTest, ShouldSkip_Priority) { +TEST_F(CommerceHintAgentUnitTest, ShouldSkip_Priority) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeatureWithParameters( ntp_features::kNtpChromeCartModule, kSkipParams); @@ -882,7 +968,7 @@ } } -TEST(CommerceHintAgentTest, ShouldSkipAddToCartFromResource) { +TEST_F(CommerceHintAgentUnitTest, ShouldSkipAddToCartFromResource) { for (auto const& entry : kSkipAddToCartRequests) { EXPECT_TRUE(CommerceHintAgent::ShouldSkipAddToCartRequest( GURL(entry.first), GURL(entry.second))); @@ -986,7 +1072,7 @@ #define MAYBE_RegexBenchmark RegexBenchmark #endif -TEST(CommerceHintAgentTest, MAYBE_RegexBenchmark) { +TEST_F(CommerceHintAgentUnitTest, MAYBE_RegexBenchmark) { std::string str = "abcdefghijklmnop"; const GURL basic_url = GURL("http://example.com/"); // Compile regex before benchmark loop.
diff --git a/chrome/renderer/cart/commerce_renderer_feature_list.cc b/chrome/renderer/cart/commerce_renderer_feature_list.cc deleted file mode 100644 index 1ff9975..0000000 --- a/chrome/renderer/cart/commerce_renderer_feature_list.cc +++ /dev/null
@@ -1,66 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/renderer/cart/commerce_renderer_feature_list.h" - -#include "base/feature_list.h" -#include "base/metrics/field_trial_params.h" -#include "base/no_destructor.h" -#include "third_party/re2/src/re2/re2.h" - -namespace commerce_renderer_feature { - -namespace { - -constexpr base::FeatureParam<std::string> kCouponPartnerMerchantPattern{ - &commerce_renderer_feature::kRetailCoupons, - "coupon-partner-merchant-pattern", - // This regex does not match anything. - "\\b\\B"}; - -constexpr base::FeatureParam<std::string> kDiscountPartnerMerchantPattern{ - &ntp_features::kNtpChromeCartModule, "partner-merchant-pattern", - // This regex does not match anything. - "\\b\\B"}; - -const re2::RE2& GetCouponPartnerMerchantPattern() { - re2::RE2::Options options; - options.set_case_sensitive(false); - static base::NoDestructor<re2::RE2> instance( - kCouponPartnerMerchantPattern.Get(), options); - return *instance; -} - -bool IsCouponPartnerMerchant(const GURL& url) { - const std::string& url_string = url.spec(); - return RE2::PartialMatch( - re2::StringPiece(url_string.data(), url_string.size()), - GetCouponPartnerMerchantPattern()); -} - -const re2::RE2& GetDiscountPartnerMerchantPattern() { - re2::RE2::Options options; - options.set_case_sensitive(false); - static base::NoDestructor<re2::RE2> instance( - kDiscountPartnerMerchantPattern.Get(), options); - return *instance; -} - -bool IsDiscountPartnerMerchant(const GURL& url) { - const std::string& url_string = url.spec(); - return RE2::PartialMatch( - re2::StringPiece(url_string.data(), url_string.size()), - GetDiscountPartnerMerchantPattern()); -} - -} // namespace - -const base::Feature kRetailCoupons{"RetailCoupons", - base::FEATURE_ENABLED_BY_DEFAULT}; - -bool IsPartnerMerchant(const GURL& url) { - return IsCouponPartnerMerchant(url) || IsDiscountPartnerMerchant(url); -} - -} // namespace commerce_renderer_feature
diff --git a/chrome/renderer/cart/commerce_renderer_feature_list.h b/chrome/renderer/cart/commerce_renderer_feature_list.h deleted file mode 100644 index c8d1b32..0000000 --- a/chrome/renderer/cart/commerce_renderer_feature_list.h +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_RENDERER_CART_COMMERCE_RENDERER_FEATURE_LIST_H_ -#define CHROME_RENDERER_CART_COMMERCE_RENDERER_FEATURE_LIST_H_ - -#include "base/feature_list.h" -#include "base/metrics/field_trial_params.h" -#include "components/search/ntp_features.h" -#include "url/gurl.h" - -namespace commerce_renderer_feature { -extern const base::Feature kRetailCoupons; - -// Whether to use OptimizationGuide to optimize renderer signal collection. -constexpr base::FeatureParam<bool> kOptimizeRendererSignal( - &ntp_features::kNtpChromeCartModule, - "optimize-renderer-signal", - false); - -// Check if a URL belongs to a partner merchant of any type of discount. -bool IsPartnerMerchant(const GURL& url); -} // namespace commerce_renderer_feature - -#endif // CHROME_RENDERER_CART_COMMERCE_RENDERER_FEATURE_LIST_H_
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index b388532..bbac287 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -48,6 +48,7 @@ #include "chrome/renderer/chrome_render_frame_observer.h" #include "chrome/renderer/chrome_render_thread_observer.h" #include "chrome/renderer/loadtimes_extension_bindings.h" +#include "chrome/renderer/media/chrome_key_systems.h" #include "chrome/renderer/media/flash_embed_rewrite.h" #include "chrome/renderer/media/webrtc_logging_agent_impl.h" #include "chrome/renderer/net/net_error_helper.h" @@ -1471,11 +1472,7 @@ void ChromeContentRendererClient::GetSupportedKeySystems( media::GetSupportedKeySystemsCB cb) { - key_systems_provider_.GetSupportedKeySystems(std::move(cb)); -} - -bool ChromeContentRendererClient::IsKeySystemsUpdateNeeded() { - return key_systems_provider_.IsKeySystemsUpdateNeeded(); + GetChromeKeySystems(std::move(cb)); } bool ChromeContentRendererClient::ShouldReportDetailedMessageForSource(
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h index 743e9836..5230b897 100644 --- a/chrome/renderer/chrome_content_renderer_client.h +++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -17,7 +17,6 @@ #include "base/memory/scoped_refptr.h" #include "build/build_config.h" #include "chrome/common/media/webrtc_logging.mojom.h" -#include "chrome/renderer/media/chrome_key_systems_provider.h" #include "components/nacl/common/buildflags.h" #include "components/spellcheck/spellcheck_buildflags.h" #include "content/public/renderer/content_renderer_client.h" @@ -158,7 +157,6 @@ media::SpeechRecognitionClient::OnReadyCallback callback) override; #endif void GetSupportedKeySystems(media::GetSupportedKeySystemsCB cb) override; - bool IsKeySystemsUpdateNeeded() override; bool IsPluginAllowedToUseCameraDeviceAPI(const GURL& url) override; void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override; void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override; @@ -267,8 +265,6 @@ std::unique_ptr<web_cache::WebCacheImpl> web_cache_impl_; std::unique_ptr<chrome::WebRtcLoggingAgentImpl> webrtc_logging_agent_impl_; - ChromeKeySystemsProvider key_systems_provider_; - #if BUILDFLAG(ENABLE_SPELLCHECK) std::unique_ptr<SpellCheck> spellcheck_; #endif
diff --git a/chrome/renderer/media/chrome_key_systems.cc b/chrome/renderer/media/chrome_key_systems.cc index 0cdaa37d..ff769b98 100644 --- a/chrome/renderer/media/chrome_key_systems.cc +++ b/chrome/renderer/media/chrome_key_systems.cc
@@ -32,26 +32,18 @@ #include "components/cdm/renderer/android_key_systems.h" #endif -#if BUILDFLAG(ENABLE_LIBRARY_CDMS) +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) || BUILDFLAG(IS_WIN) #include "base/feature_list.h" #include "content/public/renderer/key_system_support.h" #include "media/base/media_switches.h" #include "media/base/video_codecs.h" #if BUILDFLAG(ENABLE_WIDEVINE) #include "third_party/widevine/cdm/widevine_cdm_common.h" // nogncheck -// TODO(crbug.com/663554): Needed for WIDEVINE_CDM_MIN_GLIBC_VERSION. -// component updated CDM on all desktop platforms and remove this. -#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. // nogncheck #if BUILDFLAG(ENABLE_PLATFORM_HEVC) && BUILDFLAG(IS_CHROMEOS_ASH) #include "ash/constants/ash_features.h" #endif // BUILDFLAG(ENABLE_PLATFORM_HEVC) && BUILDFLAG(IS_CHROMEOS_ASH) -// The following must be after widevine_cdm_version.h. -#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) -#include <gnu/libc-version.h> -#include "base/version.h" -#endif // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) #endif // BUILDFLAG(ENABLE_WIDEVINE) -#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) +#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) || BUILDFLAG(IS_WIN) using media::EmeFeatureSupport; using media::EmeSessionTypeSupport; @@ -61,46 +53,7 @@ namespace { -#if BUILDFLAG(ENABLE_LIBRARY_CDMS) - -// Helper callback for chained key system query operations. -using TrampolineCB = media::GetSupportedKeySystemsCB; - -void OnExternalClearKeyQueried( - TrampolineCB cb, - KeySystemPropertiesVector key_systems, - bool is_supported, - media::mojom::KeySystemCapabilityPtr capability) { - DVLOG(1) << __func__; - - // TODO(xhwang): Actually use `capability` to determine capabilities. - if (is_supported) { - key_systems.push_back(std::make_unique<cdm::ExternalClearKeyProperties>()); - } else { - DVLOG(1) << "External Clear Key not supported"; - } - - std::move(cb).Run(std::move(key_systems)); -} - -// External Clear Key (used for testing). -void QueryExternalClearKey(TrampolineCB cb, - KeySystemPropertiesVector key_systems) { - DVLOG(1) << __func__; - - if (!base::FeatureList::IsEnabled(media::kExternalClearKeyForTesting)) { - std::move(cb).Run(std::move(key_systems)); - return; - } - - static const char kExternalClearKeyKeySystem[] = - "org.chromium.externalclearkey"; - - content::IsKeySystemSupported( - kExternalClearKeyKeySystem, - base::BindOnce(&OnExternalClearKeyQueried, std::move(cb), - std::move(key_systems))); -} +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) || BUILDFLAG(IS_WIN) #if BUILDFLAG(ENABLE_WIDEVINE) SupportedCodecs GetVP9Codecs( @@ -273,7 +226,7 @@ #endif // BUILDFLAG(IS_CHROMEOS) } -bool AddWidevine(media::mojom::KeySystemCapabilityPtr capability, +bool AddWidevine(const media::mojom::KeySystemCapabilityPtr& capability, KeySystemPropertiesVector* key_systems) { // Codecs and encryption schemes. SupportedCodecs codecs = media::EME_CODEC_NONE; @@ -294,6 +247,8 @@ cdm_supports_persistent_license = base::Contains(capability->sw_secure_capability->session_types, media::CdmSessionType::kPersistentLicense); + + DVLOG(2) << "Software secure Widevine supported"; } if (capability->hw_secure_capability) { @@ -311,6 +266,7 @@ // session support between software and hardware CDMs. This should be // fixed so that if there is both a software and a hardware CDM, persistent // session support can be different between the versions. + DVLOG(2) << "Hardware secure Widevine supported"; } // Robustness. @@ -347,60 +303,65 @@ distinctive_identifier_support)); return true; } +#endif // BUILDFLAG(ENABLE_WIDEVINE) -void OnWidevineQueried(TrampolineCB cb, - KeySystemPropertiesVector key_systems, - bool is_supported, - media::mojom::KeySystemCapabilityPtr capability) { +const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey"; + +void AddExternalClearKey( + const media::mojom::KeySystemCapabilityPtr& /*capability*/, + KeySystemPropertiesVector* key_systems) { DVLOG(1) << __func__; - if (is_supported) { - if (!AddWidevine(std::move(capability), &key_systems)) - DVLOG(1) << "Invalid Widevine CDM capability."; - } else { - DVLOG(1) << "Widevine CDM is not currently available."; - } - - std::move(cb).Run(std::move(key_systems)); -} - -void QueryWidevine(TrampolineCB cb, KeySystemPropertiesVector key_systems) { - DVLOG(1) << __func__; - -#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) - base::Version glibc_version(gnu_get_libc_version()); - DCHECK(glibc_version.IsValid()); - if (glibc_version < base::Version(WIDEVINE_CDM_MIN_GLIBC_VERSION)) { - std::move(cb).Run(std::move(key_systems)); + if (!base::FeatureList::IsEnabled(media::kExternalClearKeyForTesting)) { + DLOG(ERROR) << "ExternalClearKey supported despite not enabled."; return; } -#endif // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) - content::IsKeySystemSupported( - kWidevineKeySystem, base::BindOnce(&OnWidevineQueried, std::move(cb), - std::move(key_systems))); + // TODO(xhwang): Actually use `capability` to determine capabilities. + key_systems->push_back(std::make_unique<cdm::ExternalClearKeyProperties>()); } + +void OnKeySystemSupportUpdated( + media::GetSupportedKeySystemsCB cb, + content::KeySystemCapabilityPtrMap key_system_capabilities) { + KeySystemPropertiesVector key_systems; + for (const auto& entry : key_system_capabilities) { + const auto& key_system = entry.first; + const auto& capability = entry.second; +#if BUILDFLAG(ENABLE_WIDEVINE) + if (key_system == kWidevineKeySystem) { + AddWidevine(capability, &key_systems); + continue; + } #endif // BUILDFLAG(ENABLE_WIDEVINE) -#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) + + if (key_system == kExternalClearKeyKeySystem) { + AddExternalClearKey(capability, &key_systems); + continue; + } + + DLOG(ERROR) << "Unrecognized key system: " << key_system; + } + + cb.Run(std::move(key_systems)); +} + +#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) || BUILDFLAG(IS_WIN) } // namespace void GetChromeKeySystems(media::GetSupportedKeySystemsCB cb) { - KeySystemPropertiesVector key_systems; - #if BUILDFLAG(IS_ANDROID) + KeySystemPropertiesVector key_systems; cdm::AddAndroidWidevine(&key_systems); -#endif // BUILDFLAG(IS_ANDROID) - -#if BUILDFLAG(ENABLE_LIBRARY_CDMS) -#if BUILDFLAG(ENABLE_WIDEVINE) - auto trampoline_cb = base::BindOnce(&QueryWidevine, std::move(cb)); -#else - auto trampoline_cb = std::move(cb); -#endif // BUILDFLAG(ENABLE_WIDEVINE) - - QueryExternalClearKey(std::move(trampoline_cb), std::move(key_systems)); -#else std::move(cb).Run(std::move(key_systems)); -#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) + return; +#elif BUILDFLAG(ENABLE_LIBRARY_CDMS) || BUILDFLAG(IS_WIN) + content::ObserveKeySystemSupportUpdate( + base::BindRepeating(&OnKeySystemSupportUpdated, std::move(cb))); + return; +#else + std::move(cb).Run({}); + return; +#endif }
diff --git a/chrome/renderer/media/chrome_key_systems_provider.cc b/chrome/renderer/media/chrome_key_systems_provider.cc deleted file mode 100644 index e359643..0000000 --- a/chrome/renderer/media/chrome_key_systems_provider.cc +++ /dev/null
@@ -1,87 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/renderer/media/chrome_key_systems_provider.h" - -#include "chrome/renderer/media/chrome_key_systems.h" -#include "third_party/widevine/cdm/buildflags.h" - -#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) -#include "third_party/widevine/cdm/widevine_cdm_common.h" // nogncheck -#endif - -ChromeKeySystemsProvider::ChromeKeySystemsProvider() = default; -ChromeKeySystemsProvider::~ChromeKeySystemsProvider() = default; - -void ChromeKeySystemsProvider::GetSupportedKeySystems( - media::GetSupportedKeySystemsCB cb) { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (!test_provider_.is_null()) { - media::KeySystemPropertiesVector key_systems; - test_provider_.Run(&key_systems); - OnSupportedKeySystemsReady(std::move(cb), std::move(key_systems)); - return; - } - - GetChromeKeySystems( - base::BindOnce(&ChromeKeySystemsProvider::OnSupportedKeySystemsReady, - weak_factory_.GetWeakPtr(), std::move(cb))); -} - -bool ChromeKeySystemsProvider::IsKeySystemsUpdateNeeded() { - DCHECK(thread_checker_.CalledOnValidThread()); - - // Always needs update if we have never updated, regardless the - // |last_update_time_ticks_|'s initial value. - if (!has_updated_) { - DCHECK(is_update_needed_); - return true; - } - - if (!is_update_needed_) - return false; - - // The update could be expensive. For example, it could involve an IPC to the - // browser process. Use a minimum update interval to avoid unnecessarily - // frequent update. - static const int kMinUpdateIntervalInMilliseconds = 1000; - if ((tick_clock_->NowTicks() - last_update_time_ticks_).InMilliseconds() < - kMinUpdateIntervalInMilliseconds) { - return false; - } - - return true; -} - -void ChromeKeySystemsProvider::SetTickClockForTesting( - const base::TickClock* tick_clock) { - tick_clock_ = tick_clock; -} - -void ChromeKeySystemsProvider::SetProviderDelegateForTesting( - KeySystemsProviderDelegate test_provider) { - test_provider_ = std::move(test_provider); -} - -void ChromeKeySystemsProvider::OnSupportedKeySystemsReady( - media::GetSupportedKeySystemsCB cb, - media::KeySystemPropertiesVector key_systems) { - has_updated_ = true; - last_update_time_ticks_ = tick_clock_->NowTicks(); - -// Check whether all potentially supported key systems are supported. If so, -// no need to update again. -#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) - for (const auto& properties : key_systems) { - if (properties->GetBaseKeySystemName() == kWidevineKeySystem) { - is_update_needed_ = false; - } - } -#else - is_update_needed_ = false; -#endif - - std::move(cb).Run(std::move(key_systems)); -}
diff --git a/chrome/renderer/media/chrome_key_systems_provider.h b/chrome/renderer/media/chrome_key_systems_provider.h deleted file mode 100644 index 5753760..0000000 --- a/chrome/renderer/media/chrome_key_systems_provider.h +++ /dev/null
@@ -1,68 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_PROVIDER_H_ -#define CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_PROVIDER_H_ - -#include <memory> -#include <vector> - -#include "base/callback.h" -#include "base/memory/weak_ptr.h" -#include "base/threading/thread_checker.h" -#include "base/time/default_tick_clock.h" -#include "base/time/tick_clock.h" -#include "base/time/time.h" -#include "media/base/key_system_properties.h" - -using KeySystemsProviderDelegate = - base::RepeatingCallback<void(media::KeySystemPropertiesVector*)>; - -class ChromeKeySystemsProvider { - public: - ChromeKeySystemsProvider(); - ChromeKeySystemsProvider(const ChromeKeySystemsProvider&) = delete; - ChromeKeySystemsProvider& operator=(const ChromeKeySystemsProvider&) = delete; - ~ChromeKeySystemsProvider(); - - // Gets supported key system properties. - void GetSupportedKeySystems(media::GetSupportedKeySystemsCB cb); - - // Returns whether client key systems properties should be updated. - // TODO(chcunningham): Refactor this to a proper change "observer" API that is - // less fragile (don't assume GetSupportedKeySystems has just one caller). - bool IsKeySystemsUpdateNeeded(); - - void SetTickClockForTesting(const base::TickClock* tick_clock); - - void SetProviderDelegateForTesting(KeySystemsProviderDelegate test_provider); - - private: - void OnSupportedKeySystemsReady(media::GetSupportedKeySystemsCB cb, - media::KeySystemPropertiesVector key_systems); - - // Whether GetSupportedKeySystems() has ever been called. - bool has_updated_ = false; - - // Whether a future update is needed. For example, when some potentially - // supported key systems are NOT supported yet. This could happen when the - // required component for a key system is not yet available. - bool is_update_needed_ = true; - - // Throttle how often we signal an update is needed to avoid unnecessary high - // frequency of expensive IPC calls. - base::TimeTicks last_update_time_ticks_; - const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance(); - - // Ensure all methods are called from the same (Main) thread. - base::ThreadChecker thread_checker_; - - // For unit tests to inject their own key systems. Will bypass adding default - // Chrome key systems when set. - KeySystemsProviderDelegate test_provider_; - - base::WeakPtrFactory<ChromeKeySystemsProvider> weak_factory_{this}; -}; - -#endif // CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_PROVIDER_H_
diff --git a/chrome/renderer/media/chrome_key_systems_provider_unittest.cc b/chrome/renderer/media/chrome_key_systems_provider_unittest.cc deleted file mode 100644 index 605c1cb..0000000 --- a/chrome/renderer/media/chrome_key_systems_provider_unittest.cc +++ /dev/null
@@ -1,170 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/renderer/media/chrome_key_systems_provider.h" - -#include <memory> -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/test/bind.h" -#include "base/test/simple_test_tick_clock.h" -#include "base/test/task_environment.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/widevine/cdm/buildflags.h" -#include "third_party/widevine/cdm/widevine_cdm_common.h" - -namespace { - -class TestKeySystemProperties : public media::KeySystemProperties { - public: - explicit TestKeySystemProperties(const std::string& key_system_name) - : key_system_name_(key_system_name) {} - - std::string GetBaseKeySystemName() const override { return key_system_name_; } - - bool IsSupportedInitDataType( - media::EmeInitDataType init_data_type) const override { - return false; - } - - media::EmeConfigRule GetEncryptionSchemeConfigRule( - media::EncryptionScheme encryption_scheme) const override { - return media::EmeConfigRule::NOT_SUPPORTED; - } - - media::SupportedCodecs GetSupportedCodecs() const override { - return media::EME_CODEC_NONE; - } - - media::EmeConfigRule GetRobustnessConfigRule( - const std::string& key_system, - media::EmeMediaType media_type, - const std::string& requested_robustness, - const bool* /*hw_secure_requirement*/) const override { - return requested_robustness.empty() ? media::EmeConfigRule::SUPPORTED - : media::EmeConfigRule::NOT_SUPPORTED; - } - - media::EmeSessionTypeSupport GetPersistentLicenseSessionSupport() - const override { - return media::EmeSessionTypeSupport::NOT_SUPPORTED; - } - - media::EmeFeatureSupport GetPersistentStateSupport() const override { - return media::EmeFeatureSupport::NOT_SUPPORTED; - } - - media::EmeFeatureSupport GetDistinctiveIdentifierSupport() const override { - return media::EmeFeatureSupport::NOT_SUPPORTED; - } - - private: - const std::string key_system_name_; -}; - -class TestKeySystemsProviderDelegate { - public: - TestKeySystemsProviderDelegate() : include_widevine_(false) {} - - void AddTestKeySystems( - std::vector<std::unique_ptr<media::KeySystemProperties>>* key_systems) { - key_systems->emplace_back( - new TestKeySystemProperties("com.example.foobar")); - - if (include_widevine_) { -#if BUILDFLAG(ENABLE_WIDEVINE) - key_systems->emplace_back( - new TestKeySystemProperties(kWidevineKeySystem)); -#else - // Tests should only attempt to include Widevine when it is available. - NOTREACHED(); -#endif - } - } - - void set_include_widevine(bool include_widevine) { - include_widevine_ = include_widevine; - } - - private: - bool include_widevine_; -}; - -void CopyKeySystems(media::KeySystemPropertiesVector* output, - media::KeySystemPropertiesVector input) { - *output = std::move(input); -} - -} // namespace - -TEST(ChromeKeySystemsProviderTest, IsKeySystemsUpdateNeeded) { - base::test::TaskEnvironment task_environment_; - ChromeKeySystemsProvider key_systems_provider; - media::KeySystemPropertiesVector key_systems; - - base::SimpleTestTickClock tick_clock; - key_systems_provider.SetTickClockForTesting(&tick_clock); - - std::unique_ptr<TestKeySystemsProviderDelegate> provider_delegate( - new TestKeySystemsProviderDelegate()); - key_systems_provider.SetProviderDelegateForTesting( - base::BindRepeating(&TestKeySystemsProviderDelegate::AddTestKeySystems, - base::Unretained(provider_delegate.get()))); - - // IsKeySystemsUpdateNeeded() always returns true after construction. - EXPECT_TRUE(key_systems_provider.IsKeySystemsUpdateNeeded()); - - key_systems_provider.GetSupportedKeySystems( - base::BindOnce(&CopyKeySystems, &key_systems)); - - // No update needed immediately after GetSupportedKeySystems() call. - EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded()); - - // Widevine not initially provided. - EXPECT_EQ(key_systems.size(), 1U); - EXPECT_EQ(key_systems[0]->GetBaseKeySystemName(), "com.example.foobar"); - - // This is timing related. The update interval for Widevine is 1000 ms. - EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded()); - tick_clock.Advance(base::Milliseconds(990)); - EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded()); - tick_clock.Advance(base::Milliseconds(10)); - -#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) - // Require update once enough time has passed for builds that install Widevine - // as a component. - EXPECT_TRUE(key_systems_provider.IsKeySystemsUpdateNeeded()); - - // Now add Widevine. - provider_delegate->set_include_widevine(true); - key_systems.clear(); - - key_systems_provider.GetSupportedKeySystems( - base::BindOnce(&CopyKeySystems, &key_systems)); - - // Widevine should now be among the list. - bool found_widevine = false; - for (const auto& key_system_properties : key_systems) { - if (key_system_properties->GetBaseKeySystemName() == kWidevineKeySystem) { - found_widevine = true; - break; - } - } - EXPECT_TRUE(found_widevine); - - // Update not needed now, nor later because Widevine has been described. - EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded()); - tick_clock.Advance(base::Milliseconds(1000)); - EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded()); - tick_clock.Advance(base::Milliseconds(1000)); - EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded()); -#else - // No update needed for builds that either don't offer Widevine or do so - // as part of Chrome rather than component installer. - EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded()); -#endif // BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) -}
diff --git a/chrome/services/sharing/nearby/platform.cc b/chrome/services/sharing/nearby/platform.cc index 694666e..604b83d 100644 --- a/chrome/services/sharing/nearby/platform.cc +++ b/chrome/services/sharing/nearby/platform.cc
@@ -56,6 +56,19 @@ return 0; } +std::string ImplementationPlatform::GetDownloadPath(std::string& parent_folder, + std::string& file_name) { + // This should return the <download_path>/parent_folder/file_name. For now we + // will just return an empty string, since chrome doesn't call this yet. + // TODO(b/223710122): Eventually chrome should implement this method. + NOTIMPLEMENTED(); + return std::string(""); +} + +OSName ImplementationPlatform::GetCurrentOS() { + return OSName::kChromeOS; +} + std::unique_ptr<SubmittableExecutor> ImplementationPlatform::CreateSingleThreadExecutor() { return std::make_unique<chrome::SubmittableExecutor>( @@ -109,6 +122,7 @@ return std::make_unique<chrome::AtomicBoolean>(initial_value); } +ABSL_DEPRECATED("This interface will be deleted in the near future.") std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile( std::int64_t payload_id, std::int64_t total_size) { @@ -117,6 +131,17 @@ connections.ExtractInputFile(payload_id)); } +std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile( + absl::string_view file_path, + size_t size) { + // This constructor is not called by Chrome. Returning nullptr, just in case. + // TODO(b/223710122): Eventually chrome should implement and use this + // constructor exclusively. + NOTIMPLEMENTED(); + return nullptr; +} + +ABSL_DEPRECATED("This interface will be deleted in the near future.") std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile( std::int64_t payload_id) { auto& connections = connections::NearbyConnections::GetInstance(); @@ -124,6 +149,15 @@ connections.ExtractOutputFile(payload_id)); } +std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile( + absl::string_view file_path) { + // This constructor is not called by Chrome. Returning nullptr, just in case. + // TODO(b/223710122): Eventually chrome should implement and use this + // constructor exclusively. + NOTIMPLEMENTED(); + return nullptr; +} + std::unique_ptr<LogMessage> ImplementationPlatform::CreateLogMessage( const char* file, int line,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index c9e0a66c..8e73823 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -4834,7 +4834,6 @@ "../browser/content_settings/page_specific_content_settings_unittest.cc", "../browser/content_settings/sound_content_setting_observer_unittest.cc", "../browser/custom_handlers/chrome_protocol_handler_registry_unittest.cc", - "../browser/download/bubble/download_display_controller_unittest.cc", "../browser/download/chrome_download_manager_delegate_unittest.cc", "../browser/download/deferred_client_wrapper_unittest.cc", "../browser/download/download_history_unittest.cc", @@ -5046,6 +5045,7 @@ "../browser/resource_coordinator/tab_load_tracker_unittest.cc", "../browser/resources_util_unittest.cc", "../browser/security_events/security_event_recorder_impl_unittest.cc", + "../browser/segmentation_platform/model_provider_factory_impl_unittest.cc", "../browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc", "../browser/signin/e2e_tests/test_accounts_util_unittest.cc", @@ -5161,7 +5161,6 @@ "../renderer/chrome_content_renderer_client_unittest.cc", "../renderer/chrome_render_frame_observer_unittest.cc", "../renderer/instant_restricted_id_cache_unittest.cc", - "../renderer/media/chrome_key_systems_provider_unittest.cc", "../renderer/media/flash_embed_rewrite_unittest.cc", "../renderer/net/net_error_helper_core_unittest.cc", "../renderer/plugins/plugin_uma_unittest.cc", @@ -5326,6 +5325,9 @@ "../browser/content_settings/generated_cookie_prefs_unittest.cc", "../browser/content_settings/generated_notification_pref_unittest.cc", "../browser/device_identity/device_oauth2_token_service_unittest.cc", + "../browser/download/bubble/download_bubble_controller_unittest.cc", + "../browser/download/bubble/download_bubble_prefs_unittest.cc", + "../browser/download/bubble/download_display_controller_unittest.cc", "../browser/download/download_shelf_context_menu_unittest.cc", "../browser/enterprise/signals/client_certificate_fetcher_unittest.cc", "../browser/metrics/power/power_metrics_reporter_unittest.cc", @@ -6622,6 +6624,7 @@ "//ash/components/disks:test_support", "//ash/components/login/auth:test_support", "//ash/components/login/session", + "//ash/components/multidevice:test_support", "//ash/components/proximity_auth", "//ash/components/proximity_auth:test_support", "//ash/public/cpp:test_support", @@ -6650,7 +6653,6 @@ "//chrome/services/sharing/public/cpp", "//chrome/services/sharing/public/cpp:unit_tests", "//chromeos/components/feature_usage:feature_usage", - "//chromeos/components/multidevice:test_support", "//chromeos/components/sync_wifi", "//chromeos/dbus", "//chromeos/dbus/attestation", @@ -6827,6 +6829,7 @@ "../browser/lacros/force_installed_tracker_lacros_unittest.cc", "../browser/lacros/lacros_memory_pressure_evaluator_unittest.cc", "../browser/lacros/lacros_url_handling_unittest.cc", + "../browser/lacros/launcher_search/search_util_unittest.cc", "../browser/lacros/metrics_reporting_observer_unittest.cc", "../browser/lacros/net/network_settings_translation_unittest.cc", "../browser/lacros/prefs_ash_observer_unittest.cc",
diff --git a/chrome/test/base/web_ui_test_data_source.cc b/chrome/test/base/web_ui_test_data_source.cc index e378496..569e3c6 100644 --- a/chrome/test/base/web_ui_test_data_source.cc +++ b/chrome/test/base/web_ui_test_data_source.cc
@@ -4,6 +4,7 @@ #include "chrome/test/base/web_ui_test_data_source.h" +#include "chrome/common/webui_url_constants.h" #include "chrome/test/data/grit/webui_generated_test_resources.h" #include "chrome/test/data/grit/webui_generated_test_resources_map.h" #include "content/public/browser/web_ui_data_source.h" @@ -12,7 +13,7 @@ content::WebUIDataSource* CreateWebUITestDataSource() { content::WebUIDataSource* source = - content::WebUIDataSource::Create("webui-test"); + content::WebUIDataSource::Create(chrome::kChromeUIWebUITestHost); source->AddResourcePaths(base::make_span(kWebuiGeneratedTestResources, kWebuiGeneratedTestResourcesSize));
diff --git a/chrome/test/data/browsing_topics/empty_page.html b/chrome/test/data/browsing_topics/empty_page.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/test/data/browsing_topics/empty_page.html
diff --git a/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html b/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html
diff --git a/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html.mock-http-headers b/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html.mock-http-headers new file mode 100644 index 0000000..c434c36 --- /dev/null +++ b/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html.mock-http-headers
@@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Permissions-Policy: browsing-topics=()
diff --git a/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html b/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html
diff --git a/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html.mock-http-headers b/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html.mock-http-headers new file mode 100644 index 0000000..d27f17b --- /dev/null +++ b/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html.mock-http-headers
@@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Permissions-Policy: interest-cohort=()
diff --git a/chrome/test/data/browsing_topics/one_iframe_page.html b/chrome/test/data/browsing_topics/one_iframe_page.html new file mode 100644 index 0000000..67d36c1 --- /dev/null +++ b/chrome/test/data/browsing_topics/one_iframe_page.html
@@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> +<body> + <iframe src="empty_page.html" id="frame"></iframe> +</body> +</html>
diff --git a/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html b/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html new file mode 100644 index 0000000..67d36c1 --- /dev/null +++ b/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html
@@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> +<body> + <iframe src="empty_page.html" id="frame"></iframe> +</body> +</html>
diff --git a/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html.mock-http-headers b/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html.mock-http-headers new file mode 100644 index 0000000..584910a2 --- /dev/null +++ b/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html.mock-http-headers
@@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Permissions-Policy: browsing-topics=(self "https://c.test:{{PORT}}")
diff --git a/chrome/test/data/extensions/api_test/scripting/sub_frames/worker.js b/chrome/test/data/extensions/api_test/scripting/sub_frames/worker.js index 9dc41af6..b546c0c 100644 --- a/chrome/test/data/extensions/api_test/scripting/sub_frames/worker.js +++ b/chrome/test/data/extensions/api_test/scripting/sub_frames/worker.js
@@ -23,21 +23,28 @@ // Returns all frames in the given tab. async function getFramesInTab(tabId) { - // TODO(devlin): Update this when webNavigation supports promises directly. - const frames = await new Promise(resolve => { - chrome.webNavigation.getAllFrames({tabId: tabId}, resolve); - }); + const frames = await chrome.webNavigation.getAllFrames({tabId: tabId}); chrome.test.assertTrue(frames.length > 0); return frames; } -// Returns the ID of the frame with the given `hostname`. -function findFrameIdWithHostname(frames, hostname) { +// Returns the frame with the given `hostname`. +function findFrameWithHostname(frames, hostname) { const frame = frames.find(frame => { return (new URL(frame.url)).hostname == hostname; }); chrome.test.assertTrue(!!frame, 'No frame with hostname: ' + hostname); - return frame.frameId; + return frame; +} + +// Returns the ID of the frame with the given `hostname`. +function findFrameIdWithHostname(frames, hostname) { + return findFrameWithHostname(frames, hostname).frameId; +} + +// Returns the ID of the document with the given `hostname`. +function findDocumentIdWithHostname(frames, hostname) { + return findFrameWithHostname(frames, hostname).documentId; } chrome.test.runTests([ @@ -87,20 +94,34 @@ const query = {url: 'http://a.com/*'}; const tab = await getSingleTab(query); const frames = await getFramesInTab(tab.id); - const frameId = findFrameIdWithHostname(frames, 'b.com'); + const frame = findFrameWithHostname(frames, 'b.com'); - const results = await chrome.scripting.executeScript({ + let results = await chrome.scripting.executeScript({ target: { tabId: tab.id, - frameIds: [frameId], + frameIds: [frame.frameId], }, func: injectedFunction, }); chrome.test.assertEq(1, results.length); - const resultUrl = new URL(results[0].result); + let resultUrl = new URL(results[0].result); chrome.test.assertEq('b.com', resultUrl.hostname); - chrome.test.assertEq(frameId, results[0].frameId); + chrome.test.assertEq(frame.frameId, results[0].frameId); + + // Now try via documentId. + results = await chrome.scripting.executeScript({ + target: { + tabId: tab.id, + documentIds: [frame.documentId], + }, + func: injectedFunction, + }); + chrome.test.assertEq(1, results.length); + + resultUrl = new URL(results[0].result); + chrome.test.assertEq('b.com', resultUrl.hostname); + chrome.test.assertEq(frame.documentId, results[0].documentId); chrome.test.succeed(); }, @@ -113,8 +134,12 @@ findFrameIdWithHostname(frames, 'a.com'), findFrameIdWithHostname(frames, 'b.com'), ]; + const documentIds = [ + findDocumentIdWithHostname(frames, 'a.com'), + findDocumentIdWithHostname(frames, 'b.com'), + ]; - const results = await chrome.scripting.executeScript({ + let results = await chrome.scripting.executeScript({ target: { tabId: tab.id, frameIds: frameIds, @@ -125,13 +150,34 @@ // Since we specified frame IDs, there's no guarantee as to the order // of the result. Compare a sorted output. - const resultUrls = results.map(result => { + let resultUrls = results.map(result => { return (new URL(result.result)).hostname; }); chrome.test.assertEq(['a.com', 'b.com'], resultUrls.sort()); chrome.test.assertEq( frameIds, results.map(result => result.frameId).sort()); + + // Now try the via documentId. + results = await chrome.scripting.executeScript({ + target: { + tabId: tab.id, + documentIds: documentIds, + }, + func: injectedFunction, + }); + chrome.test.assertEq(2, results.length); + + // Since we specified frame IDs, there's no guarantee as to the order + // of the result. Compare a sorted output. + resultUrls = results.map(result => { + return (new URL(result.result)).hostname; + }); + chrome.test.assertEq(['a.com', 'b.com'], resultUrls.sort()); + chrome.test.assertEq( + documentIds.sort(), + results.map(result => result.documentId).sort()); + chrome.test.succeed(); }, @@ -140,20 +186,34 @@ const query = {url: 'http://a.com/*'}; const tab = await getSingleTab(query); const frames = await getFramesInTab(tab.id); - const frameId = findFrameIdWithHostname(frames, 'b.com'); + const frame = findFrameWithHostname(frames, 'b.com'); - const results = await chrome.scripting.executeScript({ + let results = await chrome.scripting.executeScript({ target: { tabId: tab.id, - frameIds: [frameId, frameId], + frameIds: [frame.frameId, frame.frameId], }, func: injectedFunction, }); chrome.test.assertEq(1, results.length); - const resultUrl = new URL(results[0].result); + let resultUrl = new URL(results[0].result); chrome.test.assertEq('b.com', resultUrl.hostname); - chrome.test.assertEq(frameId, results[0].frameId); + chrome.test.assertEq(frame.frameId, results[0].frameId); + + // Now try the via documentId. + results = await chrome.scripting.executeScript({ + target: { + tabId: tab.id, + documentIds: [frame.documentId, frame.documentId], + }, + func: injectedFunction, + }); + chrome.test.assertEq(1, results.length); + + resultUrl = new URL(results[0].result); + chrome.test.assertEq('b.com', resultUrl.hostname); + chrome.test.assertEq(frame.documentId, results[0].documentId); chrome.test.succeed(); }, @@ -170,6 +230,10 @@ findFrameIdWithHostname(frames, 'b.com'), findFrameIdWithHostname(frames, 'c.com'), ]; + const documentIds = [ + findDocumentIdWithHostname(frames, 'b.com'), + findDocumentIdWithHostname(frames, 'c.com'), + ]; await chrome.test.assertPromiseRejects( chrome.scripting.executeScript({ @@ -180,6 +244,17 @@ func: injectedFunction, }), getAccessError(deniedFrame.url)); + + // Now try the via documentId. + await chrome.test.assertPromiseRejects( + chrome.scripting.executeScript({ + target: { + tabId: tab.id, + documentIds: documentIds, + }, + func: injectedFunction, + }), + getAccessError(deniedFrame.url)); chrome.test.succeed(); }, @@ -189,10 +264,15 @@ const tab = await getSingleTab(query); const frames = await getFramesInTab(tab.id); const nonExistentFrameId = 99999; + const nonExistentDocumentId = '0123456789ABCDEF0123456789ABCDEF'; const frameIds = [ findFrameIdWithHostname(frames, 'b.com'), nonExistentFrameId, ]; + const documentIds = [ + findDocumentIdWithHostname(frames, 'b.com'), + nonExistentDocumentId, + ]; await chrome.test.assertPromiseRejects( chrome.scripting.executeScript({ @@ -204,6 +284,18 @@ }), `Error: No frame with id ${nonExistentFrameId} in ` + `tab with id ${tab.id}`); + + // Now try the via documentId. + await chrome.test.assertPromiseRejects( + chrome.scripting.executeScript({ + target: { + tabId: tab.id, + documentIds: documentIds, + }, + func: injectedFunction, + }), + `Error: No document with id ${nonExistentDocumentId} in ` + + `tab with id ${tab.id}`); chrome.test.succeed(); }, @@ -215,6 +307,9 @@ const frameIds = [ findFrameIdWithHostname(frames, 'b.com'), ]; + const documentIds = [ + findDocumentIdWithHostname(frames, 'b.com'), + ]; await chrome.test.assertPromiseRejects( chrome.scripting.executeScript({ @@ -225,7 +320,71 @@ }, func: injectedFunction, }), - `Error: Cannot specify both 'allFrames' and 'frameIds'.`); + `Error: Cannot specify 'allFrames' if either 'frameIds' or ` + + `'documentIds' is specified.`); + + // Now try the via documentId. + await chrome.test.assertPromiseRejects( + chrome.scripting.executeScript({ + target: { + tabId: tab.id, + documentIds: documentIds, + allFrames: true, + }, + func: injectedFunction, + }), + `Error: Cannot specify 'allFrames' if either 'frameIds' or ` + + `'documentIds' is specified.`); chrome.test.succeed(); }, + + // Test that an extension cannot specify both frameIds and documentIds. + async function specifyingBothFrameIdsAndDocumentIdsIsInvalid() { + const query = {url: 'http://a.com/*'}; + const tab = await getSingleTab(query); + const frames = await getFramesInTab(tab.id); + const frameIds = [ + findFrameIdWithHostname(frames, 'b.com'), + ]; + const documentIds = [ + findDocumentIdWithHostname(frames, 'b.com'), + ]; + + await chrome.test.assertPromiseRejects( + chrome.scripting.executeScript({ + target: { + tabId: tab.id, + documentIds: documentIds, + frameIds: frameIds + }, + func: injectedFunction, + }), + `Error: Cannot specify both 'frameIds' and 'documentIds'.`); + chrome.test.succeed(); + }, + + // Test that an extension cannot specify both a documentId from another tab. + async function specifyingBothFrameIdsAndDocumentIdsIsInvalid() { + const query_a = {url: 'http://a.com/*'}; + const tab_a = await getSingleTab(query_a); + const query_d = {url: 'http://d.com/*'}; + const tab_d = await getSingleTab(query_d); + const frames = await getFramesInTab(tab_d.id); + const documentIds = [ + findDocumentIdWithHostname(frames, 'b.com'), + ]; + + await chrome.test.assertPromiseRejects( + chrome.scripting.executeScript({ + target: { + tabId: tab_a.id, + documentIds: documentIds + }, + func: injectedFunction, + }), + `Error: No document with id ${documentIds[0]} in ` + + `tab with id ${tab_a.id}`); + chrome.test.succeed(); + }, + ]);
diff --git a/chrome/test/data/pdf/BUILD.gn b/chrome/test/data/pdf/BUILD.gn new file mode 100644 index 0000000..4c4931d --- /dev/null +++ b/chrome/test/data/pdf/BUILD.gn
@@ -0,0 +1,79 @@ +# Copyright 2022 The Chromium 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("//chrome/browser/resources/pdf/pdf.gni") +import("//pdf/features.gni") +import("//tools/typescript/ts_library.gni") +import("//ui/webui/resources/tools/generate_grd.gni") + +assert(enable_pdf, "enable_pdf check failed") + +generate_grd("build_grdp") { + grd_prefix = "pdf" + out_grd = "$target_gen_dir/resources.grdp" + + deps = [ ":build_ts" ] + manifest_files = [ "$target_gen_dir/tsconfig.manifest" ] + resource_path_prefix = "pdf" +} + +ts_library("build_ts") { + root_dir = "." + out_dir = "$target_gen_dir/tsc" + tsconfig_base = "tsconfig_base.json" + path_mappings = [ + "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/*|" + + rebase_path("$root_gen_dir/chrome/browser/resources/pdf/tsc/*", + target_gen_dir), + "chrome://webui-test/*|" + + rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*", + target_gen_dir), + ] + in_files = [ + "basic_plugin_test.js", + "basic_test.js", + "beep_test.js", + "bookmarks_test.js", + "download_controls_test.js", + "fullscreen_test.js", + "gesture_detector_test.js", + "layout_test.js", + "material_elements_test.js", + "metrics_test.js", + "navigator_test.js", + "nobeep_test.js", + "page_change_test.js", + "params_parser_test.js", + "post_message_proxy_test.js", + "scroll_with_form_field_focused_test.js", + "test_util.js", + "title_test.js", + "touch_handling_test.js", + "viewer_password_dialog_test.js", + "viewer_pdf_sidenav_test.js", + "viewer_properties_dialog_test.js", + "viewer_thumbnail_bar_test.js", + "viewer_thumbnail_test.js", + "viewer_toolbar_test.js", + "viewport_scroller_test.js", + "viewport_test.js", + "whitespace_title_test.js", + "zoom_manager_test.js", + ] + + if (enable_ink) { + in_files += [ + "annotations_feature_enabled_test.js", + "annotations_toolbar_test.js", + "viewer_toolbar_dropdown_test.js", + ] + } + + if (is_chromeos_ash) { + in_files += [ "printing_icon_test.js" ] + } + + definitions = ts_definitions + deps = [ "//chrome/browser/resources/pdf:build_ts" ] +}
diff --git a/chrome/test/data/pdf/tsconfig_base.json b/chrome/test/data/pdf/tsconfig_base.json new file mode 100644 index 0000000..99a81eca --- /dev/null +++ b/chrome/test/data/pdf/tsconfig_base.json
@@ -0,0 +1,6 @@ +{ + "extends": "../../../../tools/typescript/tsconfig_base.json", + "compilerOptions": { + "allowJs": true + } +}
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index 859137c..67dd4b2 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -15391,7 +15391,7 @@ }, "prefs": { "hsts.policy.upgrade_bypass_list": { - "location": "local_state" + "location": "user_profile" } } }
diff --git a/chrome/test/data/webui/.eslintrc.js b/chrome/test/data/webui/.eslintrc.js index d5594eaa..8d6486f 100644 --- a/chrome/test/data/webui/.eslintrc.js +++ b/chrome/test/data/webui/.eslintrc.js
@@ -6,14 +6,20 @@ 'env': {'browser': true, 'es6': true}, 'rules': { 'no-restricted-properties': [ - 'error', - { + 'error', { 'object': 'MockInteractions', 'property': 'tap', 'message': 'Do not use on-tap handlers in prod code, and use the ' + 'native click() method in tests. See more context at ' + 'crbug.com/812035.', }, + { + 'object': 'test', + 'property': 'only', + 'message': 'test.only() silently disables other tests in the same ' + + 'suite(). Did you forget deleting it before uploading? Use ' + + 'test.skip() instead to explicitly disable certain test() cases.', + } ], 'no-var': 'off', 'prefer-const': 'off',
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn index b4e276b..eefb01f 100644 --- a/chrome/test/data/webui/BUILD.gn +++ b/chrome/test/data/webui/BUILD.gn
@@ -10,6 +10,7 @@ import("//components/signin/features.gni") import("//crypto/features.gni") import("//extensions/buildflags/buildflags.gni") +import("//pdf/features.gni") import("//printing/buildflags/buildflags.gni") import("//third_party/closure_compiler/compile_js.gni") import("//tools/grit/grit_rule.gni") @@ -659,6 +660,13 @@ grdp_files += [ "$target_gen_dir/tab_strip/resources.grdp" ] } + if (enable_pdf) { + # Include PDF Viewer tests, since they are also served by + # chrome://webui-test, even though they reside in chrome/test/data/pdf/. + deps += [ "../pdf:build_grdp" ] + grdp_files += [ "$target_gen_dir/../pdf/resources.grdp" ] + } + if (!is_android) { deps += [ "support_tool:build_grdp",
diff --git a/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/OWNERS b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/OWNERS index 7027ab73..18377141 100644 --- a/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/OWNERS +++ b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js index c5d27a76..a86fddb 100644 --- a/chrome/test/data/webui/settings/cr_settings_browsertest.js +++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -493,9 +493,12 @@ } }; -TEST_F('CrSettingsPrivacyGuidePageTest', 'PrivacyGuidePageTests', function() { - runMochaSuite('PrivacyGuidePage'); -}); +// TODO(crbug.com/1307443): disabling due to flakiness on several builders. +TEST_F( + 'CrSettingsPrivacyGuidePageTest', 'DISABLED_PrivacyGuidePageTests', + function() { + runMochaSuite('PrivacyGuidePage'); + }); TEST_F( 'CrSettingsPrivacyGuidePageTest', 'PrivacyGuideFragmentMetricsTests',
diff --git a/chrome/updater/constants.cc b/chrome/updater/constants.cc index 649ad94..c9efb87 100644 --- a/chrome/updater/constants.cc +++ b/chrome/updater/constants.cc
@@ -42,6 +42,7 @@ const char kAppVersionSwitch[] = "app-version"; const char kWakeSwitch[] = "wake"; const char kTagSwitch[] = "tag"; +const char kInstallerDataSwitch[] = "installerdata"; const char kServerServiceSwitch[] = "service"; @@ -86,6 +87,8 @@ // Specifies that urls that can be cached by proxies are preferred. const char kDownloadPreferenceCacheable[] = "cacheable"; +const char kUTF8BOM[] = "\xEF\xBB\xBF"; + #if BUILDFLAG(IS_MAC) // The user defaults suite name. const char kUserDefaultsSuiteName[] = MAC_BUNDLE_IDENTIFIER_STRING ".defaults";
diff --git a/chrome/updater/constants.h b/chrome/updater/constants.h index 032b71d..cb298af3 100644 --- a/chrome/updater/constants.h +++ b/chrome/updater/constants.h
@@ -83,6 +83,12 @@ // currently encoded as a ASCII string. extern const char kTagSwitch[]; +// The --installerdata=file.dat switch is passed to an installer if an +// installdataindex is specified in the tag or if installerdata is passed in via +// --appargs. The corresponding installerdata is written to file.dat with an +// UTF8 encoding as well as a UTF8 BOM. +extern const char kInstallerDataSwitch[]; + #if BUILDFLAG(IS_WIN) // A debug switch to indicate that --install is running from the `out` directory // of the build. When this switch is present, the setup picks up the run time @@ -319,6 +325,10 @@ extern const char kDownloadPreferenceCacheable[]; +// UTF8 byte order mark (BOM) used to prefix the contents of the installerdata +// file. +extern const char kUTF8BOM[]; + constexpr int kPolicyNotSet = -1; constexpr int kPolicyDisabled = 0; constexpr int kPolicyEnabled = 1;
diff --git a/chrome/updater/installer.cc b/chrome/updater/installer.cc index de32b95..73101f1 100644 --- a/chrome/updater/installer.cc +++ b/chrome/updater/installer.cc
@@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/files/file_enumerator.h" +#include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/notreached.h" @@ -163,9 +164,10 @@ // the prefs are updated asynchronously with the new |pv| and |fingerprint|. // The task sequencing guarantees that the prefs will be updated by the // time another CrxDataCallback is invoked, which needs updated values. - return RunApplicationInstaller(application_installer, - install_params->arguments, - std::move(progress_callback)); + return RunApplicationInstaller( + application_installer, install_params->arguments, + WriteInstallerDataToTempFile(install_params->server_install_data), + std::move(progress_callback)); } void Installer::InstallWithSyncPrimitives( @@ -232,6 +234,7 @@ Installer::Result Installer::RunApplicationInstaller( const base::FilePath& /*app_installer*/, const std::string& /*arguments*/, + const absl::optional<base::FilePath>& /*installer_data_file*/, ProgressCallback /*progress_callback*/) { NOTIMPLEMENTED(); return Installer::Result(-1);
diff --git a/chrome/updater/installer.h b/chrome/updater/installer.h index 6cf30ad3..8fd7c22 100644 --- a/chrome/updater/installer.h +++ b/chrome/updater/installer.h
@@ -96,9 +96,11 @@ // creating processes, mounting images, running scripts, and collecting // exit codes. The install progress, if it can be collected, is reported by // invoking the |progress_callback|. - Result RunApplicationInstaller(const base::FilePath& app_installer, - const std::string& arguments, - ProgressCallback progress_callback); + Result RunApplicationInstaller( + const base::FilePath& app_installer, + const std::string& arguments, + const absl::optional<base::FilePath>& installer_data_file, + ProgressCallback progress_callback); // Deletes recursively the install paths not matching the |pv_| version. void DeleteOlderInstallPaths();
diff --git a/chrome/updater/installer_mac.cc b/chrome/updater/installer_mac.cc index 9f0a029..3b36eb0 100644 --- a/chrome/updater/installer_mac.cc +++ b/chrome/updater/installer_mac.cc
@@ -10,18 +10,21 @@ #include "base/strings/strcat.h" #include "chrome/updater/constants.h" #include "chrome/updater/mac/install_from_archive.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace updater { Installer::Result Installer::RunApplicationInstaller( const base::FilePath& app_installer, const std::string& arguments, + const absl::optional<base::FilePath>& installer_data_file, ProgressCallback /*progress_callback*/) { DVLOG(1) << "Running application install from DMG at " << app_installer; // InstallFromArchive() returns the exit code of the script. 0 is success and // anything else should be an error. - const int exit_code = InstallFromArchive(app_installer, checker_path_, ap_, - updater_scope_, pv_, arguments); + const int exit_code = + InstallFromArchive(app_installer, checker_path_, ap_, updater_scope_, pv_, + arguments, installer_data_file); return exit_code == 0 ? Result() : Result(kErrorApplicationInstallerFailed, exit_code); }
diff --git a/chrome/updater/mac/install_from_archive.h b/chrome/updater/mac/install_from_archive.h index 15d8889..b20e8fb 100644 --- a/chrome/updater/mac/install_from_archive.h +++ b/chrome/updater/mac/install_from_archive.h
@@ -7,6 +7,8 @@ #include <string> +#include "third_party/abseil-cpp/absl/types/optional.h" + namespace base { class FilePath; class Version; @@ -49,12 +51,14 @@ // Choose which type of archive to install from. Possible types of archives are // DMG, Zip and just the App. From there, it calls the archive specific // installation method. -int InstallFromArchive(const base::FilePath& file_path, - const base::FilePath& existence_checker_path, - const std::string& ap, - const UpdaterScope& scope, - const base::Version& pv, - const std::string& arguments); +int InstallFromArchive( + const base::FilePath& file_path, + const base::FilePath& existence_checker_path, + const std::string& ap, + const UpdaterScope& scope, + const base::Version& pv, + const std::string& arguments, + const absl::optional<base::FilePath>& installer_data_file); } // namespace updater
diff --git a/chrome/updater/mac/install_from_archive.mm b/chrome/updater/mac/install_from_archive.mm index 70d090ef..498f510 100644 --- a/chrome/updater/mac/install_from_archive.mm +++ b/chrome/updater/mac/install_from_archive.mm
@@ -26,9 +26,11 @@ #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" #include "base/version.h" +#include "chrome/updater/constants.h" #include "chrome/updater/mac/mac_util.h" #include "chrome/updater/updater_scope.h" #include "chrome/updater/util.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace updater { namespace { @@ -121,6 +123,7 @@ int RunExecutable(const base::FilePath& existence_checker_path, const std::string& ap, const std::string& arguments, + const absl::optional<base::FilePath>& installer_data_file, const UpdaterScope& scope, const base::Version& pv, const base::FilePath& unpacked_path) { @@ -171,6 +174,11 @@ {"UPDATE_IS_MACHINE", scope == UpdaterScope::kSystem ? "1" : "0"}, {"UNPACK_DIR", unpacked_path.value()}, }; + if (installer_data_file) { + options.environment.emplace(base::ToUpperASCII(kInstallerDataSwitch), + installer_data_file->value()); + } + int exit_code = 0; VLOG(1) << "Running " << command.GetCommandLineString(); if (!base::LaunchProcess(command, options).WaitForExit(&exit_code)) @@ -304,12 +312,14 @@ } } // namespace -int InstallFromArchive(const base::FilePath& file_path, - const base::FilePath& existence_checker_path, - const std::string& ap, - const UpdaterScope& scope, - const base::Version& pv, - const std::string& arguments) { +int InstallFromArchive( + const base::FilePath& file_path, + const base::FilePath& existence_checker_path, + const std::string& ap, + const UpdaterScope& scope, + const base::Version& pv, + const std::string& arguments, + const absl::optional<base::FilePath>& installer_data_file) { const std::map<std::string, int (*)(const base::FilePath&, base::OnceCallback<int(const base::FilePath&)>)> @@ -323,7 +333,7 @@ if (base::PathExists(new_path)) { return entry.second( new_path, base::BindOnce(&RunExecutable, existence_checker_path, ap, - arguments, scope, pv)); + arguments, installer_data_file, scope, pv)); } }
diff --git a/chrome/updater/mac/setup/setup_unittest.mm b/chrome/updater/mac/setup/setup_unittest.mm index 5f01cd1..11fb173f 100644 --- a/chrome/updater/mac/setup/setup_unittest.mm +++ b/chrome/updater/mac/setup/setup_unittest.mm
@@ -163,7 +163,7 @@ ASSERT_TRUE(base::PathExists(dmg_file_path)); ASSERT_NE(updater::InstallFromArchive(dmg_file_path, {}, {}, updater::UpdaterScope::kUser, - base::Version("0"), {}), + base::Version("0"), {}, {}), 0); } @@ -175,7 +175,7 @@ ASSERT_TRUE(base::PathExists(dmg_file_path)); ASSERT_NE(updater::InstallFromArchive(dmg_file_path, {}, {}, updater::UpdaterScope::kUser, - base::Version("0"), "arg2"), + base::Version("0"), "arg2", {}), 0); } @@ -192,7 +192,7 @@ ASSERT_EQ(updater::InstallFromArchive(dmg_file_path, installed_app_path, {}, updater::UpdaterScope::kUser, - base::Version(kTestAppVersion), {}), + base::Version(kTestAppVersion), {}, {}), 0); } @@ -211,7 +211,7 @@ std::string args = base::StrCat({kTestAppVersion, " arg1 arg2"}); ASSERT_EQ(updater::InstallFromArchive(dmg_file_path, installed_app_path, {}, updater::UpdaterScope::kUser, - base::Version("0"), args), + base::Version("0"), args, {}), 0); } @@ -220,28 +220,29 @@ ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); test_dir = test_dir.Append("updater"); - ASSERT_EQ(updater::InstallFromArchive( - test_dir.Append("setup_test_envcheck").Append("marker.app"), - base::FilePath::FromASCII("xc_path"), "ap", - updater::UpdaterScope::kUser, base::Version("0"), "arg1 arg2"), - 0); + ASSERT_EQ( + updater::InstallFromArchive( + test_dir.Append("setup_test_envcheck").Append("marker.app"), + base::FilePath::FromASCII("xc_path"), "ap", + updater::UpdaterScope::kUser, base::Version("0"), "arg1 arg2", {}), + 0); ASSERT_EQ( updater::InstallFromArchive( test_dir.Append("setup_test_preinstallfailure").Append("marker.app"), - {}, {}, updater::UpdaterScope::kUser, base::Version("0"), {}), + {}, {}, updater::UpdaterScope::kUser, base::Version("0"), {}, {}), 1); ASSERT_EQ( updater::InstallFromArchive( test_dir.Append("setup_test_installfailure").Append("marker.app"), {}, - {}, updater::UpdaterScope::kUser, base::Version("0"), {}), + {}, updater::UpdaterScope::kUser, base::Version("0"), {}, {}), 2); ASSERT_EQ( updater::InstallFromArchive( test_dir.Append("setup_test_postinstallfailure").Append("marker.app"), - {}, {}, updater::UpdaterScope::kUser, base::Version("0"), {}), + {}, {}, updater::UpdaterScope::kUser, base::Version("0"), {}, {}), 3); }
diff --git a/chrome/updater/util.cc b/chrome/updater/util.cc index b0af7e1e..90c6856e 100644 --- a/chrome/updater/util.cc +++ b/chrome/updater/util.cc
@@ -15,6 +15,7 @@ #include "base/base_paths.h" #include "base/command_line.h" +#include "base/files/file.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" @@ -302,4 +303,30 @@ #endif // BUILDFLAG(IS_WIN) +absl::optional<base::FilePath> WriteInstallerDataToTempFile( + const std::string& installer_data) { + VLOG(2) << __func__ << ": " << installer_data; + if (installer_data.empty()) + return absl::nullopt; + + base::FilePath module_dir; + if (!base::PathService::Get(base::DIR_MODULE, &module_dir)) + return absl::nullopt; + + base::FilePath path; + base::File file = base::CreateAndOpenTemporaryFileInDir(module_dir, &path); + if (!file.IsValid()) + return absl::nullopt; + + const std::string installer_data_utf8_bom = + base::StrCat({kUTF8BOM, installer_data}); + if (file.Write(0, installer_data_utf8_bom.c_str(), + installer_data_utf8_bom.length()) == -1) { + VLOG(2) << __func__ << " file.Write failed"; + return absl::nullopt; + } + + return path; +} + } // namespace updater
diff --git a/chrome/updater/util.h b/chrome/updater/util.h index 8dc15e5..6f0441f 100644 --- a/chrome/updater/util.h +++ b/chrome/updater/util.h
@@ -195,6 +195,12 @@ #endif // BUILDFLAG(IS_WIN) +// Writes the provided string prefixed with the UTF8 byte order mark to a +// temporary file. The temporary file is created in the same directory as the +// current exe. +absl::optional<base::FilePath> WriteInstallerDataToTempFile( + const std::string& installer_data); + } // namespace updater #endif // CHROME_UPDATER_UTIL_H_
diff --git a/chrome/updater/util_unittest.cc b/chrome/updater/util_unittest.cc index 60fad81..9e40e9f 100644 --- a/chrome/updater/util_unittest.cc +++ b/chrome/updater/util_unittest.cc
@@ -5,6 +5,9 @@ #include "chrome/updater/util.h" #include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/strings/strcat.h" #include "base/test/scoped_command_line.h" #include "chrome/updater/constants.h" #include "chrome/updater/tag.h" @@ -38,4 +41,20 @@ } } +TEST(Util, WriteInstallerDataToTempFile) { + EXPECT_FALSE(WriteInstallerDataToTempFile("")); + + const std::string kInstallerData = + R"({"distribution":{"msi":true,"allow_downgrade":false}})"; + const absl::optional<base::FilePath> installer_data_file = + WriteInstallerDataToTempFile(kInstallerData); + ASSERT_TRUE(installer_data_file); + + std::string contents; + EXPECT_TRUE(base::ReadFileToString(*installer_data_file, &contents)); + EXPECT_EQ(base::StrCat({kUTF8BOM, kInstallerData}), contents); + + EXPECT_TRUE(base::DeleteFile(*installer_data_file)); +} + } // namespace updater
diff --git a/chrome/updater/win/installer_api.cc b/chrome/updater/win/installer_api.cc index 8eadc18..e331030f 100644 --- a/chrome/updater/win/installer_api.cc +++ b/chrome/updater/win/installer_api.cc
@@ -325,6 +325,7 @@ Installer::Result Installer::RunApplicationInstaller( const base::FilePath& app_installer, const std::string& arguments, + const absl::optional<base::FilePath>& installer_data_file, ProgressCallback progress_callback) { if (!app_installer.MatchesExtension(L".exe") && !app_installer.MatchesExtension(L".msi")) { @@ -337,10 +338,8 @@ const std::wstring argsw = base::UTF8ToWide(arguments); const std::wstring cmdline = app_installer.MatchesExtension(L".msi") - ? BuildMsiCommandLine(argsw, app_installer) - : base::StrCat( - {base::CommandLine(app_installer).GetCommandLineString(), L" ", - argsw}); + ? BuildMsiCommandLine(argsw, installer_data_file, app_installer) + : BuildExeCommandLine(argsw, installer_data_file, app_installer); VLOG(1) << "Running application installer: " << cmdline; base::LaunchOptions options;
diff --git a/chrome/updater/win/setup/uninstall.cc b/chrome/updater/win/setup/uninstall.cc index a77e6e3b..8f7383e 100644 --- a/chrome/updater/win/setup/uninstall.cc +++ b/chrome/updater/win/setup/uninstall.cc
@@ -161,6 +161,9 @@ uninstall_list->Rollback(); return kErrorFailedToDeleteRegistryKeys; } + + // TODO(crbug.com/1307528) : Windows Uninstall discrepancies need fixing. + DeleteGoogleUpdateEntries(scope, key); } DeleteComInterfaces(key, uninstall_all); @@ -171,8 +174,6 @@ if (scope == UpdaterScope::kUser) UnregisterUserRunAtStartup(GetTaskNamePrefix(scope)); - DeleteGoogleUpdateEntries(scope, key); - return RunUninstallScript(scope, uninstall_all); }
diff --git a/chrome/updater/win/win_util.cc b/chrome/updater/win/win_util.cc index cd02dc4..09262e4d 100644 --- a/chrome/updater/win/win_util.cc +++ b/chrome/updater/win/win_util.cc
@@ -666,15 +666,45 @@ COMGLB_EXCEPTION_DONOT_HANDLE); } -std::wstring BuildMsiCommandLine(const std::wstring& arguments, - const base::FilePath& msi_installer) { +std::wstring BuildMsiCommandLine( + const std::wstring& arguments, + const absl::optional<base::FilePath>& installer_data_file, + const base::FilePath& msi_installer) { if (!msi_installer.MatchesExtension(L".msi")) { return std::wstring(); } return base::StrCat( - {L"msiexec ", arguments, L" REBOOT=ReallySuppress /qn /i \"", - msi_installer.value(), L"\" /log \"", msi_installer.value(), L".log\""}); + {L"msiexec ", arguments, + installer_data_file + ? base::StrCat( + {L" ", + base::UTF8ToWide(base::ToUpperASCII(kInstallerDataSwitch)), + L"=\"", installer_data_file->value(), L"\""}) + : L"", + L" REBOOT=ReallySuppress /qn /i \"", msi_installer.value(), + L"\" /log \"", msi_installer.value(), L".log\""}); +} + +std::wstring BuildExeCommandLine( + const std::wstring& arguments, + const absl::optional<base::FilePath>& installer_data_file, + const base::FilePath& exe_installer) { + if (!exe_installer.MatchesExtension(L".exe")) { + return std::wstring(); + } + + return base::StrCat({base::CommandLine(exe_installer).GetCommandLineString(), + L" ", arguments, [&installer_data_file]() { + if (!installer_data_file) + return std::wstring(); + + base::CommandLine installer_data_args( + base::CommandLine::NO_PROGRAM); + installer_data_args.AppendSwitchPath( + kInstallerDataSwitch, *installer_data_file); + return installer_data_args.GetCommandLineString(); + }()}); } bool IsServiceRunning(const std::wstring& service_name) {
diff --git a/chrome/updater/win/win_util.h b/chrome/updater/win/win_util.h index 55899a0..c9c948d 100644 --- a/chrome/updater/win/win_util.h +++ b/chrome/updater/win/win_util.h
@@ -214,11 +214,20 @@ // program state. [[nodiscard]] HRESULT DisableCOMExceptionHandling(); -// Builds a command line running `MSIExec` on the provided `msi_installer` and -// `arguments`, with added logging to a log file in the same directory as the -// MSI installer. -std::wstring BuildMsiCommandLine(const std::wstring& arguments, - const base::FilePath& msi_installer); +// Builds a command line running `MSIExec` on the provided +// `msi_installer`,`arguments`, and `installer_data_file`, with added logging to +// a log file in the same directory as the MSI installer. +std::wstring BuildMsiCommandLine( + const std::wstring& arguments, + const absl::optional<base::FilePath>& installer_data_file, + const base::FilePath& msi_installer); + +// Builds a command line running the provided `exe_installer`, `arguments`, and +// `installer_data_file`. +std::wstring BuildExeCommandLine( + const std::wstring& arguments, + const absl::optional<base::FilePath>& installer_data_file, + const base::FilePath& exe_installer); // Returns `true` if the service specified is currently running or starting. bool IsServiceRunning(const std::wstring& service_name);
diff --git a/chrome/updater/win/win_util_unittest.cc b/chrome/updater/win/win_util_unittest.cc index b3eecc1..a33b5bbf 100644 --- a/chrome/updater/win/win_util_unittest.cc +++ b/chrome/updater/win/win_util_unittest.cc
@@ -67,15 +67,42 @@ } TEST(WinUtil, BuildMsiCommandLine) { - EXPECT_STREQ(L"", BuildMsiCommandLine(std::wstring(L"arg1 arg2 arg3"), + EXPECT_STREQ(L"", BuildMsiCommandLine(std::wstring(L"arg1 arg2 arg3"), {}, base::FilePath(L"NotMsi.exe")) .c_str()); EXPECT_STREQ( L"msiexec arg1 arg2 arg3 REBOOT=ReallySuppress /qn /i \"c:\\my " L"path\\YesMsi.msi\" /log \"c:\\my path\\YesMsi.msi.log\"", - BuildMsiCommandLine(std::wstring(L"arg1 arg2 arg3"), + BuildMsiCommandLine(std::wstring(L"arg1 arg2 arg3"), {}, base::FilePath(L"c:\\my path\\YesMsi.msi")) .c_str()); + EXPECT_STREQ( + L"msiexec arg1 arg2 arg3 INSTALLERDATA=\"c:\\my path\\installer data " + L"file.dat\" REBOOT=ReallySuppress /qn /i \"c:\\my " + L"path\\YesMsi.msi\" /log \"c:\\my path\\YesMsi.msi.log\"", + BuildMsiCommandLine( + std::wstring(L"arg1 arg2 arg3"), + base::FilePath(L"c:\\my path\\installer data file.dat"), + base::FilePath(L"c:\\my path\\YesMsi.msi")) + .c_str()); +} + +TEST(WinUtil, BuildExeCommandLine) { + EXPECT_STREQ(L"", BuildExeCommandLine(std::wstring(L"arg1 arg2 arg3"), {}, + base::FilePath(L"NotExe.msi")) + .c_str()); + EXPECT_STREQ(L"\"c:\\my path\\YesExe.exe\" arg1 arg2 arg3", + BuildExeCommandLine(std::wstring(L"arg1 arg2 arg3"), {}, + base::FilePath(L"c:\\my path\\YesExe.exe")) + .c_str()); + EXPECT_STREQ( + L"\"c:\\my path\\YesExe.exe\" arg1 arg2 arg3 --installerdata=\"c:\\my " + L"path\\installer data file.dat\"", + BuildExeCommandLine( + std::wstring(L"arg1 arg2 arg3"), + base::FilePath(L"c:\\my path\\installer data file.dat"), + base::FilePath(L"c:\\my path\\YesExe.exe")) + .c_str()); } } // namespace updater
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java index 947cf5cc..07c0e6a 100644 --- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java +++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
@@ -213,6 +213,14 @@ protected void onStop() { if (DEBUG) Log.d(TAG, "onStop"); mStartedState.reset(); + // If this device is in "lock task mode," then leaving the Activity will not return to the + // Home screen and there will be no affordance for the user to return to this Activity. + // When in this mode, leaving the Activity should tear down the Cast app. + if (isInLockTaskMode(this)) { + CastWebContentsComponent.onComponentClosed( + CastWebContentsIntentUtils.getSessionId(getIntent())); + mIsFinishingState.set("User exit while in lock task mode"); + } super.onStop(); } @@ -260,16 +268,6 @@ @Override public void onUserLeaveHint() { if (DEBUG) Log.d(TAG, "onUserLeaveHint"); - // If this device is in "lock task mode," then leaving the Activity will not return to the - // Home screen and there will be no affordance for the user to return to this Activity. - // When in this mode, leaving the Activity should tear down the Cast app. - Context ctx = getApplicationContext(); - if (isInLockTaskMode(ctx)) { - CastWebContentsComponent.onComponentClosed( - CastWebContentsIntentUtils.getSessionId(getIntent())); - mIsFinishingState.set("User exit while in lock task mode"); - return; - } if (canUsePictureInPicture() && !canAutoEnterPictureInPicture()) { enterPictureInPictureMode(new PictureInPictureParams.Builder().build()); }
diff --git a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java index 3617b9d..72c1511e 100644 --- a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java +++ b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java
@@ -344,38 +344,35 @@ } @Test - public void testDoesNotCloseAppWhenUserLeave() { + public void testDoesNotCloseAppWhenActivityStops() { mShadowActivityManager.setLockTaskModeState(ActivityManager.LOCK_TASK_MODE_NONE); mActivityLifecycle.create().start().resume(); verifyBroadcastedIntent( filterFor(CastWebContentsIntentUtils.ACTION_ACTIVITY_STOPPED), () -> { - mActivity.onUserLeaveHint(); + mActivityLifecycle.pause().stop(); assertFalse(mShadowActivity.isFinishing()); - mActivityLifecycle.pause().stop().destroy(); }, false); } @Test - public void testClosesWhenUserLeaveInLockTaskMode() { + public void testClosesWhenActivityStopsInLockTaskMode() { mShadowActivityManager.setLockTaskModeState(ActivityManager.LOCK_TASK_MODE_LOCKED); mActivityLifecycle.create().start().resume(); verifyBroadcastedIntent( filterFor(CastWebContentsIntentUtils.ACTION_ACTIVITY_STOPPED), () -> { - mActivity.onUserLeaveHint(); + mActivityLifecycle.pause().stop(); assertTrue(mShadowActivity.isFinishing()); - mActivityLifecycle.pause().stop().destroy(); }, true); } @Test - public void testClosesWhenUserLeaveInLockTaskModePinned() { + public void testClosesWhenActivityStopsInLockTaskModePinned() { mShadowActivityManager.setLockTaskModeState(ActivityManager.LOCK_TASK_MODE_PINNED); mActivityLifecycle.create().start().resume(); verifyBroadcastedIntent( filterFor(CastWebContentsIntentUtils.ACTION_ACTIVITY_STOPPED), () -> { - mActivity.onUserLeaveHint(); + mActivityLifecycle.pause().stop(); assertTrue(mShadowActivity.isFinishing()); - mActivityLifecycle.pause().stop().destroy(); }, true); }
diff --git a/chromecast/browser/cast_download_manager_delegate.cc b/chromecast/browser/cast_download_manager_delegate.cc index 827b54f4..a9979152 100644 --- a/chromecast/browser/cast_download_manager_delegate.cc +++ b/chromecast/browser/cast_download_manager_delegate.cc
@@ -32,7 +32,7 @@ std::move(*callback).Run( empty, download::DownloadItem::TARGET_DISPOSITION_OVERWRITE, download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT, - download::DownloadItem::MixedContentStatus::UNKNOWN, empty, + download::DownloadItem::MixedContentStatus::UNKNOWN, empty, empty, absl::nullopt /*download_schedule*/, download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); return true;
diff --git a/chromeos/components/BUILD.gn b/chromeos/components/BUILD.gn index c41601d7..9f2ee86b9b 100644 --- a/chromeos/components/BUILD.gn +++ b/chromeos/components/BUILD.gn
@@ -26,7 +26,6 @@ "//chromeos/components/local_search_service:unit_tests", "//chromeos/components/local_search_service/public/mojom:unit_tests", "//chromeos/components/mojo_bootstrap:unit_tests", - "//chromeos/components/multidevice:unit_tests", "//chromeos/components/onc:unit_tests", "//chromeos/components/quick_answers:unit_tests", "//chromeos/components/sensors:unit_tests",
diff --git a/chromeos/components/multidevice/DEPS b/chromeos/components/multidevice/DEPS deleted file mode 100644 index 6583fd24..0000000 --- a/chromeos/components/multidevice/DEPS +++ /dev/null
@@ -1,6 +0,0 @@ -include_rules = [ - "+ash/services/device_sync/proto", - "+device/bluetooth/public/cpp", - "+mojo/public/cpp", - "+third_party/securemessage", -]
diff --git a/chromeos/crosapi/mojom/app_service_types_traits_unittest.cc b/chromeos/crosapi/mojom/app_service_types_traits_unittest.cc index 998b86c8..b845e31 100644 --- a/chromeos/crosapi/mojom/app_service_types_traits_unittest.cc +++ b/chromeos/crosapi/mojom/app_service_types_traits_unittest.cc
@@ -44,9 +44,8 @@ input->paused = false; auto intent_filter = std::make_unique<apps::IntentFilter>(); - apps_util::AddSingleValueCondition(apps::ConditionType::kScheme, "https", - apps::PatternMatchType::kNone, - intent_filter); + intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme, "https", + apps::PatternMatchType::kNone); intent_filter->activity_name = "activity_name"; intent_filter->activity_label = "activity_label"; input->intent_filters.push_back(std::move(intent_filter)); @@ -135,9 +134,8 @@ input->paused = false; auto intent_filter = std::make_unique<apps::IntentFilter>(); - apps_util::AddSingleValueCondition(apps::ConditionType::kScheme, "https", - apps::PatternMatchType::kNone, - intent_filter); + intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme, "https", + apps::PatternMatchType::kNone); input->intent_filters.push_back(std::move(intent_filter)); input->window_mode = apps::WindowMode::kBrowser; input->allow_uninstall = true; @@ -558,30 +556,22 @@ TEST(AppServiceTypesTraitsTest, RoundTripIntentFilters) { auto input = std::make_unique<apps::App>(apps::AppType::kArc, "abcdefg"); auto intent_filter = std::make_unique<apps::IntentFilter>(); - apps_util::AddSingleValueCondition(apps::ConditionType::kScheme, "1", - apps::PatternMatchType::kNone, - intent_filter); - apps_util::AddSingleValueCondition(apps::ConditionType::kHost, "2", - apps::PatternMatchType::kLiteral, - intent_filter); - apps_util::AddSingleValueCondition(apps::ConditionType::kPattern, "3", - apps::PatternMatchType::kPrefix, - intent_filter); - apps_util::AddSingleValueCondition(apps::ConditionType::kAction, "4", - apps::PatternMatchType::kGlob, - intent_filter); - apps_util::AddSingleValueCondition(apps::ConditionType::kMimeType, "5", - apps::PatternMatchType::kMimeType, - intent_filter); - apps_util::AddSingleValueCondition(apps::ConditionType::kFile, "6", - apps::PatternMatchType::kMimeType, - intent_filter); - apps_util::AddSingleValueCondition(apps::ConditionType::kFile, "7", - apps::PatternMatchType::kFileExtension, - intent_filter); - apps_util::AddSingleValueCondition(apps::ConditionType::kHost, "8", - apps::PatternMatchType::kSuffix, - intent_filter); + intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme, "1", + apps::PatternMatchType::kNone); + intent_filter->AddSingleValueCondition(apps::ConditionType::kHost, "2", + apps::PatternMatchType::kLiteral); + intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern, "3", + apps::PatternMatchType::kPrefix); + intent_filter->AddSingleValueCondition(apps::ConditionType::kAction, "4", + apps::PatternMatchType::kGlob); + intent_filter->AddSingleValueCondition(apps::ConditionType::kMimeType, "5", + apps::PatternMatchType::kMimeType); + intent_filter->AddSingleValueCondition(apps::ConditionType::kFile, "6", + apps::PatternMatchType::kMimeType); + intent_filter->AddSingleValueCondition( + apps::ConditionType::kFile, "7", apps::PatternMatchType::kFileExtension); + intent_filter->AddSingleValueCondition(apps::ConditionType::kHost, "8", + apps::PatternMatchType::kSuffix); input->intent_filters.push_back(std::move(intent_filter)); apps::AppPtr output;
diff --git a/chromeos/crosapi/mojom/launcher_search.mojom b/chromeos/crosapi/mojom/launcher_search.mojom index 6571d4bb..31dafdc 100644 --- a/chromeos/crosapi/mojom/launcher_search.mojom +++ b/chromeos/crosapi/mojom/launcher_search.mojom
@@ -22,6 +22,8 @@ // Search session has been cancelled due to a newer query. No more results // will be sent. kCancelled = 3, + // The AutocompleteController is unavailable. No more results will be sent. + [MinVersion=1] kBackendUnavailable = 4, }; // Enum represents the result type. @@ -34,7 +36,7 @@ }; // Struct represents search result. -// Next min ID: 14 +// Next min ID: 15 [Stable] struct SearchResult { // Type of the result. Used to distinguish between different types of result. @@ -94,6 +96,7 @@ kSearch = 5, kHistory = 6, kCalculator = 7, + [MinVersion=1] kOpenTab = 8, }; // Enum representing the Omnibox answer subtype.
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt index a6274265..6c66e4fc 100644 --- a/chromeos/profiles/atom.afdo.newest.txt +++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-atom-101-4928.0-1647252934-benchmark-101.0.4948.0-r1-redacted.afdo.xz +chromeos-chrome-amd64-atom-101-4928.0-1647252934-benchmark-101.0.4950.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt index 43d65629..6e3f038 100644 --- a/chromeos/profiles/bigcore.afdo.newest.txt +++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-bigcore-101-4928.0-1647252364-benchmark-101.0.4948.0-r1-redacted.afdo.xz +chromeos-chrome-amd64-bigcore-101-4928.0-1647252364-benchmark-101.0.4950.0-r1-redacted.afdo.xz
diff --git a/components/BUILD.gn b/components/BUILD.gn index 3b9df0b..1f80dbc 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn
@@ -498,7 +498,10 @@ } if (!is_android) { - deps += [ "//components/commerce/core:commerce_heuristics_data_unittests" ] + deps += [ + "//components/commerce/core:commerce_heuristics_data_unittests", + "//components/commerce/core:feature_list_unittests", + ] } if (enable_library_cdms) {
diff --git a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h index 702dc6fa..f8614ef7 100644 --- a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h +++ b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
@@ -31,7 +31,9 @@ VIRTUAL_CARD_ENROLLMENT_BUBBLE_NOT_INTERACTED = 3, // The bubble lost focus and was deactivated. VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS = 4, - kMaxValue = VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS, + // The user cancelled the bubble. + VIRTUAL_CARD_ENROLLMENT_BUBBLE_CANCELLED = 5, + kMaxValue = VIRTUAL_CARD_ENROLLMENT_BUBBLE_CANCELLED, }; // Metrics to record the source that prompted the virtual card enrollment
diff --git a/components/autofill_assistant/android/BUILD.gn b/components/autofill_assistant/android/BUILD.gn index 6eac8dd..6b96c0d 100644 --- a/components/autofill_assistant/android/BUILD.gn +++ b/components/autofill_assistant/android/BUILD.gn
@@ -348,7 +348,6 @@ "internal/java/res/drawable/autofill_assistant_default_details.xml", "internal/java/res/drawable/autofill_assistant_details_bg.xml", "internal/java/res/drawable/autofill_assistant_details_list_divider.xml", - "internal/java/res/drawable/autofill_assistant_lightblue_rect_bg.xml", "internal/java/res/drawable/autofill_assistant_rounded_corner_background.xml", "internal/java/res/drawable/autofill_assistant_swipe_indicator.xml", "internal/java/res/drawable/ic_add_outline_white_24dp.xml", @@ -404,7 +403,6 @@ "internal/java/res/layout/autofill_assistant_popup_list_section.xml", "internal/java/res/layout/autofill_assistant_static_text_section.xml", "internal/java/res/menu/profile_icon_menu.xml", - "internal/java/res/values-night-v17/colors.xml", "internal/java/res/values-v17/attrs.xml", "internal/java/res/values-v17/colors.xml", "internal/java/res/values-v17/dimens.xml",
diff --git a/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_lightblue_rect_bg.xml b/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_lightblue_rect_bg.xml deleted file mode 100644 index 2d0b4a5..0000000 --- a/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_lightblue_rect_bg.xml +++ /dev/null
@@ -1,11 +0,0 @@ -<!-- Copyright 2019 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. --> -<shape - xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <corners - android:radius="4dp" /> - <solid - android:color="@color/autofill_assistant_light_blue" /> -</shape> \ No newline at end of file
diff --git a/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_terms_and_conditions.xml b/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_terms_and_conditions.xml index 0b7cd3ef..98695c5d 100644 --- a/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_terms_and_conditions.xml +++ b/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_terms_and_conditions.xml
@@ -28,7 +28,7 @@ android:layout_height="wrap_content" android:layout_marginStart="@dimen/autofill_assistant_bottombar_horizontal_spacing" android:layout_marginEnd="@dimen/autofill_assistant_bottombar_horizontal_spacing" - android:background="@drawable/autofill_assistant_lightblue_rect_bg" android:padding="8dp" - android:textAppearance="@style/TextAppearance.TextSmall.Secondary"/> + android:paddingStart="40dp" + android:textAppearance="@style/TextAppearance.TextSmall.Secondary.Baseline.Dark"/> </LinearLayout>
diff --git a/components/autofill_assistant/android/internal/java/res/values-night-v17/colors.xml b/components/autofill_assistant/android/internal/java/res/values-night-v17/colors.xml deleted file mode 100644 index ae4be5f..0000000 --- a/components/autofill_assistant/android/internal/java/res/values-night-v17/colors.xml +++ /dev/null
@@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. --> - -<resources> - <!-- - TODO(crbuc.com/806868): Use Chrome approved colors and remove this. - - Please see src/ui/android/java/res/values/colors.xml for the shared common colors. - --> - <color name="autofill_assistant_light_blue">@color/modern_grey_900</color> -</resources>
diff --git a/components/autofill_assistant/android/internal/java/res/values-v17/colors.xml b/components/autofill_assistant/android/internal/java/res/values-v17/colors.xml index 56c401a..e045d57 100644 --- a/components/autofill_assistant/android/internal/java/res/values-v17/colors.xml +++ b/components/autofill_assistant/android/internal/java/res/values-v17/colors.xml
@@ -9,7 +9,6 @@ Please see src/ui/android/java/res/values/colors.xml for the shared common colors. --> - <color name="autofill_assistant_light_blue">@color/modern_blue_600_alpha_10</color> <color name="autofill_assistant_actions_shadow_color">@color/modern_grey_100</color> <color name="autofill_assistant_details_divider_color">@color/default_chip_outline_color</color> </resources>
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto index 7f315c8..0dfb6c9b 100644 --- a/components/autofill_assistant/browser/service.proto +++ b/components/autofill_assistant/browser/service.proto
@@ -1662,6 +1662,9 @@ // Timeout, defaults to 5s. // TODO(b/218482826): Consider moving this to settings. optional int32 model_timeout_ms = 3 [default = 5000]; + // If true, ignore the objective and treat it as a wildcard '*' when + // matching. + optional bool ignore_objective = 4; } optional SemanticInformation semantic_information = 11;
diff --git a/components/autofill_assistant/browser/web/element_finder.cc b/components/autofill_assistant/browser/web/element_finder.cc index c533269a..a8bd92c 100644 --- a/components/autofill_assistant/browser/web/element_finder.cc +++ b/components/autofill_assistant/browser/web/element_finder.cc
@@ -284,6 +284,7 @@ driver->GetAutofillAssistantAgent()->GetSemanticNodes( selector_.proto.semantic_information().semantic_role(), selector_.proto.semantic_information().objective(), + selector_.proto.semantic_information().ignore_objective(), base::Milliseconds( selector_.proto.semantic_information().model_timeout_ms()), base::BindOnce(&ElementFinder::OnRunAnnotateDomModelOnFrame,
diff --git a/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/components/autofill_assistant/browser/web/web_controller_browsertest.cc index a067f62..eebcc9a 100644 --- a/components/autofill_assistant/browser/web/web_controller_browsertest.cc +++ b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -104,6 +104,7 @@ GetSemanticNodes, (int32_t role, int32_t objective, + bool ignore_objective, base::TimeDelta model_timeout, base::OnceCallback<void(mojom::NodeDataStatus, const std::vector<NodeData>&)> callback), @@ -3396,11 +3397,11 @@ NodeData node_data; node_data.backend_node_id = backend_node_id; EXPECT_CALL(autofill_assistant_agent_, - GetSemanticNodes(1, 2, base::Milliseconds(5000), _)) - .WillOnce(RunOnceCallback<3>(mojom::NodeDataStatus::kSuccess, + GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _)) + .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess, std::vector<NodeData>{node_data})) // Capture any other frames. - .WillRepeatedly(RunOnceCallback<3>( + .WillRepeatedly(RunOnceCallback<4>( mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>())); // We pretend that the button is the correct element. @@ -3422,11 +3423,11 @@ NodeData node_data; node_data.backend_node_id = backend_node_id; EXPECT_CALL(autofill_assistant_agent_, - GetSemanticNodes(1, 2, base::Milliseconds(5000), _)) - .WillOnce(RunOnceCallback<3>(mojom::NodeDataStatus::kSuccess, + GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _)) + .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess, std::vector<NodeData>{node_data})) // Capture any other frames. - .WillRepeatedly(RunOnceCallback<3>( + .WillRepeatedly(RunOnceCallback<4>( mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>())); // We pretend that the button is the correct element. @@ -3442,8 +3443,8 @@ // All frames return an empty list as a result. EXPECT_CALL(autofill_assistant_agent_, - GetSemanticNodes(1, 2, base::Milliseconds(5000), _)) - .WillRepeatedly(RunOnceCallback<3>(mojom::NodeDataStatus::kSuccess, + GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _)) + .WillRepeatedly(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess, std::vector<NodeData>{})); FindElementExpectEmptyResult(Selector(proto)); @@ -3461,13 +3462,13 @@ NodeData node_data_other; node_data_other.backend_node_id = 13; EXPECT_CALL(autofill_assistant_agent_, - GetSemanticNodes(1, 2, base::Milliseconds(5000), _)) - .WillOnce(RunOnceCallback<3>(mojom::NodeDataStatus::kSuccess, + GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _)) + .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess, std::vector<NodeData>{node_data})) - .WillOnce(RunOnceCallback<3>(mojom::NodeDataStatus::kSuccess, + .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess, std::vector<NodeData>{node_data_other})) // Capture any other frames. - .WillRepeatedly(RunOnceCallback<3>( + .WillRepeatedly(RunOnceCallback<4>( mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>())); // Two elements are found in different frames. @@ -3476,4 +3477,24 @@ EXPECT_EQ(TOO_MANY_ELEMENTS, status.proto_status()); } +IN_PROC_BROWSER_TEST_F( + WebControllerBrowserTest, + ElementExistenceCheckWithSemanticModelUsesIgnoreObjective) { + EXPECT_CALL(autofill_assistant_agent_, + GetSemanticNodes(1, 2, true, base::Milliseconds(5000), _)) + .WillRepeatedly(RunOnceCallback<4>( + mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>())); + + SelectorProto proto; + auto* semantic_information = proto.mutable_semantic_information(); + semantic_information->set_semantic_role(1); + semantic_information->set_objective(2); + // All we want is this to be propagated to the GetSemanticNodes call as + // configured in the previous expectation. + semantic_information->set_ignore_objective(true); + + ClientStatus ignore_status; + FindElement(Selector(proto), &ignore_status, nullptr); +} + } // namespace autofill_assistant
diff --git a/components/autofill_assistant/content/common/autofill_assistant_agent.mojom b/components/autofill_assistant/content/common/autofill_assistant_agent.mojom index 2fbde0d..ff86b393 100644 --- a/components/autofill_assistant/content/common/autofill_assistant_agent.mojom +++ b/components/autofill_assistant/content/common/autofill_assistant_agent.mojom
@@ -12,6 +12,8 @@ interface AutofillAssistantAgent { // Evaluates all input, textarea and select nodes in the frame's document and // returns all that match the given semantic role and objective. - GetSemanticNodes(int32 role, int32 objective, mojo_base.mojom.TimeDelta timeout) - => (NodeDataStatus status, array<autofill_assistant.mojom.NodeData> nodes); + GetSemanticNodes(int32 role, int32 objective, bool ignore_objective, + mojo_base.mojom.TimeDelta timeout) + => (NodeDataStatus status, + array<autofill_assistant.mojom.NodeData> nodes); };
diff --git a/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc b/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc index 7a36388b..f7b2cb0 100644 --- a/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc +++ b/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc
@@ -47,6 +47,7 @@ void AutofillAssistantAgent::GetSemanticNodes( int32_t role, int32_t objective, + bool ignore_objective, base::TimeDelta model_timeout, GetSemanticNodesCallback callback) { blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); @@ -60,7 +61,7 @@ model_timeout, base::BindOnce(&AutofillAssistantAgent::OnGetModelFile, weak_ptr_factory_.GetWeakPtr(), base::Time::Now(), frame, - role, objective, std::move(callback))); + role, objective, ignore_objective, std::move(callback))); } void AutofillAssistantAgent::GetAnnotateDomModel( @@ -80,6 +81,7 @@ blink::WebLocalFrame* frame, int32_t role, int32_t objective, + bool ignore_objective, GetSemanticNodesCallback callback, mojom::ModelStatus model_status, base::File model) { @@ -122,10 +124,10 @@ for (const auto& node_signal : node_signals) { auto result = model_executor.ExecuteModelWithInput(node_signal); DVLOG(3) << "Annotated node with result: role: " << result->first - << " and objective: " << result->second; - // TODO(mcarlen): Use the objective wildcard here to ignore the second part - // of the condition. - if (result && result->first == role && result->second == objective) { + << " and objective: " << result->second + << " (or ignore: " << ignore_objective << ")"; + if (result && result->first == role && + (result->second == objective || ignore_objective)) { NodeData node_data; node_data.backend_node_id = node_signal.backend_node_id; nodes.push_back(node_data);
diff --git a/components/autofill_assistant/content/renderer/autofill_assistant_agent.h b/components/autofill_assistant/content/renderer/autofill_assistant_agent.h index 9f53341..b546af6 100644 --- a/components/autofill_assistant/content/renderer/autofill_assistant_agent.h +++ b/components/autofill_assistant/content/renderer/autofill_assistant_agent.h
@@ -44,6 +44,7 @@ // mojom::AutofillAssistantAgent: void GetSemanticNodes(int32_t role, int32_t objective, + bool ignore_objective, base::TimeDelta model_timeout, GetSemanticNodesCallback callback) override; @@ -61,6 +62,7 @@ blink::WebLocalFrame* frame, int32_t role, int32_t objective, + bool ignore_objective, GetSemanticNodesCallback callback, mojom::ModelStatus model_status, base::File model);
diff --git a/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc b/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc index 6c09851..36900340 100644 --- a/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc +++ b/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
@@ -8,6 +8,7 @@ #include "base/files/file_path.h" #include "base/path_service.h" #include "base/run_loop.h" +#include "base/test/bind.h" #include "base/test/gmock_callback_support.h" #include "base/test/mock_callback.h" #include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h" @@ -24,6 +25,7 @@ using ::base::test::RunOnceCallback; using ::testing::_; +using ::testing::SizeIs; class MockAutofillAssistantDriver : public mojom::AutofillAssistantDriver { public: @@ -95,7 +97,7 @@ base::MockCallback<base::OnceCallback<void(mojom::NodeDataStatus, const std::vector<NodeData>&)>> callback; - EXPECT_CALL(callback, Run(mojom::NodeDataStatus::kSuccess, _)); + EXPECT_CALL(callback, Run(mojom::NodeDataStatus::kSuccess, SizeIs(1))); LoadHTML(R"( <div> @@ -106,6 +108,7 @@ autofill_assistant_agent_->GetSemanticNodes( /* role= */ 47 /* ADDRESS_LINE1 */, /* objective= */ 7 /* FILL_DELIVERY_ADDRESS */, + /* ignore_objective= */ false, /* model_timeout= */ base::Milliseconds(1000), callback.Get()); base::RunLoop().RunUntilIdle(); @@ -130,6 +133,7 @@ autofill_assistant_agent_->GetSemanticNodes( /* role= */ 47 /* ADDRESS_LINE1 */, /* objective= */ 7 /* FILL_DELIVERY_ADDRESS */, + /* ignore_objective= */ false, /* model_timeout= */ base::Milliseconds(0), callback.Get()); base::RunLoop().RunUntilIdle(); @@ -155,10 +159,35 @@ autofill_assistant_agent_->GetSemanticNodes( /* role= */ 47 /* ADDRESS_LINE1 */, /* objective= */ 7 /* FILL_DELIVERY_ADDRESS */, + /* ignore_objective= */ false, /* model_timeout= */ base::Milliseconds(1000), callback.Get()); base::RunLoop().RunUntilIdle(); } +TEST_F(AutofillAssistantAgentBrowserTest, GetSemanticNodesIgnoreObjective) { + EXPECT_CALL(autofill_assistant_driver_, GetAnnotateDomModel) + .WillOnce(RunOnceCallback<1>(mojom::ModelStatus::kSuccess, + model_file_.Duplicate())); + + LoadHTML(R"( + <div> + <h1>Shipping address</h1> + <label for="street">Street Address</label><input id="street"> + </div>)"); + + base::MockCallback<base::OnceCallback<void(mojom::NodeDataStatus, + const std::vector<NodeData>&)>> + callback; + EXPECT_CALL(callback, Run(mojom::NodeDataStatus::kSuccess, SizeIs(1))); + + autofill_assistant_agent_->GetSemanticNodes( + /* role= */ 47 /* ADDRESS_LINE1 */, + /* objective= */ 6 /* FILL_BILLING_ADDRESS */, + /* ignore_objective= */ true, + /* model_timeout= */ base::Milliseconds(1000), callback.Get()); + base::RunLoop().RunUntilIdle(); +} + } // namespace } // namespace autofill_assistant
diff --git a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java index 7172d91..edcdbd6 100644 --- a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java +++ b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java
@@ -7,22 +7,16 @@ import android.app.Notification; import android.app.Service; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.Build; -import android.os.Process; import androidx.annotation.VisibleForTesting; import androidx.core.app.ServiceCompat; import androidx.core.content.ContextCompat; -import org.chromium.base.ApiCompatibilityUtils; -import org.chromium.base.BuildInfo; import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.compat.ApiHelperForQ; import org.chromium.base.compat.ApiHelperForS; -import org.chromium.ui.permissions.PermissionConstants; -import org.chromium.ui.permissions.PermissionPrefs; /** * Utility functions that call into Android foreground service related API, and provides @@ -98,26 +92,4 @@ Log.e(TAG, "Failed to stop foreground service, ", e); } } - - /** - * Utility method to check if any foreground service can be started. Starting from android T, - * foreground services are not allowed to start if user hasn't been asked for notification - * permissions. Media is excluded from this restriction. - * @return - */ - public static boolean canStartForegroundServiceExcludingMedia() { - if (!BuildInfo.isAtLeastT()) return true; - return hasEverRequestedNotificationPermission(); - } - - private static boolean hasEverRequestedNotificationPermission() { - boolean hasPermission = - ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationContext(), - PermissionConstants.NOTIFICATION_PERMISSION, Process.myPid(), - Process.myUid()) - == PackageManager.PERMISSION_GRANTED; - if (hasPermission) return true; - - return PermissionPrefs.getAndroidNotificationPermissionRequestTimestamp() != 0; - } }
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGenerator.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGenerator.java index 4ce384a..76bef0a 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGenerator.java +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGenerator.java
@@ -14,6 +14,7 @@ import android.text.TextPaint; import android.text.TextUtils; +import androidx.annotation.ColorInt; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -99,7 +100,7 @@ /** * Sets the background color to use when generating icons. */ - public void setBackgroundColor(int color) { + public void setBackgroundColor(@ColorInt int color) { mBackgroundPaint.setColor(color); }
diff --git a/components/browsing_topics/BUILD.gn b/components/browsing_topics/BUILD.gn index 18e1135a..0d028813 100644 --- a/components/browsing_topics/BUILD.gn +++ b/components/browsing_topics/BUILD.gn
@@ -4,6 +4,8 @@ source_set("browsing_topics") { sources = [ + "browsing_topics_calculator.cc", + "browsing_topics_calculator.h", "browsing_topics_service.h", "browsing_topics_service_impl.cc", "browsing_topics_service_impl.h", @@ -20,16 +22,33 @@ deps = [ "//base", "//components/browsing_topics/common:common", + "//components/history/content/browser", + "//components/history/core/browser", "//components/keyed_service/core", + "//components/optimization_guide/content/browser", "//components/privacy_sandbox", + "//content/public/browser", "//crypto", "//third_party/blink/public/common", ] } +source_set("test_support") { + testonly = true + sources = [ + "test_util.cc", + "test_util.h", + ] + + public_deps = [ "//base" ] + + deps = [ ":browsing_topics" ] +} + source_set("unit_tests") { testonly = true sources = [ + "browsing_topics_calculator_unittest.cc", "browsing_topics_state_unittest.cc", "epoch_topics_unittest.cc", "topic_and_domains_unittest.cc", @@ -38,9 +57,21 @@ deps = [ ":browsing_topics", + ":test_support", "//base", "//base/test:test_support", + "//components/content_settings/core/test:test_support", + "//components/history/core/browser:browser", + "//components/history/core/test", + "//components/optimization_guide/content/browser:browser", + "//components/optimization_guide/content/browser:test_support", + "//components/optimization_guide/core:test_support", "//components/prefs:test_support", + "//components/privacy_sandbox:privacy_sandbox", + "//components/privacy_sandbox:privacy_sandbox_prefs", + "//components/privacy_sandbox:test_support", + "//components/sync_preferences:test_support", + "//content/test:test_support", "//testing/gtest", "//third_party/blink/public/common", ]
diff --git a/components/browsing_topics/DEPS b/components/browsing_topics/DEPS index dba046b..26ef4ba 100644 --- a/components/browsing_topics/DEPS +++ b/components/browsing_topics/DEPS
@@ -1,6 +1,17 @@ include_rules = [ - "+crypto", - "+third_party/blink/public/common", + "+components/history", + "+components/optimization_guide", "+components/keyed_service", "+components/privacy_sandbox", + "+content/public/browser", + "+content/public/test", + "+crypto", + "+third_party/blink/public/common", ] + +specific_include_rules = { + ".*_unittest.cc": [ + "+components/content_settings/core", + "+components/sync_preferences", + ], +}
diff --git a/components/browsing_topics/browsing_topics_calculator.cc b/components/browsing_topics/browsing_topics_calculator.cc new file mode 100644 index 0000000..88ade005 --- /dev/null +++ b/components/browsing_topics/browsing_topics_calculator.cc
@@ -0,0 +1,370 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/browsing_topics/browsing_topics_calculator.h" + +#include "base/containers/contains.h" +#include "base/metrics/histogram_functions.h" +#include "base/rand_util.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/browsing_topics/util.h" +#include "components/history/core/browser/history_service.h" +#include "components/optimization_guide/content/browser/page_content_annotations_service.h" +#include "components/privacy_sandbox/canonical_topic.h" +#include "components/privacy_sandbox/privacy_sandbox_settings.h" +#include "content/public/browser/browsing_topics_site_data_manager.h" +#include "third_party/blink/public/common/features.h" + +namespace browsing_topics { + +namespace { + +// Derive the mapping from hosts to topics and the mapping from topics to hosts. +// Precondition: the annotation didn't fail in general (e.g. `ModelInfo` is +// valid). +void DeriveHostTopicsMapAndTopicHostsMap( + const std::vector<std::string>& raw_hosts, + const std::vector<optimization_guide::BatchAnnotationResult>& results, + std::map<HashedHost, std::set<Topic>>& host_topics_map, + std::map<Topic, std::set<HashedHost>>& topic_hosts_map) { + DCHECK(host_topics_map.empty()); + DCHECK(topic_hosts_map.empty()); + + DCHECK_EQ(raw_hosts.size(), results.size()); + + for (size_t i = 0; i < results.size(); ++i) { + const optimization_guide::BatchAnnotationResult& result = results[i]; + const std::string raw_host = raw_hosts[i]; + + // As long as the annotation didn't fail in general, the individual + // `result.topics()` should always be valid. + const std::vector<optimization_guide::WeightedIdentifier>& + annotation_result_topics = result.topics().value(); + + HashedHost host = HashMainFrameHostForStorage(raw_host); + + for (const optimization_guide::WeightedIdentifier& annotation_result_topic : + annotation_result_topics) { + // Note that `annotation_result_topic.weight()` is ignored. This is the + // intended use of the model for the Topics API. + Topic topic = Topic(annotation_result_topic.value()); + + topic_hosts_map[topic].insert(host); + host_topics_map[host].insert(topic); + } + } +} + +// For `topic`, derive the context domains that observed it. This is done by +// first getting the hosts about `topic` from `topic_hosts_map`, and +// for each site, get the callers (context domains) that were on that site and +// add the callers to a result set. +std::set<HashedDomain> GetTopicObservationDomains( + const Topic& topic, + const std::map<Topic, std::set<HashedHost>>& topic_hosts_map, + const std::map<HashedHost, std::vector<HashedDomain>>& + host_context_domains_map) { + std::set<HashedDomain> topic_observation_domains; + + // If `topic` was padded, it may not exist in `topic_hosts_map`. In this + // case, return an empty set. + auto it = topic_hosts_map.find(topic); + if (it == topic_hosts_map.end()) + return std::set<HashedDomain>(); + + const std::set<HashedHost>& hosts = it->second; + + for (const HashedHost& host : hosts) { + // `host` came from the history database, and it may not exist in the + // `host_context_domains_map` which came from the usage contexts + // database, due to e.g. per-context data deletion, database errors, etc. + // In this case, continue checking other hosts. + auto it = host_context_domains_map.find(host); + if (it == host_context_domains_map.end()) + continue; + + const std::vector<HashedDomain>& context_domains = it->second; + + for (const HashedDomain& context_domain : context_domains) { + topic_observation_domains.insert(context_domain); + + // To limit memory usage, cap the number of context domains to keep + // per-topic. The larger `HashedDomain`s will be kept. This is fair, as + // the hashing for context domains is per-user, so we are not + // prioritizing any domains in general. + if (topic_observation_domains.size() > + static_cast<size_t>( + blink::features:: + kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToKeepPerTopic + .Get())) { + topic_observation_domains.erase(topic_observation_domains.begin()); + } + } + } + + return topic_observation_domains; +} + +} // namespace + +BrowsingTopicsCalculator::BrowsingTopicsCalculator( + privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings, + history::HistoryService* history_service, + content::BrowsingTopicsSiteDataManager* site_data_manager, + optimization_guide::PageContentAnnotationsService* annotations_service, + CalculateCompletedCallback callback) + : privacy_sandbox_settings_(privacy_sandbox_settings), + history_service_(history_service), + site_data_manager_(site_data_manager), + annotations_service_(annotations_service), + calculate_completed_callback_(std::move(callback)), + calculation_time_(base::Time::Now()) { + // Continue asynchronously so that `calculate_completed_callback_` isn't + // called synchronously while `this` is being constructed. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&BrowsingTopicsCalculator::CheckCanCalculate, + weak_ptr_factory_.GetWeakPtr())); +} + +BrowsingTopicsCalculator::~BrowsingTopicsCalculator() = default; + +uint64_t BrowsingTopicsCalculator::GenerateRandUint64() { + return base::RandUint64(); +} + +void BrowsingTopicsCalculator::DeriveTopTopics( + const std::map<HashedHost, size_t>& history_hosts_count, + const std::map<HashedHost, std::set<Topic>>& host_topics_map, + size_t taxonomy_size, + std::vector<Topic>& top_topics, + size_t& padded_top_topics_start_index) { + DCHECK(top_topics.empty()); + DCHECK_EQ(padded_top_topics_start_index, 0u); + + // Derive the frequency of each topic, by summing up the frequencies of the + // associated hosts. TODO(yaoxia): consider applying inverse frequency of + // topics (https://github.com/jkarlin/topics/issues/42). + std::map<Topic, size_t> topics_count; + for (auto const& [host, host_count] : history_hosts_count) { + const std::set<Topic>& topics = host_topics_map.at(host); + for (const Topic& topic : topics) { + topics_count[topic] += host_count; + } + } + + DCHECK_LE( + static_cast<size_t>( + blink::features::kBrowsingTopicsNumberOfTopTopicsPerEpoch.Get()), + taxonomy_size); + + // Get the top up to `kBrowsingTopicsNumberOfTopTopicsPerEpoch` topics, + // sorted by decreasing count. + std::vector<std::pair<Topic, size_t>> top_topics_count(std::min( + static_cast<size_t>( + blink::features::kBrowsingTopicsNumberOfTopTopicsPerEpoch.Get()), + topics_count.size())); + + std::partial_sort_copy( + topics_count.begin(), topics_count.end(), top_topics_count.begin(), + top_topics_count.end(), + [](auto& left, auto& right) { return left.second > right.second; }); + + std::transform(top_topics_count.begin(), top_topics_count.end(), + std::back_inserter(top_topics), + [](auto& topic_count) { return topic_count.first; }); + + padded_top_topics_start_index = top_topics.size(); + + // Pad the top topics with distinct random topics until we have + // `kBrowsingTopicsNumberOfTopTopicsPerEpoch` topics. + while (top_topics.size() < + static_cast<size_t>( + blink::features::kBrowsingTopicsNumberOfTopTopicsPerEpoch.Get())) { + Topic padded_topic(0); + + do { + int padded_topic_index = + base::checked_cast<int>(GenerateRandUint64() % taxonomy_size); + padded_topic = Topic(padded_topic_index + 1); + } while (base::Contains(top_topics, padded_topic)); + + top_topics.emplace_back(std::move(padded_topic)); + } +} + +void BrowsingTopicsCalculator::CheckCanCalculate() { + if (!privacy_sandbox_settings_->IsTopicsAllowed()) { + OnCalculateCompleted(CalculatorResultStatus::kFailurePermissionDenied); + return; + } + + // Get the the api usages context map (from the calling context domain to a + // set of history hosts) so that we can figure out which topics the APIs were + // called on. + site_data_manager_->GetBrowsingTopicsApiUsage( + /*begin_time=*/DeriveApiUsageContextDataStartTime( + calculation_time_, + privacy_sandbox_settings_->TopicsDataAccessibleSince()), + /*end_time=*/calculation_time_, + base::BindOnce(&BrowsingTopicsCalculator:: + OnGetRecentBrowsingTopicsApiUsagesCompleted, + weak_ptr_factory_.GetWeakPtr())); +} + +void BrowsingTopicsCalculator::OnGetRecentBrowsingTopicsApiUsagesCompleted( + browsing_topics::ApiUsageContextQueryResult result) { + DCHECK(host_context_domains_map_.empty()); + + if (!result.success) { + OnCalculateCompleted( + CalculatorResultStatus::kFailureApiUsageContextQueryError); + return; + } + + for (const ApiUsageContext& usage_context : result.api_usage_contexts) { + host_context_domains_map_[usage_context.hashed_main_frame_host] + .emplace_back(usage_context.hashed_context_domain); + } + + // `ApiUsageContext::hashed_main_frame_host` is a hashed number. To get the + // topic associated with it, we will need to match it against a set of raw + // hosts with topics. Thus, here we query the history with the larger time + // range (from DeriveApiUsageContextDataStartTime() to `calculation_time_`) to + // get the raw hosts. + history::QueryOptions options; + options.begin_time = DeriveApiUsageContextDataStartTime( + calculation_time_, + privacy_sandbox_settings_->TopicsDataAccessibleSince()); + options.end_time = calculation_time_; + options.duplicate_policy = history::QueryOptions::KEEP_ALL_DUPLICATES; + + history_service_->QueryHistory( + std::u16string(), options, + base::BindOnce( + &BrowsingTopicsCalculator::OnGetRecentlyVisitedURLsCompleted, + weak_ptr_factory_.GetWeakPtr()), + &history_task_tracker_); +} + +void BrowsingTopicsCalculator::OnGetRecentlyVisitedURLsCompleted( + history::QueryResults results) { + DCHECK(history_hosts_count_.empty()); + + std::set<std::string> raw_hosts; + + for (const history::URLResult& url_result : results) { + if (!(url_result.content_annotations().annotation_flags & + history::VisitContentAnnotationFlag::kBrowsingTopicsEligible)) { + continue; + } + + std::string raw_host = url_result.url().host(); + raw_hosts.insert(raw_host); + + if (url_result.visit_time() >= + DeriveHistoryDataStartTime( + calculation_time_, + privacy_sandbox_settings_->TopicsDataAccessibleSince())) { + HashedHost host = HashMainFrameHostForStorage(raw_host); + history_hosts_count_[host]++; + } + } + + base::UmaHistogramCounts1000( + "BrowsingTopics.EpochTopicsCalculation.EligibleDistinctHistoryHostsCount", + history_hosts_count_.size()); + + std::vector<std::string> raw_hosts_vector(raw_hosts.begin(), raw_hosts.end()); + + annotations_service_->BatchAnnotatePageTopics( + base::BindOnce(&BrowsingTopicsCalculator::OnGetTopicsForHostsCompleted, + weak_ptr_factory_.GetWeakPtr(), raw_hosts_vector), + raw_hosts_vector); +} + +void BrowsingTopicsCalculator::OnGetTopicsForHostsCompleted( + std::vector<std::string> raw_hosts, + const std::vector<optimization_guide::BatchAnnotationResult>& results) { + absl::optional<optimization_guide::ModelInfo> model_info = + annotations_service_->GetModelInfoForType( + optimization_guide::AnnotationType::kPageTopics); + + if (!model_info) { + OnCalculateCompleted( + CalculatorResultStatus::kFailureAnnotationExecutionError); + return; + } + + absl::optional<size_t> taxonomy_size = GetTaxonomySize(); + if (!taxonomy_size) { + OnCalculateCompleted( + CalculatorResultStatus::kFailureTaxonomyVersionNotSupportedInBinary); + return; + } + + const int model_version = base::checked_cast<int>(model_info->GetVersion()); + DCHECK_GT(model_version, 0); + + std::map<HashedHost, std::set<Topic>> host_topics_map; + std::map<Topic, std::set<HashedHost>> topic_hosts_map; + DeriveHostTopicsMapAndTopicHostsMap(raw_hosts, results, host_topics_map, + topic_hosts_map); + + std::vector<Topic> top_topics; + size_t padded_top_topics_start_index = 0u; + DeriveTopTopics(history_hosts_count_, host_topics_map, *taxonomy_size, + top_topics, padded_top_topics_start_index); + + base::UmaHistogramCounts100( + "BrowsingTopics.EpochTopicsCalculation.TopTopicsCountBeforePadding", + padded_top_topics_start_index); + + // For each top topic, derive the context domains that observed it + std::vector<TopicAndDomains> top_topics_and_observing_domains; + + for (const Topic& topic : top_topics) { + if (!privacy_sandbox_settings_->IsTopicAllowed( + privacy_sandbox::CanonicalTopic( + topic, + blink::features::kBrowsingTopicsTaxonomyVersion.Get()))) { + top_topics_and_observing_domains.emplace_back(TopicAndDomains()); + continue; + } + + std::set<HashedDomain> topic_observation_domains = + GetTopicObservationDomains(topic, topic_hosts_map, + host_context_domains_map_); + + base::UmaHistogramCounts1000( + "BrowsingTopics.EpochTopicsCalculation." + "ObservationContextDomainsCountPerTopTopic", + topic_observation_domains.size()); + + top_topics_and_observing_domains.emplace_back( + TopicAndDomains(topic, std::move(topic_observation_domains))); + } + + OnCalculateCompleted( + CalculatorResultStatus::kSuccess, + EpochTopics(std::move(top_topics_and_observing_domains), + padded_top_topics_start_index, *taxonomy_size, + blink::features::kBrowsingTopicsTaxonomyVersion.Get(), + model_version, calculation_time_)); +} + +void BrowsingTopicsCalculator::OnCalculateCompleted( + CalculatorResultStatus status, + EpochTopics epoch_topics) { + DCHECK(status != CalculatorResultStatus::kSuccess || + epoch_topics.HasValidTopics()); + + base::UmaHistogramEnumeration( + "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus", status); + + std::move(calculate_completed_callback_).Run(std::move(epoch_topics)); + + // Do not add code after this. BrowsingTopicsCalculator has been destroyed. +} + +} // namespace browsing_topics
diff --git a/components/browsing_topics/browsing_topics_calculator.h b/components/browsing_topics/browsing_topics_calculator.h new file mode 100644 index 0000000..1232da37 --- /dev/null +++ b/components/browsing_topics/browsing_topics_calculator.h
@@ -0,0 +1,137 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_BROWSING_TOPICS_BROWSING_TOPICS_CALCULATOR_H_ +#define COMPONENTS_BROWSING_TOPICS_BROWSING_TOPICS_CALCULATOR_H_ + +#include <map> +#include <set> + +#include "base/callback.h" +#include "base/task/cancelable_task_tracker.h" +#include "components/browsing_topics/common/common_types.h" +#include "components/browsing_topics/epoch_topics.h" +#include "components/history/core/browser/history_types.h" + +namespace privacy_sandbox { +class PrivacySandboxSettings; +} // namespace privacy_sandbox + +namespace history { +class HistoryService; +} // namespace history + +namespace content { +class BrowsingTopicsSiteDataManager; +} // namespace content + +namespace optimization_guide { +class PageContentAnnotationsService; +class BatchAnnotationResult; +} // namespace optimization_guide + +namespace browsing_topics { + +// Responsible for doing a one-off browsing topics calculation. It will: +// 1) Check the user settings for calculation permissions. +// 2) Query the `BrowsingTopicsSiteDataManager` for the contexts where the +// Topics API was called on. +// 3) Query the `HistoryService` for the hosts of the pages the API was called +// on. +// 4) Query the `PageContentAnnotationsService` with a set of hosts, to get the +// corresponding topics. +// 5) Derive `EpochTopics` (i.e. the top topics and the their observed-by +// contexts), and return it as the final result. +class BrowsingTopicsCalculator { + public: + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class CalculatorResultStatus { + kSuccess = 0, + kFailurePermissionDenied = 1, + kFailureApiUsageContextQueryError = 2, + kFailureAnnotationExecutionError = 3, + kFailureTaxonomyVersionNotSupportedInBinary = 4, + + kMaxValue = kFailureTaxonomyVersionNotSupportedInBinary, + }; + + using CalculateCompletedCallback = base::OnceCallback<void(EpochTopics)>; + + BrowsingTopicsCalculator( + privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings, + history::HistoryService* history_service, + content::BrowsingTopicsSiteDataManager* site_data_manager, + optimization_guide::PageContentAnnotationsService* annotations_service, + CalculateCompletedCallback callback); + + BrowsingTopicsCalculator(const BrowsingTopicsCalculator&) = delete; + BrowsingTopicsCalculator& operator=(const BrowsingTopicsCalculator&) = delete; + BrowsingTopicsCalculator(BrowsingTopicsCalculator&&) = delete; + BrowsingTopicsCalculator& operator=(BrowsingTopicsCalculator&&) = delete; + + virtual ~BrowsingTopicsCalculator(); + + protected: + // This method exists for the purposes of overriding in tests. + virtual uint64_t GenerateRandUint64(); + + private: + // Get the top `kBrowsingTopicsNumberOfTopTopicsPerEpoch` topics. If there + // aren't enough topics, pad with random ones. Return the result topics, and + // the starting index of the padded topics (or + // `kBrowsingTopicsNumberOfTopTopicsPerEpoch` if there's no padded topics). + // Precondition: the hosts in `history_hosts_count` should exist in + // `host_topics_map`. + void DeriveTopTopics( + const std::map<HashedHost, size_t>& history_hosts_count, + const std::map<HashedHost, std::set<Topic>>& host_topics_map, + size_t taxonomy_size, + std::vector<Topic>& top_topics, + size_t& padded_top_topics_start_index); + + void CheckCanCalculate(); + + void OnGetRecentBrowsingTopicsApiUsagesCompleted( + browsing_topics::ApiUsageContextQueryResult result); + + void OnGetRecentlyVisitedURLsCompleted(history::QueryResults results); + + void OnGetTopicsForHostsCompleted( + std::vector<std::string> raw_hosts, + const std::vector<optimization_guide::BatchAnnotationResult>& results); + + void OnCalculateCompleted(CalculatorResultStatus status, + EpochTopics epoch_topics = EpochTopics()); + + // Those pointers are safe to hold and use throughout the lifetime of + // `BrowsingTopicsService`, which owns this object. + privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings_; + history::HistoryService* history_service_; + content::BrowsingTopicsSiteDataManager* site_data_manager_; + optimization_guide::PageContentAnnotationsService* annotations_service_; + + CalculateCompletedCallback calculate_completed_callback_; + + // The calculation start time. + base::Time calculation_time_; + + // The history hosts over + // `kBrowsingTopicsNumberOfEpochsOfObservationDataToUseForFiltering` epochs, + // and the calling context domains that used the Topics API in each main frame + // host. + std::map<HashedHost, std::vector<HashedDomain>> host_context_domains_map_; + + // The hashed history hosts and their count over the last epoch. + std::map<HashedHost, size_t> history_hosts_count_; + + // Used for the async tasks querying the HistoryService. + base::CancelableTaskTracker history_task_tracker_; + + base::WeakPtrFactory<BrowsingTopicsCalculator> weak_ptr_factory_{this}; +}; + +} // namespace browsing_topics + +#endif // COMPONENTS_BROWSING_TOPICS_BROWSING_TOPICS_CALCULATOR_H_
diff --git a/components/browsing_topics/browsing_topics_calculator_unittest.cc b/components/browsing_topics/browsing_topics_calculator_unittest.cc new file mode 100644 index 0000000..79fcfb0 --- /dev/null +++ b/components/browsing_topics/browsing_topics_calculator_unittest.cc
@@ -0,0 +1,789 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/browsing_topics/browsing_topics_calculator.h" + +#include "base/files/scoped_temp_dir.h" +#include "base/logging.h" +#include "base/test/bind.h" +#include "base/test/gtest_util.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" +#include "components/browsing_topics/test_util.h" +#include "components/browsing_topics/util.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/history/core/browser/history_database_params.h" +#include "components/history/core/browser/history_service.h" +#include "components/history/core/test/test_history_database.h" +#include "components/optimization_guide/content/browser/page_content_annotations_service.h" +#include "components/optimization_guide/content/browser/test_page_content_annotator.h" +#include "components/optimization_guide/core/test_model_info_builder.h" +#include "components/optimization_guide/core/test_optimization_guide_model_provider.h" +#include "components/privacy_sandbox/privacy_sandbox_prefs.h" +#include "components/privacy_sandbox/privacy_sandbox_settings.h" +#include "components/privacy_sandbox/privacy_sandbox_test_util.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/browsing_topics_test_util.h" +#include "third_party/blink/public/common/features.h" + +namespace browsing_topics { + +namespace { + +const size_t kTaxonomySize = 349; +const int kTaxonomyVersion = 1; + +const std::string kHost1 = "www.foo1.com"; +const std::string kHost2 = "www.foo2.com"; +const std::string kHost3 = "www.foo3.com"; +const std::string kHost4 = "www.foo4.com"; +const std::string kHost5 = "www.foo5.com"; +const std::string kHost6 = "www.foo6.com"; + +const std::string kTokenizedHost1 = "foo1 com"; +const std::string kTokenizedHost2 = "foo2 com"; +const std::string kTokenizedHost3 = "foo3 com"; +const std::string kTokenizedHost4 = "foo4 com"; +const std::string kTokenizedHost5 = "foo5 com"; +const std::string kTokenizedHost6 = "foo6 com"; + +} // namespace + +class BrowsingTopicsCalculatorTest : public testing::Test { + public: + BrowsingTopicsCalculatorTest() + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) { + EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); + + content_settings::CookieSettings::RegisterProfilePrefs(prefs_.registry()); + HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry()); + privacy_sandbox::RegisterProfilePrefs(prefs_.registry()); + + host_content_settings_map_ = new HostContentSettingsMap( + &prefs_, /*is_off_the_record=*/false, /*store_last_modified=*/false, + /*restore_session=*/false); + cookie_settings_ = new content_settings::CookieSettings( + host_content_settings_map_.get(), &prefs_, false, "chrome-extension"); + privacy_sandbox_settings_ = std::make_unique< + privacy_sandbox::PrivacySandboxSettings>( + std::make_unique< + privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate>(), + host_content_settings_map_.get(), cookie_settings_, &prefs_, + /*incognito_profile=*/false); + + topics_site_data_manager_ = + std::make_unique<content::TesterBrowsingTopicsSiteDataManager>( + temp_dir_.GetPath()); + + history_service_ = std::make_unique<history::HistoryService>(); + history_service_->Init( + history::TestHistoryDatabaseParamsForPath(temp_dir_.GetPath())); + + optimization_guide_model_provider_ = std::make_unique< + optimization_guide::TestOptimizationGuideModelProvider>(); + page_content_annotations_service_ = + std::make_unique<optimization_guide::PageContentAnnotationsService>( + "en-US", optimization_guide_model_provider_.get(), + history_service_.get(), nullptr, base::FilePath(), nullptr); + + page_content_annotations_service_->OverridePageContentAnnotatorForTesting( + &test_page_content_annotator_); + + task_environment_.RunUntilIdle(); + } + + ~BrowsingTopicsCalculatorTest() override { + host_content_settings_map_->ShutdownOnUIThread(); + } + + EpochTopics CalculateTopics() { + EpochTopics result; + + base::RunLoop run_loop; + + TesterBrowsingTopicsCalculator topics_calculator = + TesterBrowsingTopicsCalculator( + privacy_sandbox_settings_.get(), history_service_.get(), + topics_site_data_manager_.get(), + page_content_annotations_service_.get(), + base::BindLambdaForTesting([&](EpochTopics epoch_topics) { + result = std::move(epoch_topics); + run_loop.Quit(); + }), + /*rand_uint64_queue=*/ + base::queue<uint64_t>{{100, 101, 102, 103, 104}}); + + run_loop.Run(); + + return result; + } + + void AddHistoryEntries(const std::vector<std::string>& hosts, + base::Time time) { + history::HistoryAddPageArgs add_page_args; + add_page_args.time = time; + add_page_args.context_id = reinterpret_cast<history::ContextID>(1); + + for (const std::string& host : hosts) { + static int nav_entry_id = 0; + ++nav_entry_id; + + add_page_args.url = GURL(base::StrCat({"https://", host})); + add_page_args.nav_entry_id = nav_entry_id; + + history_service_->AddPage(add_page_args); + history_service_->SetBrowsingTopicsAllowed( + add_page_args.context_id, nav_entry_id, add_page_args.url); + } + + task_environment_.RunUntilIdle(); + } + + void AddApiUsageContextEntries( + std::vector<std::pair<std::string, std::set<HashedDomain>>> + main_frame_hosts_with_context_domains) { + for (auto& [main_frame_host, context_domains] : + main_frame_hosts_with_context_domains) { + topics_site_data_manager_->OnBrowsingTopicsApiUsed( + HashMainFrameHostForStorage(main_frame_host), + base::flat_set<HashedDomain>(context_domains.begin(), + context_domains.end())); + } + + task_environment_.RunUntilIdle(); + } + + std::vector<optimization_guide::WeightedIdentifier> TopicsAndWeight( + std::vector<int32_t> topics, + double weight) { + std::vector<optimization_guide::WeightedIdentifier> result; + for (int32_t topic : topics) { + result.emplace_back( + optimization_guide::WeightedIdentifier(topic, weight)); + } + + return result; + } + + void ExpectResultTopicsEqual( + const std::vector<TopicAndDomains>& result, + std::vector<std::pair<Topic, std::set<HashedDomain>>> expected) { + DCHECK_EQ(expected.size(), 5u); + EXPECT_EQ(result.size(), 5u); + + for (int i = 0; i < 5; ++i) { + EXPECT_EQ(result[i].topic(), expected[i].first); + EXPECT_EQ(result[i].hashed_domains(), expected[i].second); + } + } + + protected: + content::BrowserTaskEnvironment task_environment_; + + sync_preferences::TestingPrefServiceSyncable prefs_; + scoped_refptr<HostContentSettingsMap> host_content_settings_map_; + scoped_refptr<content_settings::CookieSettings> cookie_settings_; + std::unique_ptr<privacy_sandbox::PrivacySandboxSettings> + privacy_sandbox_settings_; + + std::unique_ptr<content::TesterBrowsingTopicsSiteDataManager> + topics_site_data_manager_; + + std::unique_ptr<history::HistoryService> history_service_; + + std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider> + optimization_guide_model_provider_; + std::unique_ptr<optimization_guide::PageContentAnnotationsService> + page_content_annotations_service_; + + optimization_guide::TestPageContentAnnotator test_page_content_annotator_; + + base::ScopedTempDir temp_dir_; +}; + +TEST_F(BrowsingTopicsCalculatorTest, PermissionDenied) { + base::HistogramTester histograms; + + privacy_sandbox_settings_->SetPrivacySandboxEnabled(false); + + EpochTopics result = CalculateTopics(); + EXPECT_FALSE(result.HasValidTopics()); + + histograms.ExpectUniqueSample( + "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus", + /*kFailurePermissionDenied*/ 1, + /*expected_bucket_count=*/1); +} + +TEST_F(BrowsingTopicsCalculatorTest, ApiUsageContextQueryError) { + base::HistogramTester histograms; + + topics_site_data_manager_->SetQueryFailureOverride(); + + EpochTopics result = CalculateTopics(); + EXPECT_FALSE(result.HasValidTopics()); + + histograms.ExpectUniqueSample( + "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus", + /*kFailureApiUsageContextQueryError*/ 2, + /*expected_bucket_count=*/1); +} + +TEST_F(BrowsingTopicsCalculatorTest, AnnotationExecutionError) { + base::HistogramTester histograms; + + EpochTopics result = CalculateTopics(); + EXPECT_FALSE(result.HasValidTopics()); + + histograms.ExpectUniqueSample( + "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus", + /*kFailureAnnotationExecutionError*/ 3, + /*expected_bucket_count=*/1); +} + +class BrowsingTopicsCalculatorUnsupporedTaxonomyVersionTest + : public BrowsingTopicsCalculatorTest { + public: + BrowsingTopicsCalculatorUnsupporedTaxonomyVersionTest() { + feature_list_.InitAndEnableFeatureWithParameters( + blink::features::kBrowsingTopics, {{"taxonomy_version", "999"}}); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +TEST_F(BrowsingTopicsCalculatorUnsupporedTaxonomyVersionTest, + TaxonomyVersionNotSupportedInBinary) { + base::HistogramTester histograms; + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), {}); + + EpochTopics result = CalculateTopics(); + EXPECT_FALSE(result.HasValidTopics()); + + histograms.ExpectUniqueSample( + "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus", + /*kFailureTaxonomyVersionNotSupportedInBinary*/ 4, + /*expected_bucket_count=*/1); +} + +TEST_F(BrowsingTopicsCalculatorTest, TopicsMetadata) { + base::HistogramTester histograms; + base::Time begin_time = base::Time::Now(); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), {}); + + EpochTopics result1 = CalculateTopics(); + EXPECT_TRUE(result1.HasValidTopics()); + EXPECT_EQ(result1.taxonomy_size(), kTaxonomySize); + EXPECT_EQ(result1.taxonomy_version(), kTaxonomyVersion); + EXPECT_EQ(result1.model_version(), 1); + EXPECT_EQ(result1.calculation_time(), begin_time); + + histograms.ExpectUniqueSample( + "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus", + /*kSuccess*/ 0, + /*expected_bucket_count=*/1); + + task_environment_.AdvanceClock(base::Seconds(2)); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(50).Build(), {}); + + EpochTopics result2 = CalculateTopics(); + EXPECT_TRUE(result2.HasValidTopics()); + EXPECT_EQ(result2.taxonomy_size(), kTaxonomySize); + EXPECT_EQ(result2.taxonomy_version(), kTaxonomyVersion); + EXPECT_EQ(result2.model_version(), 50); + EXPECT_EQ(result2.calculation_time(), begin_time + base::Seconds(2)); + + histograms.ExpectUniqueSample( + "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus", + /*kSuccess*/ 0, + /*expected_bucket_count=*/2); +} + +TEST_F(BrowsingTopicsCalculatorTest, TopTopicsRankedByFrequency) { + base::Time begin_time = base::Time::Now(); + + AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6}, + begin_time); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual(result.top_topics_and_observing_domains(), + {{Topic(6), {}}, + {Topic(5), {}}, + {Topic(4), {}}, + {Topic(3), {}}, + {Topic(2), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 5u); +} + +TEST_F(BrowsingTopicsCalculatorTest, + TopTopicsRankedByFrequency_AlsoAffectedByHostsCount) { + base::Time begin_time = base::Time::Now(); + + AddHistoryEntries({kHost1, kHost1, kHost1, kHost1, kHost1, kHost1, kHost2, + kHost3, kHost4, kHost5, kHost6}, + begin_time); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual(result.top_topics_and_observing_domains(), + {{Topic(2), {}}, + {Topic(1), {}}, + {Topic(6), {}}, + {Topic(5), {}}, + {Topic(4), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 5u); +} + +TEST_F(BrowsingTopicsCalculatorTest, + TopTopicsRankingNotAffectedByAnnotationWeight) { + base::Time begin_time = base::Time::Now(); + + AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6}, + begin_time); + + // Setting the weight for Topic(1) and Topic(2) to 0.9. This weight shouldn't + // affect the top topics ordering. + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2}, 0.9)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual(result.top_topics_and_observing_domains(), + {{Topic(6), {}}, + {Topic(5), {}}, + {Topic(4), {}}, + {Topic(3), {}}, + {Topic(2), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 5u); +} + +TEST_F(BrowsingTopicsCalculatorTest, AllTopTopicsRandomlyPadded) { + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual(result.top_topics_and_observing_domains(), + {{Topic(101), {}}, + {Topic(102), {}}, + {Topic(103), {}}, + {Topic(104), {}}, + {Topic(105), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 0u); +} + +TEST_F(BrowsingTopicsCalculatorTest, TopTopicsPartiallyPadded) { + base::HistogramTester histograms; + + base::Time begin_time = base::Time::Now(); + + AddHistoryEntries({kHost4, kHost5, kHost6}, begin_time); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual(result.top_topics_and_observing_domains(), + {{Topic(6), {}}, + {Topic(5), {}}, + {Topic(4), {}}, + {Topic(101), {}}, + {Topic(102), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 3u); +} + +TEST_F(BrowsingTopicsCalculatorTest, TopTopicsAndObservingDomains) { + base::Time begin_time = base::Time::Now(); + + AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6}, + begin_time); + + AddApiUsageContextEntries( + {{kHost1, {}}, + {kHost2, {}}, + {kHost3, {HashedDomain(2)}}, + {kHost4, {HashedDomain(3)}}, + {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}}); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual( + result.top_topics_and_observing_domains(), + {{Topic(6), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}, + {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}, + {Topic(4), {HashedDomain(2), HashedDomain(3)}}, + {Topic(3), {HashedDomain(2)}}, + {Topic(2), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 5u); +} + +TEST_F( + BrowsingTopicsCalculatorTest, + HistoryHostsBefore21DaysAgo_IgnoredForTopTopicsDecision_IgnoredForObservingDomainsDecision) { + base::Time begin_time = base::Time::Now(); + + AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6}, + begin_time - base::Days(21)); + + AddApiUsageContextEntries( + {{kHost1, {}}, + {kHost2, {}}, + {kHost3, {HashedDomain(2)}}, + {kHost4, {HashedDomain(3)}}, + {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}}); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 103, 4, 5, 6}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 103, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({103, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual(result.top_topics_and_observing_domains(), + {{Topic(101), {}}, + {Topic(102), {}}, + {Topic(103), {}}, + {Topic(104), {}}, + {Topic(105), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 0u); +} + +TEST_F( + BrowsingTopicsCalculatorTest, + HistoryHostsBetween7And21Days_IgnoredForTopTopicsDecision_ConsideredForObservingDomainsDecision) { + base::Time begin_time = base::Time::Now(); + + AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6}, + begin_time - base::Days(20)); + + AddApiUsageContextEntries( + {{kHost1, {}}, + {kHost2, {}}, + {kHost3, {HashedDomain(2)}}, + {kHost4, {HashedDomain(3)}}, + {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}}); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 103, 4, 5, 6}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 103, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({103, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual(result.top_topics_and_observing_domains(), + {{Topic(101), {}}, + {Topic(102), {}}, + {Topic(103), {HashedDomain(2)}}, + {Topic(104), {}}, + {Topic(105), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 0u); +} + +TEST_F(BrowsingTopicsCalculatorTest, + DataQueryBoundedByTopicsDataAccessibleSince) { + base::Time begin_time = base::Time::Now(); + + prefs_.SetTime(prefs::kPrivacySandboxTopicsDataAccessibleSince, + begin_time + base::Days(6)); + + AddHistoryEntries({kHost1, kHost2}, begin_time); + AddApiUsageContextEntries({{kHost1, {}}, {kHost2, {}}}); + + task_environment_.AdvanceClock(base::Days(6)); + + AddHistoryEntries({kHost3, kHost4, kHost5, kHost6}, + begin_time + base::Days(6)); + AddApiUsageContextEntries( + {{kHost3, {HashedDomain(2)}}, + {kHost4, {HashedDomain(3)}}, + {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}}); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual( + result.top_topics_and_observing_domains(), + {{Topic(6), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}, + {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}, + {Topic(4), {HashedDomain(2), HashedDomain(3)}}, + {Topic(3), {HashedDomain(2)}}, + {Topic(101), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 4u); +} + +TEST_F(BrowsingTopicsCalculatorTest, + TopTopicsAndObservingDomains_DomainsSizeExceedsLimit) { + base::Time begin_time = base::Time::Now(); + + std::set<HashedDomain> large_size_domains; + for (int i = 1; i <= 1001; ++i) { + large_size_domains.insert(HashedDomain(i)); + } + + AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6}, + begin_time); + + AddApiUsageContextEntries({{kHost1, {}}, + {kHost2, {}}, + {kHost3, {HashedDomain(2)}}, + {kHost4, {HashedDomain(3)}}, + {kHost5, large_size_domains}}); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + std::set<HashedDomain> expected_domains_after_capping = large_size_domains; + expected_domains_after_capping.erase(HashedDomain(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual(result.top_topics_and_observing_domains(), + {{Topic(6), expected_domains_after_capping}, + {Topic(5), expected_domains_after_capping}, + {Topic(4), {HashedDomain(2), HashedDomain(3)}}, + {Topic(3), {HashedDomain(2)}}, + {Topic(2), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 5u); +} + +TEST_F(BrowsingTopicsCalculatorTest, TopicBlocked) { + base::Time begin_time = base::Time::Now(); + + AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6}, + begin_time); + + AddApiUsageContextEntries( + {{kHost1, {}}, + {kHost2, {}}, + {kHost3, {HashedDomain(2)}}, + {kHost4, {HashedDomain(3)}}, + {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}}); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + privacy_sandbox_settings_->SetTopicAllowed( + privacy_sandbox::CanonicalTopic(Topic(6), kTaxonomyVersion), + /*allowed=*/false); + privacy_sandbox_settings_->SetTopicAllowed( + privacy_sandbox::CanonicalTopic(Topic(4), kTaxonomyVersion), + /*allowed=*/false); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual( + result.top_topics_and_observing_domains(), + {{Topic(0), {}}, + {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}, + {Topic(0), {}}, + {Topic(3), {HashedDomain(2)}}, + {Topic(2), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 5u); +} + +TEST_F(BrowsingTopicsCalculatorTest, PaddedTopicsDoNotDuplicate) { + base::Time begin_time = base::Time::Now(); + + AddHistoryEntries({kHost4, kHost5, kHost6}, begin_time); + + AddApiUsageContextEntries( + {{kHost1, {}}, + {kHost2, {}}, + {kHost3, {HashedDomain(2)}}, + {kHost4, {HashedDomain(3)}}, + {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}}); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 102}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 102}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 102}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 102}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 102}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({102}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual( + result.top_topics_and_observing_domains(), + {{Topic(102), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}, + {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}, + {Topic(4), {HashedDomain(3)}}, + {Topic(101), {}}, + {Topic(103), {}}}); +} + +TEST_F(BrowsingTopicsCalculatorTest, Metrics) { + base::HistogramTester histograms; + + base::Time begin_time = base::Time::Now(); + + AddHistoryEntries({kHost4, kHost5, kHost6}, begin_time); + + AddApiUsageContextEntries( + {{kHost1, {}}, + {kHost2, {}}, + {kHost3, {HashedDomain(2)}}, + {kHost4, {HashedDomain(3)}}, + {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}}); + + test_page_content_annotator_.UsePageTopics( + *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), + {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)}, + {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)}, + {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)}, + {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)}, + {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}}); + + task_environment_.AdvanceClock(base::Seconds(1)); + + EpochTopics result = CalculateTopics(); + ExpectResultTopicsEqual( + result.top_topics_and_observing_domains(), + {{Topic(6), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}, + {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}, + {Topic(4), {HashedDomain(3)}}, + {Topic(101), {}}, + {Topic(102), {}}}); + + EXPECT_EQ(result.padded_top_topics_start_index(), 3u); + + histograms.ExpectUniqueSample( + "BrowsingTopics.EpochTopicsCalculation.EligibleDistinctHistoryHostsCount", + /*sample=*/3, + /*expected_bucket_count=*/1); + + histograms.ExpectUniqueSample( + "BrowsingTopics.EpochTopicsCalculation.TopTopicsCountBeforePadding", + /*sample=*/3, + /*expected_bucket_count=*/1); + + histograms.ExpectTotalCount( + "BrowsingTopics.EpochTopicsCalculation." + "ObservationContextDomainsCountPerTopTopic", + /*count=*/5); + histograms.ExpectBucketCount( + "BrowsingTopics.EpochTopicsCalculation." + "ObservationContextDomainsCountPerTopTopic", + /*sample=*/0, + /*expected_count=*/2); + histograms.ExpectBucketCount( + "BrowsingTopics.EpochTopicsCalculation." + "ObservationContextDomainsCountPerTopTopic", + /*sample=*/1, + /*expected_count=*/1); + histograms.ExpectBucketCount( + "BrowsingTopics.EpochTopicsCalculation." + "ObservationContextDomainsCountPerTopTopic", + /*sample=*/3, + /*expected_count=*/2); +} + +} // namespace browsing_topics
diff --git a/components/browsing_topics/common/common_types.h b/components/browsing_topics/common/common_types.h index ed47152..4a59150 100644 --- a/components/browsing_topics/common/common_types.h +++ b/components/browsing_topics/common/common_types.h
@@ -19,7 +19,7 @@ struct COMPONENT_EXPORT(BROWSING_TOPICS_COMMON) ApiUsageContext { HashedDomain hashed_context_domain; - HashedHost hashed_top_host; + HashedHost hashed_main_frame_host; base::Time time; };
diff --git a/components/browsing_topics/epoch_topics.h b/components/browsing_topics/epoch_topics.h index f16e0307..d38a3c2 100644 --- a/components/browsing_topics/epoch_topics.h +++ b/components/browsing_topics/epoch_topics.h
@@ -57,6 +57,16 @@ // reset `padded_top_topics_start_index_` to 0. void ClearTopics(); + const std::vector<TopicAndDomains>& top_topics_and_observing_domains() const { + return top_topics_and_observing_domains_; + } + + size_t padded_top_topics_start_index() const { + return padded_top_topics_start_index_; + } + + size_t taxonomy_size() const { return taxonomy_size_; } + int taxonomy_version() const { return taxonomy_version_; } int model_version() const { return model_version_; }
diff --git a/components/browsing_topics/test_util.cc b/components/browsing_topics/test_util.cc new file mode 100644 index 0000000..e163029 --- /dev/null +++ b/components/browsing_topics/test_util.cc
@@ -0,0 +1,34 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/browsing_topics/test_util.h" + +namespace browsing_topics { + +TesterBrowsingTopicsCalculator::TesterBrowsingTopicsCalculator( + privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings, + history::HistoryService* history_service, + content::BrowsingTopicsSiteDataManager* site_data_manager, + optimization_guide::PageContentAnnotationsService* annotations_service, + CalculateCompletedCallback callback, + base::queue<uint64_t> rand_uint64_queue) + : BrowsingTopicsCalculator(privacy_sandbox_settings, + history_service, + site_data_manager, + annotations_service, + std::move(callback)), + rand_uint64_queue_(std::move(rand_uint64_queue)) {} + +TesterBrowsingTopicsCalculator::~TesterBrowsingTopicsCalculator() = default; + +uint64_t TesterBrowsingTopicsCalculator::GenerateRandUint64() { + DCHECK(!rand_uint64_queue_.empty()); + + uint64_t next_rand_uint64 = rand_uint64_queue_.front(); + rand_uint64_queue_.pop(); + + return next_rand_uint64; +} + +} // namespace browsing_topics
diff --git a/components/browsing_topics/test_util.h b/components/browsing_topics/test_util.h new file mode 100644 index 0000000..cc9c2351 --- /dev/null +++ b/components/browsing_topics/test_util.h
@@ -0,0 +1,47 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_BROWSING_TOPICS_TEST_UTIL_H_ +#define COMPONENTS_BROWSING_TOPICS_TEST_UTIL_H_ + +#include "base/containers/queue.h" +#include "components/browsing_topics/browsing_topics_calculator.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace browsing_topics { + +// A tester class that allows mocking the generated random numbers. +class TesterBrowsingTopicsCalculator : public BrowsingTopicsCalculator { + public: + // Initialize a regular `BrowsingTopicsCalculator` with an additional + // `rand_uint64_queue` member for generating random numbers. + TesterBrowsingTopicsCalculator( + privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings, + history::HistoryService* history_service, + content::BrowsingTopicsSiteDataManager* site_data_manager, + optimization_guide::PageContentAnnotationsService* annotations_service, + CalculateCompletedCallback callback, + base::queue<uint64_t> rand_uint64_queue); + + ~TesterBrowsingTopicsCalculator() override; + + TesterBrowsingTopicsCalculator(const TesterBrowsingTopicsCalculator&) = + delete; + TesterBrowsingTopicsCalculator& operator=( + const TesterBrowsingTopicsCalculator&) = delete; + TesterBrowsingTopicsCalculator(TesterBrowsingTopicsCalculator&&) = delete; + TesterBrowsingTopicsCalculator& operator=(TesterBrowsingTopicsCalculator&&) = + delete; + + // Pop and return the next number in `rand_uint64_queue_`. Precondition: + // `rand_uint64_queue_` is not empty. + uint64_t GenerateRandUint64() override; + + private: + base::queue<uint64_t> rand_uint64_queue_; +}; + +} // namespace browsing_topics + +#endif // COMPONENTS_BROWSING_TOPICS_TEST_UTIL_H_
diff --git a/components/browsing_topics/topic_and_domains.h b/components/browsing_topics/topic_and_domains.h index 1663378..dd335ef 100644 --- a/components/browsing_topics/topic_and_domains.h +++ b/components/browsing_topics/topic_and_domains.h
@@ -33,6 +33,8 @@ static TopicAndDomains FromDictValue(const base::Value::Dict& dict_value); base::Value::Dict ToDictValue() const; + bool IsValid() const { return topic_ != Topic(0); } + const Topic& topic() const { return topic_; } const std::set<HashedDomain>& hashed_domains() const {
diff --git a/components/browsing_topics/topic_and_domains_unittest.cc b/components/browsing_topics/topic_and_domains_unittest.cc index c89bc19..8424d97a 100644 --- a/components/browsing_topics/topic_and_domains_unittest.cc +++ b/components/browsing_topics/topic_and_domains_unittest.cc
@@ -14,6 +14,7 @@ TopicAndDomains read_topic_and_domains = TopicAndDomains::FromDictValue(base::Value::Dict()); + EXPECT_FALSE(read_topic_and_domains.IsValid()); EXPECT_EQ(read_topic_and_domains.topic(), Topic(0)); EXPECT_TRUE(read_topic_and_domains.hashed_domains().empty()); } @@ -25,6 +26,7 @@ TopicAndDomains read_topic_and_domains = TopicAndDomains::FromDictValue(dict_value); + EXPECT_FALSE(read_topic_and_domains.IsValid()); EXPECT_EQ(read_topic_and_domains.topic(), Topic(0)); EXPECT_TRUE(read_topic_and_domains.hashed_domains().empty()); } @@ -38,6 +40,7 @@ TopicAndDomains read_topic_and_domains = TopicAndDomains::FromDictValue(dict_value); + EXPECT_TRUE(read_topic_and_domains.IsValid()); EXPECT_EQ(read_topic_and_domains.topic(), Topic(2)); EXPECT_EQ(read_topic_and_domains.hashed_domains(), std::set({HashedDomain(123), HashedDomain(456)}));
diff --git a/components/browsing_topics/util.cc b/components/browsing_topics/util.cc index 7de6513b..998fb09 100644 --- a/components/browsing_topics/util.cc +++ b/components/browsing_topics/util.cc
@@ -10,6 +10,7 @@ #include "base/rand_util.h" #include "crypto/hmac.h" #include "crypto/sha2.h" +#include "third_party/blink/public/common/features.h" namespace browsing_topics { @@ -28,7 +29,7 @@ const char kEpochSwitchTimeDecisionPrefix[] = "TopicsV1_EpochSwitchTimeDecision|"; const char kContextDomainStoragePrefix[] = "TopicsV1_ContextDomainStorage|"; -const char kTopHostStoragePrefix[] = "TopicsV1_TopHostStorage|"; +const char kMainFrameHostStoragePrefix[] = "TopicsV1_MainFrameHostStorage|"; uint64_t HmacHash(ReadOnlyHmacKey hmac_key, const std::string& use_case_prefix, @@ -48,6 +49,16 @@ } // namespace +absl::optional<size_t> GetTaxonomySize() { + if (blink::features::kBrowsingTopicsTaxonomyVersion.Get() == 1) { + // Taxonomy version 1 has 349 topics. + // https://github.com/jkarlin/topics/blob/main/taxonomy_v1.md + return 349; + } + + return absl::nullopt; +} + HmacKey GenerateRandomHmacKey() { if (g_hmac_key_override_for_testing.IsCreated()) return g_hmac_key_override_for_testing.Get(); @@ -112,13 +123,32 @@ HmacHash(hmac_key, kContextDomainStoragePrefix, context_domain)); } -HashedHost HashTopHostForStorage(const std::string& top_host) { +HashedHost HashMainFrameHostForStorage(const std::string& main_frame_host) { int64_t result; - crypto::SHA256HashString(kTopHostStoragePrefix + top_host, &result, - sizeof(result)); + crypto::SHA256HashString(kMainFrameHostStoragePrefix + main_frame_host, + &result, sizeof(result)); return HashedHost(result); } +base::Time DeriveHistoryDataStartTime(base::Time calculation_time, + base::Time data_accessible_since) { + return std::max(data_accessible_since, + calculation_time - + blink::features::kBrowsingTopicsTimePeriodPerEpoch.Get()); +} + +base::Time DeriveApiUsageContextDataStartTime( + base::Time calculation_time, + base::Time data_accessible_since) { + return std::max( + data_accessible_since, + calculation_time - + blink::features:: + kBrowsingTopicsNumberOfEpochsOfObservationDataToUseForFiltering + .Get() * + blink::features::kBrowsingTopicsTimePeriodPerEpoch.Get()); +} + void OverrideHmacKeyForTesting(ReadOnlyHmacKey hmac_key) { std::copy(hmac_key.begin(), hmac_key.end(), g_hmac_key_override_for_testing.Get().begin());
diff --git a/components/browsing_topics/util.h b/components/browsing_topics/util.h index 943ddd5..b8f7e84 100644 --- a/components/browsing_topics/util.h +++ b/components/browsing_topics/util.h
@@ -8,12 +8,22 @@ #include "base/containers/span.h" #include "base/time/time.h" #include "components/browsing_topics/common/common_types.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace browsing_topics { using HmacKey = std::array<uint8_t, 32>; using ReadOnlyHmacKey = base::span<const uint8_t, 32>; +// Get the size of the taxonomy. This is used for generating random topics from +// [1, `GetTaxonomySize()`]. It returns nullopt if this Chrome binary does not +// support the finch configured taxonomy version +// `kBrowsingTopicsTaxonomyVersion`. +// +// TODO(yaoxia): this should be maintained by UX along with the string mappings. +// Consider moving to a UX component. +absl::optional<size_t> GetTaxonomySize(); + // Generate a 256 bit random hmac key. HmacKey GenerateRandomHmacKey(); @@ -53,9 +63,21 @@ HashedDomain HashContextDomainForStorage(ReadOnlyHmacKey hmac_key, const std::string& context_domain); -// Returns a hash of `top_host` to be stored more efficiently in disk and +// Returns a hash of `main_frame_host` to be stored more efficiently in disk and // memory. -HashedHost HashTopHostForStorage(const std::string& top_host); +HashedHost HashMainFrameHostForStorage(const std::string& main_frame_host); + +// Returns the maximum of |`calculation_time` - history data time range|, and +// |data_accessible_since|. +base::Time DeriveHistoryDataStartTime( + base::Time calculation_time, + base::Time data_accessible_since = base::Time()); + +// Returns the maximum of |`calculation_time` - api usage data time range|, +// and |data_accessible_since|. +base::Time DeriveApiUsageContextDataStartTime( + base::Time calculation_time, + base::Time data_accessible_since = base::Time()); // Override the key to be returned for subsequent invocations of // `GenerateRandomHmacKey()`.
diff --git a/components/browsing_topics/util_unittest.cc b/components/browsing_topics/util_unittest.cc index ff8e2580..569ab81 100644 --- a/components/browsing_topics/util_unittest.cc +++ b/components/browsing_topics/util_unittest.cc
@@ -247,11 +247,12 @@ })); } -TEST_F(BrowsingTopicsUtilTest, HashTopHostForStorage) { +TEST_F(BrowsingTopicsUtilTest, HashMainFrameHostForStorage) { CheckUniformRandom(base::BindLambdaForTesting([&]() { - std::string top_host = GenerateRandomDomainOrHost(); + std::string main_frame_host = GenerateRandomDomainOrHost(); - return static_cast<uint64_t>(HashTopHostForStorage(top_host).value()); + return static_cast<uint64_t>( + HashMainFrameHostForStorage(main_frame_host).value()); })); }
diff --git a/components/commerce/core/BUILD.gn b/components/commerce/core/BUILD.gn index 4d499db..aeee6873 100644 --- a/components/commerce/core/BUILD.gn +++ b/components/commerce/core/BUILD.gn
@@ -19,9 +19,30 @@ deps = [ "//base", "//components/flags_ui", + "//components/search", "//third_party/re2:re2", "//url:url", ] + + if (!is_android) { + deps += [ ":commerce_heuristics_data" ] + } +} + +source_set("feature_list_unittests") { + testonly = true + sources = [ "commerce_feature_list_unittest.cc" ] + deps = [ + ":feature_list", + "//base", + "//base/test:test_support", + "//testing/gtest", + "//third_party/re2:re2", + ] + + if (!is_android) { + deps += [ ":commerce_heuristics_data" ] + } } static_library("metrics") {
diff --git a/components/commerce/core/DEPS b/components/commerce/core/DEPS index d2e100a..d4d4a896 100644 --- a/components/commerce/core/DEPS +++ b/components/commerce/core/DEPS
@@ -3,4 +3,5 @@ "+components/optimization_guide", "+components/prefs", "+third_party/re2", + "+components/search", ]
diff --git a/components/commerce/core/commerce_feature_list.cc b/components/commerce/core/commerce_feature_list.cc index 7b0f698..77e84be 100644 --- a/components/commerce/core/commerce_feature_list.cc +++ b/components/commerce/core/commerce_feature_list.cc
@@ -7,22 +7,58 @@ #include "base/feature_list.h" #include "base/metrics/field_trial_params.h" #include "base/no_destructor.h" +#include "build/buildflag.h" +#if !BUILDFLAG(IS_ANDROID) +#include "components/commerce/core/commerce_heuristics_data.h" +#endif // !BUILDFLAG(IS_ANDROID) #include "third_party/re2/src/re2/re2.h" namespace commerce { namespace { -constexpr base::FeatureParam<std::string> kPartnerMerchantPattern{ +constexpr base::FeatureParam<std::string> kRulePartnerMerchantPattern{ + &ntp_features::kNtpChromeCartModule, "partner-merchant-pattern", + // This regex does not match anything. + "\\b\\B"}; + +constexpr base::FeatureParam<std::string> kCouponPartnerMerchantPattern{ &commerce::kRetailCoupons, "coupon-partner-merchant-pattern", // This regex does not match anything. "\\b\\B"}; -const re2::RE2& GetPartnerMerchantPattern() { +const re2::RE2& GetRulePartnerMerchantPattern() { +#if !BUILDFLAG(IS_ANDROID) + auto* pattern_from_component = + commerce_heuristics::CommerceHeuristicsData::GetInstance() + .GetRuleDiscountPartnerMerchantPattern(); + if (pattern_from_component && kRulePartnerMerchantPattern.Get() == + kRulePartnerMerchantPattern.default_value) { + return *pattern_from_component; + } +#endif // !BUILDFLAG(IS_ANDROID) re2::RE2::Options options; options.set_case_sensitive(false); - static base::NoDestructor<re2::RE2> instance(kPartnerMerchantPattern.Get(), - options); + static base::NoDestructor<re2::RE2> instance( + kRulePartnerMerchantPattern.Get(), options); + return *instance; +} + +const re2::RE2& GetCouponPartnerMerchantPattern() { +#if !BUILDFLAG(IS_ANDROID) + auto* pattern_from_component = + commerce_heuristics::CommerceHeuristicsData::GetInstance() + .GetCouponDiscountPartnerMerchantPattern(); + if (pattern_from_component && + kCouponPartnerMerchantPattern.Get() == + kCouponPartnerMerchantPattern.default_value) { + return *pattern_from_component; + } +#endif // !BUILDFLAG(IS_ANDROID) + re2::RE2::Options options; + options.set_case_sensitive(false); + static base::NoDestructor<re2::RE2> instance( + kCouponPartnerMerchantPattern.Get(), options); return *instance; } @@ -55,11 +91,121 @@ const base::Feature kDiscountConsentV2{"DiscountConsentV2", base::FEATURE_DISABLED_BY_DEFAULT}; +// Params for Discount Consent V2 in the NTP Cart module. +const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[] = + "discount-consent-ntp-variation"; +const base::FeatureParam<int> kNtpChromeCartModuleDiscountConsentNtpVariation{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentNtpVariationParam, 0}; +const char kNtpChromeCartModuleDiscountConsentReshowTimeParam[] = + "discount-consent-ntp-reshow-time"; +const base::FeatureParam<base::TimeDelta> + kNtpChromeCartModuleDiscountConsentReshowTime{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentReshowTimeParam, base::Days(28)}; +const char kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam[] = + "discount-consent-ntp-max-dismiss-count"; +const base::FeatureParam<int> + kNtpChromeCartModuleDiscountConsentMaxDismissalCount{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam, 1}; + +// String change variation params. +const char kNtpChromeCartModuleDiscountConsentStringChangeContentParam[] = + "string-change-content"; +const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentStringChangeContent{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentStringChangeContentParam, ""}; + +const char kNtpChromeCartModuleDiscountConsentInlineShowCloseButtonParam[] = + "inline-card-show-button"; +const base::FeatureParam<bool> + kNtpChromeCartModuleDiscountConsentInlineShowCloseButton{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentStringChangeContentParam, true}; + +// Discount consent v2 step 1 params. +const char + kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam[] = + "step-one-use-static-content"; +const base::FeatureParam<bool> + kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam, + false}; +const char kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[] = + "step-one-static-content"; +const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam, ""}; +const char kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam[] = + "step-one-one-cart-content"; +const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam, ""}; +const char kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam[] = + "step-one-two-carts-content"; +const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam, ""}; +const char + kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam[] = + "step-one-three-carts-content"; +const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam, + ""}; + +// Discount consent v2 step 2 params. +const char kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam[] = + "step-two-content"; +const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpStepTwoContent{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam, ""}; +const char + kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam[] = + "step-two-different-color"; +const base::FeatureParam<bool> + kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam, + false}; +const char kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam[] = + "dialog-content-title"; +const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle{ + &commerce::kDiscountConsentV2, + kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam, ""}; + +bool IsPartnerMerchant(const GURL& url) { + return commerce::IsCouponDiscountPartnerMerchant(url) || + IsRuleDiscountPartnerMerchant(url); +} + +bool IsRuleDiscountPartnerMerchant(const GURL& url) { + const std::string& url_string = url.spec(); + return RE2::PartialMatch( + re2::StringPiece(url_string.data(), url_string.size()), + GetRulePartnerMerchantPattern()); +} + bool IsCouponDiscountPartnerMerchant(const GURL& url) { const std::string& url_string = url.spec(); return RE2::PartialMatch( re2::StringPiece(url_string.data(), url_string.size()), - GetPartnerMerchantPattern()); + GetCouponPartnerMerchantPattern()); +} + +bool IsCartDiscountFeatureEnabled() { + return base::GetFieldTrialParamByFeatureAsBool( + ntp_features::kNtpChromeCartModule, + ntp_features::kNtpChromeCartModuleAbandonedCartDiscountParam, false); } bool IsCouponWithCodeEnabled() { @@ -67,4 +213,10 @@ kRetailCoupons, kRetailCouponsWithCodeParam, true); } +bool IsFakeDataEnabled() { + return base::GetFieldTrialParamValueByFeature( + ntp_features::kNtpChromeCartModule, + ntp_features::kNtpChromeCartModuleDataParam) == "fake"; +} + } // namespace commerce
diff --git a/components/commerce/core/commerce_feature_list.h b/components/commerce/core/commerce_feature_list.h index 40042c268..4edbe38 100644 --- a/components/commerce/core/commerce_feature_list.h +++ b/components/commerce/core/commerce_feature_list.h
@@ -8,6 +8,7 @@ #include "base/feature_list.h" #include "base/metrics/field_trial_params.h" #include "components/flags_ui/feature_entry.h" +#include "components/search/ntp_features.h" #include "url/gurl.h" namespace commerce { @@ -75,14 +76,122 @@ // Feature flag for Discount user consent v2. extern const base::Feature kDiscountConsentV2; +// Feature parameters for ChromeCart on Desktop. + +// Whether to use OptimizationGuide to optimize renderer signal collection. +constexpr base::FeatureParam<bool> kOptimizeRendererSignal( + &ntp_features::kNtpChromeCartModule, + "optimize-renderer-signal", + false); + +constexpr base::FeatureParam<base::TimeDelta> kDiscountFetchDelayParam( + &ntp_features::kNtpChromeCartModule, + "discount-fetch-delay", + base::Hours(6)); + // Interval that controls the frequency of showing coupons in infobar bubbles. constexpr base::FeatureParam<base::TimeDelta> kCouponDisplayInterval{ &commerce::kRetailCoupons, "coupon_display_interval", base::Hours(18)}; +// The following are Feature params for Discount user consent v2. +// This indicates the Discount Consent v2 variation on the NTP Cart module. +enum class DiscountConsentNtpVariation { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + kDefault = 0, + kStringChange = 1, + kInline = 2, + kDialog = 3, + kMaxValue = kDialog +}; + +// Param indicates the ConsentV2 variation. See DiscountConsentNtpVariation +// enum. +extern const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[]; +extern const base::FeatureParam<int> + kNtpChromeCartModuleDiscountConsentNtpVariation; +// The time interval, after the last dismissal, before reshowing the consent. +extern const char kNtpChromeCartModuleDiscountConsentReshowTimeParam[]; +extern const base::FeatureParam<base::TimeDelta> + kNtpChromeCartModuleDiscountConsentReshowTime; +// The max number of dismisses allowed. +extern const char kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam[]; +extern const base::FeatureParam<int> + kNtpChromeCartModuleDiscountConsentMaxDismissalCount; + +// String change variation params. This string is replacing the content string +// of the v1 consent. +extern const char kNtpChromeCartModuleDiscountConsentStringChangeContentParam[]; +extern const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentStringChangeContent; + +// DiscountConsentNtpVariation::kInline and DiscountConsentNtpVariation::kDialog +// params. This indicate whether the 'x' button should show. +extern const char + kNtpChromeCartModuleDiscountConsentInlineShowCloseButtonParam[]; +extern const base::FeatureParam<bool> + kNtpChromeCartModuleDiscountConsentInlineShowCloseButton; + +// The following are discount consent step 1 params. +// This indicates whether the content in step 1 is a static string that does not +// contain any merchant names. +extern const char + kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam[]; +extern const base::FeatureParam<bool> + kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent; +// This the content string use in step 1 if +// kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent.Get() is true. +extern const char + kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[]; +extern const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent; +// This is a string template that takes in one merchant name, and it's used when +// there is only 1 Chrome Cart. +extern const char + kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam[]; +extern const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart; +// This is a string template that takes in two merchant names, and it's used +// when there are only 2 Chrome Carts. +extern const char + kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam[]; +extern const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts; +// This is a string template that takes in two merchant names, and it's used +// when there are 3 or more Chrome Carts. +extern const char + kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam[]; +extern const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts; + +// The following are discount consent step 2 params. +// This is the content string used in step 2. This is the actual consent string. +extern const char kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam[]; +extern const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpStepTwoContent; +// This is used to indicate whether the backgound-color of step 2 should change. +extern const char + kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam[]; +extern const base::FeatureParam<bool> + kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor; +// This is the content title use in the dialog consent. +extern const char + kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam[]; +extern const base::FeatureParam<std::string> + kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle; + +// Check if a URL belongs to a partner merchant of any type of discount. +bool IsPartnerMerchant(const GURL& url); +// Check if a URL belongs to a partner merchant of rule discount. +bool IsRuleDiscountPartnerMerchant(const GURL& url); // Check if a URL belongs to a partner merchant of coupon discount. bool IsCouponDiscountPartnerMerchant(const GURL& url); +// Check if cart discount feature is enabled. +bool IsCartDiscountFeatureEnabled(); // Check if the feature variation of coupons with code is enabled. bool IsCouponWithCodeEnabled(); +// Check if the variation with fake data is enabled. +bool IsFakeDataEnabled(); } // namespace commerce #endif // COMPONENTS_COMMERCE_CORE_COMMERCE_FEATURE_LIST_H_
diff --git a/components/commerce/core/commerce_feature_list_unittest.cc b/components/commerce/core/commerce_feature_list_unittest.cc new file mode 100644 index 0000000..d3225a1b --- /dev/null +++ b/components/commerce/core/commerce_feature_list_unittest.cc
@@ -0,0 +1,89 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/commerce/core/commerce_feature_list.h" +#include "base/test/scoped_feature_list.h" +#include "build/buildflag.h" +#include "components/commerce/core/commerce_heuristics_data.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +#if !BUILDFLAG(IS_ANDROID) +std::map<std::string, std::string> kRulePartnerMerchantParams = { + {"partner-merchant-pattern", "foo"}}; +std::map<std::string, std::string> kCouponPartnerMerchantParams = { + {"coupon-partner-merchant-pattern", "bar"}}; +const char kGlobalHeuristicsJSONData[] = R"###( + { + "rule_discount_partner_merchant_regex": "baz", + "coupon_discount_partner_merchant_regex": "qux" + } + )###"; +const char kRuleFeatureParamPartnerMerchantURL[] = "https://www.foo.com"; +const char kCouponFeatureParamPartnerMerchantURL[] = "https://www.bar.com"; +const char kRuleComponentPartnerMerchantURL[] = "https://www.baz.com"; +const char kCouponComponentPartnerMerchantURL[] = "https://www.qux.com"; +#endif //! BUILDFLAG(IS_ANDROID) +} // namespace + +class CommerceFeatureListTest : public testing::Test { + public: + void TearDown() override { features_.Reset(); } + + protected: + base::test::ScopedFeatureList features_; +}; + +#if !BUILDFLAG(IS_ANDROID) +TEST_F(CommerceFeatureListTest, TestRulePartnerMerchant_FromFeatureParam) { + features_.InitWithFeaturesAndParameters( + {{ntp_features::kNtpChromeCartModule, kRulePartnerMerchantParams}, + {commerce::kRetailCoupons, kCouponPartnerMerchantParams}}, + {}); + + ASSERT_TRUE(commerce::IsRuleDiscountPartnerMerchant( + GURL(kRuleFeatureParamPartnerMerchantURL))); + ASSERT_FALSE(commerce::IsRuleDiscountPartnerMerchant( + GURL(kCouponFeatureParamPartnerMerchantURL))); + ASSERT_TRUE(commerce::IsCouponDiscountPartnerMerchant( + GURL(kCouponFeatureParamPartnerMerchantURL))); + ASSERT_FALSE(commerce::IsCouponDiscountPartnerMerchant( + GURL(kRuleFeatureParamPartnerMerchantURL))); +} + +TEST_F(CommerceFeatureListTest, TestRulePartnerMerchant_FromComponent) { + auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance(); + ASSERT_TRUE( + data.PopulateDataFromComponent("{}", kGlobalHeuristicsJSONData, "", "")); + + ASSERT_TRUE(commerce::IsRuleDiscountPartnerMerchant( + GURL(kRuleComponentPartnerMerchantURL))); + ASSERT_FALSE(commerce::IsRuleDiscountPartnerMerchant( + GURL(kCouponComponentPartnerMerchantURL))); + ASSERT_TRUE(commerce::IsCouponDiscountPartnerMerchant( + GURL(kCouponComponentPartnerMerchantURL))); + ASSERT_FALSE(commerce::IsCouponDiscountPartnerMerchant( + GURL(kRuleComponentPartnerMerchantURL))); +} + +TEST_F(CommerceFeatureListTest, TestCouponPartnerMerchant_Priority) { + features_.InitWithFeaturesAndParameters( + {{ntp_features::kNtpChromeCartModule, kRulePartnerMerchantParams}, + {commerce::kRetailCoupons, kCouponPartnerMerchantParams}}, + {}); + + auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance(); + ASSERT_TRUE( + data.PopulateDataFromComponent("{}", kGlobalHeuristicsJSONData, "", "")); + + ASSERT_TRUE(commerce::IsRuleDiscountPartnerMerchant( + GURL(kRuleFeatureParamPartnerMerchantURL))); + ASSERT_FALSE(commerce::IsRuleDiscountPartnerMerchant( + GURL(kRuleComponentPartnerMerchantURL))); + ASSERT_TRUE(commerce::IsCouponDiscountPartnerMerchant( + GURL(kCouponFeatureParamPartnerMerchantURL))); + ASSERT_FALSE(commerce::IsCouponDiscountPartnerMerchant( + GURL(kCouponComponentPartnerMerchantURL))); +} +#endif //! BUILDFLAG(IS_ANDROID)
diff --git a/components/commerce/core/commerce_heuristics_data.cc b/components/commerce/core/commerce_heuristics_data.cc index 9942770..15aba3d 100644 --- a/components/commerce/core/commerce_heuristics_data.cc +++ b/components/commerce/core/commerce_heuristics_data.cc
@@ -16,6 +16,15 @@ // CommerceGlobalHeuristics types. constexpr char kSkipProductPatternType[] = "sensitive_product_regex"; +constexpr char kRuleDiscountPartnerMerchantPatternType[] = + "rule_discount_partner_merchant_regex"; +constexpr char kCouponDiscountPartnerMerchantPatternType[] = + "coupon_discount_partner_merchant_regex"; +constexpr char kCartPagetURLPatternType[] = "cart_page_url_regex"; +constexpr char kCheckoutPageURLPatternType[] = "checkout_page_url_regex"; +constexpr char kPurchaseButtonTextPatternType[] = "purchase_button_text_regex"; +constexpr char kAddToCartRequestPatternType[] = "add_to_cart_request_regex"; + } // namespace // static @@ -44,7 +53,18 @@ } hint_heuristics_ = std::move(*hint_json_value->GetIfDict()); global_heuristics_ = std::move(*global_json_value->GetIfDict()); + // Global regex patterns. product_skip_pattern_ = ConstructGlobalRegex(kSkipProductPatternType); + rule_discount_partner_merchant_pattern_ = + ConstructGlobalRegex(kRuleDiscountPartnerMerchantPatternType); + coupon_discount_partner_merchant_pattern_ = + ConstructGlobalRegex(kCouponDiscountPartnerMerchantPatternType); + cart_url_pattern_ = ConstructGlobalRegex(kCartPagetURLPatternType); + checkout_url_pattern_ = ConstructGlobalRegex(kCheckoutPageURLPatternType); + purchase_button_pattern_ = + ConstructGlobalRegex(kPurchaseButtonTextPatternType); + add_to_cart_request_pattern_ = + ConstructGlobalRegex(kAddToCartRequestPatternType); return true; } @@ -62,6 +82,32 @@ return product_skip_pattern_.get(); } +const re2::RE2* +CommerceHeuristicsData::GetRuleDiscountPartnerMerchantPattern() { + return rule_discount_partner_merchant_pattern_.get(); +} + +const re2::RE2* +CommerceHeuristicsData::GetCouponDiscountPartnerMerchantPattern() { + return coupon_discount_partner_merchant_pattern_.get(); +} + +const re2::RE2* CommerceHeuristicsData::GetCartPageURLPattern() { + return cart_url_pattern_.get(); +} + +const re2::RE2* CommerceHeuristicsData::GetCheckoutPageURLPattern() { + return checkout_url_pattern_.get(); +} + +const re2::RE2* CommerceHeuristicsData::GetPurchaseButtonTextPattern() { + return purchase_button_pattern_.get(); +} + +const re2::RE2* CommerceHeuristicsData::GetAddToCartRequestPattern() { + return add_to_cart_request_pattern_.get(); +} + absl::optional<std::string> CommerceHeuristicsData::GetCommerceHintHeuristics( const std::string& type, const std::string& domain) {
diff --git a/components/commerce/core/commerce_heuristics_data.h b/components/commerce/core/commerce_heuristics_data.h index 3cd7391..1c3f7519 100644 --- a/components/commerce/core/commerce_heuristics_data.h +++ b/components/commerce/core/commerce_heuristics_data.h
@@ -34,6 +34,27 @@ // Try to get the product skip pattern. const re2::RE2* GetProductSkipPattern(); + // Try to get the pattern regex to decide if a merchant is a partner merchant + // for rule discount. + const re2::RE2* GetRuleDiscountPartnerMerchantPattern(); + + // Try to get the pattern regex to decide if a merchant is a partner merchant + // for coupon discount. + const re2::RE2* GetCouponDiscountPartnerMerchantPattern(); + + // Try to get the pattern regex to decide if a URL is cart page URL. + const re2::RE2* GetCartPageURLPattern(); + + // Try to get the pattern regex to decide if a URL is checkout page URL. + const re2::RE2* GetCheckoutPageURLPattern(); + + // Try to get the pattern regex to decide if a button is a purchase button. + const re2::RE2* GetPurchaseButtonTextPattern(); + + // Try to get the pattern regex to decide if a request is a add-to-cart + // request. + const re2::RE2* GetAddToCartRequestPattern(); + private: friend class CommerceHeuristicsDataTest; @@ -49,6 +70,12 @@ base::Value::Dict hint_heuristics_; base::Value::Dict global_heuristics_; std::unique_ptr<re2::RE2> product_skip_pattern_; + std::unique_ptr<re2::RE2> rule_discount_partner_merchant_pattern_; + std::unique_ptr<re2::RE2> coupon_discount_partner_merchant_pattern_; + std::unique_ptr<re2::RE2> cart_url_pattern_; + std::unique_ptr<re2::RE2> checkout_url_pattern_; + std::unique_ptr<re2::RE2> purchase_button_pattern_; + std::unique_ptr<re2::RE2> add_to_cart_request_pattern_; }; } // namespace commerce_heuristics
diff --git a/components/commerce/core/commerce_heuristics_data_unittest.cc b/components/commerce/core/commerce_heuristics_data_unittest.cc index f660e7c..7d90958 100644 --- a/components/commerce/core/commerce_heuristics_data_unittest.cc +++ b/components/commerce/core/commerce_heuristics_data_unittest.cc
@@ -22,7 +22,13 @@ )###"; const char kGlobalHeuristicsJSONData[] = R"###( { - "sensitive_product_regex": "\\b\\B" + "sensitive_product_regex": "\\b\\B", + "rule_discount_partner_merchant_regex": "foo", + "coupon_discount_partner_merchant_regex": "bar", + "cart_page_url_regex": "cart", + "checkout_page_url_regex": "checkout", + "purchase_button_text_regex": "purchase", + "add_to_cart_request_regex": "add_to_cart" } )###"; } // namespace @@ -59,10 +65,31 @@ ASSERT_EQ(*hint_heuristics->FindDict("bar.com")->FindString("merchant_name"), "Bar"); auto* global_heuristics = GetGlobalHeuristics(); - ASSERT_EQ(global_heuristics->size(), 1u); + ASSERT_EQ(global_heuristics->size(), 7u); ASSERT_TRUE(global_heuristics->contains("sensitive_product_regex")); ASSERT_EQ(*global_heuristics->FindString("sensitive_product_regex"), "\\b\\B"); + ASSERT_TRUE( + global_heuristics->contains("rule_discount_partner_merchant_regex")); + ASSERT_EQ( + *global_heuristics->FindString("rule_discount_partner_merchant_regex"), + "foo"); + ASSERT_TRUE( + global_heuristics->contains("coupon_discount_partner_merchant_regex")); + ASSERT_EQ( + *global_heuristics->FindString("coupon_discount_partner_merchant_regex"), + "bar"); + ASSERT_TRUE(global_heuristics->contains("cart_page_url_regex")); + ASSERT_EQ(*global_heuristics->FindString("cart_page_url_regex"), "cart"); + ASSERT_TRUE(global_heuristics->contains("checkout_page_url_regex")); + ASSERT_EQ(*global_heuristics->FindString("checkout_page_url_regex"), + "checkout"); + ASSERT_TRUE(global_heuristics->contains("purchase_button_text_regex")); + ASSERT_EQ(*global_heuristics->FindString("purchase_button_text_regex"), + "purchase"); + ASSERT_TRUE(global_heuristics->contains("add_to_cart_request_regex")); + ASSERT_EQ(*global_heuristics->FindString("add_to_cart_request_regex"), + "add_to_cart"); } TEST_F(CommerceHeuristicsDataTest, TestPopulateHeuristics_Failure) { @@ -114,4 +141,59 @@ ASSERT_EQ(data.GetProductSkipPattern()->pattern(), "\\b\\B"); } + +TEST_F(CommerceHeuristicsDataTest, TestGetRuleDiscountPartnerMerchantPattern) { + auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance(); + + ASSERT_TRUE(data.PopulateDataFromComponent( + kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", "")); + + ASSERT_EQ(data.GetRuleDiscountPartnerMerchantPattern()->pattern(), "foo"); +} + +TEST_F(CommerceHeuristicsDataTest, + TestGetCouponDiscountPartnerMerchantPattern) { + auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance(); + + ASSERT_TRUE(data.PopulateDataFromComponent( + kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", "")); + + ASSERT_EQ(data.GetCouponDiscountPartnerMerchantPattern()->pattern(), "bar"); +} + +TEST_F(CommerceHeuristicsDataTest, TestGetCartPageURLPattern) { + auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance(); + + ASSERT_TRUE(data.PopulateDataFromComponent( + kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", "")); + + ASSERT_EQ(data.GetCartPageURLPattern()->pattern(), "cart"); +} + +TEST_F(CommerceHeuristicsDataTest, TestGetCheckoutPageURLPattern) { + auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance(); + + ASSERT_TRUE(data.PopulateDataFromComponent( + kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", "")); + + ASSERT_EQ(data.GetCheckoutPageURLPattern()->pattern(), "checkout"); +} + +TEST_F(CommerceHeuristicsDataTest, TestGetPurchaseButtonTextPattern) { + auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance(); + + ASSERT_TRUE(data.PopulateDataFromComponent( + kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", "")); + + ASSERT_EQ(data.GetPurchaseButtonTextPattern()->pattern(), "purchase"); +} + +TEST_F(CommerceHeuristicsDataTest, TestGetAddToCartRequestPattern) { + auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance(); + + ASSERT_TRUE(data.PopulateDataFromComponent( + kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", "")); + + ASSERT_EQ(data.GetAddToCartRequestPattern()->pattern(), "add_to_cart"); +} } // namespace commerce_heuristics
diff --git a/components/content_creation/notes/core/BUILD.gn b/components/content_creation/notes/core/BUILD.gn index f93488f3..4c5dadba 100644 --- a/components/content_creation/notes/core/BUILD.gn +++ b/components/content_creation/notes/core/BUILD.gn
@@ -57,6 +57,7 @@ "//base/test:test_support", "//components/content_creation/notes/core/server", "//components/content_creation/notes/core/templates", + "//components/content_creation/notes/core/templates:template_storage_proto", "//components/content_creation/notes/core/templates:types", "//components/content_creation/notes/core/test:test_support", "//components/prefs:test_support",
diff --git a/components/content_creation/notes/core/templates/BUILD.gn b/components/content_creation/notes/core/templates/BUILD.gn index 9679f04..6ee2083a 100644 --- a/components/content_creation/notes/core/templates/BUILD.gn +++ b/components/content_creation/notes/core/templates/BUILD.gn
@@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//third_party/protobuf/proto_library.gni") + static_library("templates") { sources = [ "note_template.cc", @@ -15,6 +17,7 @@ ] deps = [ + ":template_storage_proto", ":types", "//base", "//components/content_creation/notes/core:features", @@ -33,5 +36,13 @@ "template_types.h", ] - deps = [ "//base" ] + deps = [ + ":template_storage_proto", + "//base", + ] +} + +proto_library("template_storage_proto") { + sources = [ "template_storage.proto" ] + generate_python = false }
diff --git a/components/content_creation/notes/core/templates/note_template.cc b/components/content_creation/notes/core/templates/note_template.cc index cb64210..3d5ae543 100644 --- a/components/content_creation/notes/core/templates/note_template.cc +++ b/components/content_creation/notes/core/templates/note_template.cc
@@ -30,6 +30,18 @@ text_style_(text_style), footer_style_(footer_style) {} +NoteTemplate::NoteTemplate(const proto::NoteTemplate& note_template) + : id_(static_cast<NoteTemplateIds>(note_template.id())), + // TODO(graysonlafleur): remove localized_name UI element. + localized_name_(""), + main_background_(Background::Init(note_template.mainbackground())), + text_style_(note_template.textstyle()), + footer_style_(note_template.footerstyle()) { + if (note_template.has_contentbackground()) { + content_background_ = Background::Init(note_template.contentbackground()); + } +} + NoteTemplate::NoteTemplate(const NoteTemplate& other) : id_(other.id()), localized_name_(other.localized_name()),
diff --git a/components/content_creation/notes/core/templates/note_template.h b/components/content_creation/notes/core/templates/note_template.h index 126d2267..026559b 100644 --- a/components/content_creation/notes/core/templates/note_template.h +++ b/components/content_creation/notes/core/templates/note_template.h
@@ -6,6 +6,7 @@ #include <string> +#include "components/content_creation/notes/core/templates/template_storage.pb.h" #include "components/content_creation/notes/core/templates/template_types.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -29,6 +30,9 @@ const TextStyle& text_style, const FooterStyle& footer_style); + // Created a NoteTemplate object based on a protobuf NoteTemplate object. + explicit NoteTemplate(const proto::NoteTemplate& note_template); + NoteTemplate(const NoteTemplate& other); ~NoteTemplate();
diff --git a/components/content_creation/notes/core/templates/template_storage.proto b/components/content_creation/notes/core/templates/template_storage.proto new file mode 100644 index 0000000..9109e17 --- /dev/null +++ b/components/content_creation/notes/core/templates/template_storage.proto
@@ -0,0 +1,76 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +syntax = "proto3"; + +option optimize_for = LITE_RUNTIME; + +package content_creation.proto; + +// Specifies linear gradient chracteristics such as orientation +// and color for background. +message Gradient { + int32 orientation = 1; + repeated uint32 colors = 2; +} +// Specifies background information which can be solid color, a gradient +// or an image. +message Background { + oneof constructor_options { + uint32 color = 1; + Gradient gradient = 2; + string url = 3; + } +} +// Specifies text display characteristics. +message TextStyle { + string name = 1; + uint32 color = 2; + uint32 weight = 3; + bool allCaps = 4; + int32 alignment = 5; + int32 minTextSize = 6; + int32 maxTextSize = 7; + uint32 highlightColor = 8; + int32 highlightStyle = 9; +} +// Specifies the appearance of the elements in a note's footer. +message FooterStyle { + uint32 textColor = 1; + uint32 logoColor = 2; +} +// Specifies day, month, year used for template activation and expiration. +message Date { + int32 month = 1; + int32 day = 2; + int32 year = 3; +} +// Specifies the collection of all templates in order in which they should +// be displayed. +message Collection { + repeated CollectionItem templates = 1; + int32 max_template_number = 2; +} +// Specifies activation/expiration and GEO for which the given template should +// be valid. +message CollectionItem { + NoteTemplate templateId = 1; + Date activation = 2; + Date expiration = 3; + repeated int32 geo = 4; +} +// Specifies all visual characteristics for displaying a note. +message NoteTemplate { + // ID used to identify a given template. + int32 Id = 1; + // Main background for the Note. + Background mainBackground = 2; + // background for text area. For example, see "Lovely" template + Background contentBackground = 3; + // Targets the text that was highlighted + TextStyle textStyle = 4; + // Used to control the styling of elements in the note’s footer (domain, page + // title, Chrome logo). + FooterStyle footerStyle = 5; +}
diff --git a/components/content_creation/notes/core/templates/template_store.cc b/components/content_creation/notes/core/templates/template_store.cc index 3efc755..a9f896b 100644 --- a/components/content_creation/notes/core/templates/template_store.cc +++ b/components/content_creation/notes/core/templates/template_store.cc
@@ -11,6 +11,7 @@ #include "base/task/task_runner_util.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" +#include "base/time/time.h" #include "components/content_creation/notes/core/note_features.h" #include "components/content_creation/notes/core/note_prefs.h" #include "components/content_creation/notes/core/templates/note_template.h" @@ -22,6 +23,25 @@ namespace content_creation { +namespace { + +bool ConvertProtoDateToTime(proto::Date date, base::Time& time_date) { + base::Time::Exploded exploded_date = { + /*year=*/date.year(), + /*month=*/date.month(), + /*day_of_week=*/0, + /*day_of_month=*/date.day(), + /*hour=*/0, + /*minute=*/0, + /*second=*/0, + /*millisecond=*/0, + }; + + return base::Time::FromLocalExploded(exploded_date, &time_date); +} + +} // namespace + TemplateStore::TemplateStore( PrefService* pref_service, scoped_refptr<network::SharedURLLoaderFactory> url_loader) @@ -67,10 +87,56 @@ return templates; } +bool TemplateStore::TemplateAvailable(proto::CollectionItem current_template, + base::Time today) { + base::Time activation; + base::Time expiration; + + if (current_template.has_activation() && + (!ConvertProtoDateToTime(current_template.activation(), activation) || + today < activation)) { + return false; + } + + if (current_template.has_expiration() && + (!ConvertProtoDateToTime(current_template.expiration(), expiration) || + today >= expiration)) { + return false; + } + + return true; +} + std::vector<NoteTemplate> TemplateStore::ParseTemplatesFromString( std::string response_body) { - // TODO(graysonlafleur): implement dynamic templates here - return BuildDefaultTemplates(); + std::vector<NoteTemplate> templates = {}; + proto::Collection collection; + + if (!collection.ParseFromString(response_body)) { + return BuildDefaultTemplates(); + } + + int numTemplates = 0; + base::Time today = base::Time::NowFromSystemTime(); + + for (int i = 0; i < collection.templates_size() && + numTemplates < collection.max_template_number(); + i++) { + proto::CollectionItem current_template = collection.templates(i); + + if (!TemplateAvailable(current_template, today)) { + continue; + } + + templates.push_back(NoteTemplate(current_template.templateid())); + numTemplates++; + } + + if (templates.size() == 0) { + return BuildDefaultTemplates(); + } + + return templates; } void TemplateStore::OnTemplatesReceived(
diff --git a/components/content_creation/notes/core/templates/template_store.h b/components/content_creation/notes/core/templates/template_store.h index 157d058..2640c0c 100644 --- a/components/content_creation/notes/core/templates/template_store.h +++ b/components/content_creation/notes/core/templates/template_store.h
@@ -12,10 +12,15 @@ #include "base/memory/weak_ptr.h" #include "base/task/sequenced_task_runner.h" #include "components/content_creation/notes/core/templates/template_fetcher.h" +#include "components/content_creation/notes/core/templates/template_storage.pb.h" #include "services/network/public/cpp/shared_url_loader_factory.h" class PrefService; +namespace base { +class Time; +} + namespace content_creation { class NoteTemplate; @@ -36,6 +41,11 @@ TemplateStore(const TemplateStore&) = delete; TemplateStore& operator=(const TemplateStore&) = delete; + // Checks whether given template should be available based on activation and + // expiration dates. + bool TemplateAvailable(proto::CollectionItem current_template, + base::Time today); + // Calls Start() in TemplateFetcher to do a GET request and send the // data from the URL to OnFetchTemplateComplete. void FetchTemplates(GetTemplatesCallback callback);
diff --git a/components/content_creation/notes/core/templates/template_store_unittest.cc b/components/content_creation/notes/core/templates/template_store_unittest.cc index 8c7ee0b..fc08dd6 100644 --- a/components/content_creation/notes/core/templates/template_store_unittest.cc +++ b/components/content_creation/notes/core/templates/template_store_unittest.cc
@@ -13,6 +13,7 @@ #include "base/test/task_environment.h" #include "components/content_creation/notes/core/note_features.h" #include "components/content_creation/notes/core/templates/note_template.h" +#include "components/content_creation/notes/core/templates/template_storage.pb.h" #include "components/content_creation/notes/core/templates/template_types.h" #include "components/prefs/testing_pref_service.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" @@ -25,6 +26,17 @@ void SetUp() override { template_store_ = std::make_unique<TemplateStore>( &testing_pref_service_, test_url_loader_factory()); + + // This sets it to Linux Epoch due since it is a test. + jan_01_1970_ = base::Time::NowFromSystemTime(); + + jan_10_1960_.set_day(10); + jan_10_1960_.set_month(1); + jan_10_1960_.set_year(1960); + + jan_10_2001_.set_day(10); + jan_10_2001_.set_month(1); + jan_10_2001_.set_year(2001); } protected: @@ -37,7 +49,6 @@ for (const NoteTemplate& note_template : note_templates) { EXPECT_LT(NoteTemplateIds::kUnknown, note_template.id()); EXPECT_GE(NoteTemplateIds::kMaxValue, note_template.id()); - EXPECT_FALSE(note_template.localized_name().empty()); EXPECT_FALSE(note_template.text_style().font_name().empty()); // There should be no duplicated IDs. @@ -46,6 +57,171 @@ } } + proto::NoteTemplate GetClassicTemplate() { + // Background + proto::Background background; + + background.set_color(0xFF202124); + + /*===========================*/ + + // TextStyle + proto::TextStyle text_style; + + text_style.set_name("Source Serif Pro"); + text_style.set_color(0xFFFFFFFF); + text_style.set_weight(700); + text_style.set_allcaps(false); + text_style.set_alignment(1); + text_style.set_mintextsize(14); + text_style.set_maxtextsize(48); + + /*===========================*/ + + // FooterStyle + proto::FooterStyle footer_style; + + footer_style.set_textcolor(0xB3FFFFFF); + footer_style.set_logocolor(0x33FFFFFF); + + /*===========================*/ + + // NoteTemplate + proto::NoteTemplate classic; + + classic.set_id(1); + classic.set_allocated_mainbackground(new proto::Background(background)); + classic.set_allocated_textstyle(new proto::TextStyle(text_style)); + classic.set_allocated_footerstyle(new proto::FooterStyle(footer_style)); + + return classic; + } + + proto::NoteTemplate GetFriendlyTemplate() { + proto::Background background; + + background.set_url( + "https://www.gstatic.com/chrome/content/" + "templates/FriendlyBackground@2x.png"); + + /*===========================*/ + + proto::TextStyle text_style; + + text_style.set_name("Rock Salt"); + text_style.set_color(0xFF202124); + text_style.set_weight(400); + text_style.set_allcaps(false); + text_style.set_alignment(1); + text_style.set_mintextsize(14); + text_style.set_maxtextsize(48); + + /*===========================*/ + + proto::FooterStyle footer_style; + + footer_style.set_textcolor(0xCC000000); + footer_style.set_logocolor(0x1A000000); + + /*===========================*/ + + proto::NoteTemplate friendly; + + friendly.set_id(2); + friendly.set_allocated_mainbackground(new proto::Background(background)); + friendly.set_allocated_textstyle(new proto::TextStyle(text_style)); + friendly.set_allocated_footerstyle(new proto::FooterStyle(footer_style)); + + return friendly; + } + + proto::NoteTemplate GetLovelyTemplate() { + proto::Background background; + proto::Gradient gradient; + + gradient.set_orientation(2); + gradient.add_colors(0xFFCEF9FF); + gradient.add_colors(0xFFF1DFFF); + + background.set_allocated_gradient(new proto::Gradient(gradient)); + + proto::Background content_background; + + content_background.set_color(0xFFFFFFFF); + + /*===========================*/ + + proto::TextStyle text_style; + + text_style.set_name("Source Serif Pro"); + text_style.set_color(0xFF000000); + text_style.set_weight(400); + text_style.set_allcaps(false); + text_style.set_alignment(2); + text_style.set_mintextsize(14); + text_style.set_maxtextsize(48); + + /*===========================*/ + + proto::FooterStyle footer_style; + + footer_style.set_textcolor(0xCC000000); + footer_style.set_logocolor(0x1A000000); + + /*===========================*/ + + proto::NoteTemplate lovely; + + lovely.set_id(6); + lovely.set_allocated_mainbackground(new proto::Background(background)); + lovely.set_allocated_contentbackground( + new proto::Background(content_background)); + lovely.set_allocated_textstyle(new proto::TextStyle(text_style)); + lovely.set_allocated_footerstyle(new proto::FooterStyle(footer_style)); + + return lovely; + } + + std::string GetOneMaxTemplatesValidProtoString() { + std::string data; + + proto::Collection collection; + collection.set_max_template_number(1); + + proto::NoteTemplate classic = GetClassicTemplate(); + proto::NoteTemplate friendly = GetFriendlyTemplate(); + + proto::CollectionItem* classic_template = collection.add_templates(); + classic_template->set_allocated_templateid( + new proto::NoteTemplate(classic)); + proto::CollectionItem* friendly_template = collection.add_templates(); + friendly_template->set_allocated_templateid( + new proto::NoteTemplate(friendly)); + + collection.SerializeToString(&data); + return data; + } + + std::string GetInvalidProtoString() { + std::string data; + + proto::Collection collection; + // collection.set_max_template_number() is missing which is required. + + proto::NoteTemplate classic = GetClassicTemplate(); + proto::NoteTemplate friendly = GetFriendlyTemplate(); + + proto::CollectionItem* classic_template = collection.add_templates(); + classic_template->set_allocated_templateid( + new proto::NoteTemplate(classic)); + proto::CollectionItem* friendly_template = collection.add_templates(); + friendly_template->set_allocated_templateid( + new proto::NoteTemplate(friendly)); + + collection.SerializeToString(&data); + return data; + } + // Have to use TaskEnvironment since the TemplateStore posts tasks to the // thread pool. base::test::TaskEnvironment task_environment_{ @@ -55,11 +231,17 @@ TestingPrefServiceSimple testing_pref_service_; network::TestURLLoaderFactory test_url_loader_factory_; std::unique_ptr<TemplateStore> template_store_; + + base::Time jan_01_1970_; + proto::Date jan_10_1960_; + proto::Date jan_10_2001_; + proto::Date invalid_date_; + proto::Collection collection_; }; // Tests that the store does return templates, and also validates the // templates' information. -TEST_F(TemplateStoreTest, GetTemplatesSuccessWithDefaultTemplates) { +TEST_F(TemplateStoreTest, DefaultTemplates) { scoped_feature_list_.InitWithFeatures({kWebNotesStylizeEnabled}, {kWebNotesDynamicTemplates}); base::RunLoop run_loop; @@ -76,7 +258,154 @@ run_loop.Run(); } -TEST_F(TemplateStoreTest, GetTemplatesSuccessWithDynamicTemplates) { +// Test that templates without any dates will still be shown to the user. +TEST_F(TemplateStoreTest, NoDates) { + proto::CollectionItem* no_dates = collection_.add_templates(); + no_dates->set_allocated_templateid( + new proto::NoteTemplate(GetFriendlyTemplate())); + + EXPECT_TRUE(template_store_->TemplateAvailable(*no_dates, jan_01_1970_)); +} + +// Tests that templates with expiration dates that have not expired yet will be +// shown to the user. +TEST_F(TemplateStoreTest, ActiveExpiration) { + proto::CollectionItem* active_expiration = collection_.add_templates(); + active_expiration->set_allocated_templateid( + new proto::NoteTemplate(GetFriendlyTemplate())); + + active_expiration->set_allocated_expiration(new proto::Date(jan_10_2001_)); + EXPECT_TRUE( + template_store_->TemplateAvailable(*active_expiration, jan_01_1970_)); +} + +// Tests that templates with expired expiration dates will not be shown to the +// user. +TEST_F(TemplateStoreTest, InactiveExpiration) { + proto::CollectionItem* inactive_expiration = collection_.add_templates(); + inactive_expiration->set_allocated_templateid( + new proto::NoteTemplate(GetFriendlyTemplate())); + + inactive_expiration->set_allocated_expiration(new proto::Date(jan_10_1960_)); + EXPECT_FALSE( + template_store_->TemplateAvailable(*inactive_expiration, jan_01_1970_)); +} + +// Tests that templates with proto::Date objects that are missing fields will +// not be shown to the user. +TEST_F(TemplateStoreTest, InvalidDates) { + proto::CollectionItem* invalid_expiration = collection_.add_templates(); + invalid_expiration->set_allocated_templateid( + new proto::NoteTemplate(GetFriendlyTemplate())); + + invalid_expiration->set_allocated_expiration(new proto::Date(invalid_date_)); + EXPECT_FALSE( + template_store_->TemplateAvailable(*invalid_expiration, jan_01_1970_)); + + proto::CollectionItem* invalid_activation = collection_.add_templates(); + invalid_activation->set_allocated_templateid( + new proto::NoteTemplate(GetFriendlyTemplate())); + + invalid_activation->set_allocated_activation(new proto::Date(invalid_date_)); + EXPECT_FALSE( + template_store_->TemplateAvailable(*invalid_activation, jan_01_1970_)); +} + +// Tests that templates with activation dates that have passed will be shown to +// the user. +TEST_F(TemplateStoreTest, ActiveActivation) { + proto::CollectionItem* active_activation = collection_.add_templates(); + active_activation->set_allocated_templateid( + new proto::NoteTemplate(GetFriendlyTemplate())); + + active_activation->set_allocated_activation(new proto::Date(jan_10_1960_)); + EXPECT_TRUE( + template_store_->TemplateAvailable(*active_activation, jan_01_1970_)); +} + +// Tests that templates with activation dates that have yet to pass will not be +// shown to the user. +TEST_F(TemplateStoreTest, InactiveActivation) { + proto::CollectionItem* inactive_activation = collection_.add_templates(); + inactive_activation->set_allocated_templateid( + new proto::NoteTemplate(GetFriendlyTemplate())); + + inactive_activation->set_allocated_activation(new proto::Date(jan_10_2001_)); + EXPECT_FALSE( + template_store_->TemplateAvailable(*inactive_activation, jan_01_1970_)); +} + +// Tests that if today's date is within activation and expiration date, it will +// be passed to the user. +TEST_F(TemplateStoreTest, BothDatesActive) { + proto::CollectionItem* both_dates_active = collection_.add_templates(); + both_dates_active->set_allocated_templateid( + new proto::NoteTemplate(GetFriendlyTemplate())); + + both_dates_active->set_allocated_activation(new proto::Date(jan_10_1960_)); + both_dates_active->set_allocated_expiration(new proto::Date(jan_10_2001_)); + EXPECT_TRUE( + template_store_->TemplateAvailable(*both_dates_active, jan_01_1970_)); +} + +// Tests that if the activation and expiration dates are in the wrong order +// (activation comes after the expiration here), it will not be shown to the +// user. +TEST_F(TemplateStoreTest, BothDatesInactive) { + proto::CollectionItem* both_dates_inactive = collection_.add_templates(); + both_dates_inactive->set_allocated_templateid( + new proto::NoteTemplate(GetFriendlyTemplate())); + + both_dates_inactive->set_allocated_activation(new proto::Date(jan_10_2001_)); + both_dates_inactive->set_allocated_expiration(new proto::Date(jan_10_1960_)); + EXPECT_FALSE( + template_store_->TemplateAvailable(*both_dates_inactive, jan_01_1970_)); +} + +// Tests that if a date is impossible, such as the 40th day, 13th month, or +// 1000000th year (base::Time::exploded requires year be 4 digits) it will not +// be shown to the user. +TEST_F(TemplateStoreTest, ImpossibleDates) { + proto::CollectionItem* invalid_expiration = collection_.add_templates(); + invalid_expiration->set_allocated_templateid( + new proto::NoteTemplate(GetFriendlyTemplate())); + + invalid_date_.set_day(40); + invalid_date_.set_month(13); + invalid_date_.set_year(1000000); + + invalid_expiration->set_allocated_expiration(new proto::Date(invalid_date_)); + EXPECT_FALSE( + template_store_->TemplateAvailable(*invalid_expiration, jan_01_1970_)); +} + +// Tests that it will stop at the set maximum number of templates even if there +// are more templates that are available. +TEST_F(TemplateStoreTest, OneMaxTemplates) { + test_url_loader_factory_.AddResponse( + kTemplateUrl, GetOneMaxTemplatesValidProtoString(), net::HTTP_OK); + scoped_feature_list_.InitWithFeatures( + {kWebNotesStylizeEnabled, kWebNotesDynamicTemplates}, {}); + base::RunLoop run_loop; + + template_store_->GetTemplates(base::BindLambdaForTesting( + [&run_loop, this](std::vector<NoteTemplate> templates) { + EXPECT_EQ(1U, templates.size()); + // Tests to make sure it got the first template and not the second one. + EXPECT_EQ(static_cast<int>(templates.at(0).id()), 1); + + ValidateTemplates(templates); + + run_loop.Quit(); + })); + + run_loop.Run(); +} + +// Tests that it will give the user default templates if it is given invalid +// Protobuf data. +TEST_F(TemplateStoreTest, EmptyString) { + test_url_loader_factory_.AddResponse(kTemplateUrl, "", net::HTTP_OK); scoped_feature_list_.InitWithFeatures( {kWebNotesStylizeEnabled, kWebNotesDynamicTemplates}, {}); base::RunLoop run_loop;
diff --git a/components/content_creation/notes/core/templates/template_types.cc b/components/content_creation/notes/core/templates/template_types.cc index a6f3d79..3813250a 100644 --- a/components/content_creation/notes/core/templates/template_types.cc +++ b/components/content_creation/notes/core/templates/template_types.cc
@@ -38,6 +38,22 @@ DCHECK(image_url.size() > 0); } +Background Background::Init(const proto::Background& background) { + if (background.color() != 0) { + return Background(static_cast<ARGBColor>(background.color())); + } + + if (!background.url().empty()) { + return Background(background.url()); + } + + return Background( + std::vector<ARGBColor>(background.gradient().colors().begin(), + background.gradient().colors().end()), + static_cast<LinearGradientDirection>( + background.gradient().orientation())); +} + Background::Background(const Background& other) { color_ = other.color(); colors_ = other.colors(); @@ -85,6 +101,20 @@ highlight_color_(highlight_color), highlight_style_(highlight_style) {} +TextStyle::TextStyle(const proto::TextStyle& textstyle) + : TextStyle(textstyle.name(), + textstyle.color(), + textstyle.weight(), + textstyle.allcaps(), + static_cast<TextAlignment>(textstyle.alignment()), + textstyle.mintextsize(), + textstyle.maxtextsize()) { + if (textstyle.highlightcolor() != 0) { + highlight_color_ = textstyle.highlightcolor(); + highlight_style_ = static_cast<HighlightStyle>(textstyle.highlightstyle()); + } +} + TextStyle::TextStyle(const TextStyle& text_style) = default; TextStyle& TextStyle::operator=(const TextStyle& text_style) = default; @@ -92,4 +122,7 @@ FooterStyle::FooterStyle(ARGBColor text_color, ARGBColor logo_color) : text_color_(text_color), logo_color_(logo_color) {} +FooterStyle::FooterStyle(const proto::FooterStyle& footerstyle) + : FooterStyle(footerstyle.textcolor(), footerstyle.logocolor()) {} + } // namespace content_creation
diff --git a/components/content_creation/notes/core/templates/template_types.h b/components/content_creation/notes/core/templates/template_types.h index f62b6bf..2c4bcb8 100644 --- a/components/content_creation/notes/core/templates/template_types.h +++ b/components/content_creation/notes/core/templates/template_types.h
@@ -7,6 +7,8 @@ #include <string> #include <vector> +#include "components/content_creation/notes/core/templates/template_storage.pb.h" + namespace content_creation { using ARGBColor = uint32_t; @@ -52,6 +54,10 @@ // Creates an image background based on a remotely hosted image's URL. explicit Background(const std::string& image_url); + // Creates a Background from a protobuf Background object while still + // ensuring the DChecks(). + static Background Init(const proto::Background& background); + Background(const Background& other); ~Background(); @@ -107,6 +113,9 @@ ARGBColor highlight_color, HighlightStyle highlight_style); + // Creates a TextStyle from a protobuf TextStyle object. + explicit TextStyle(const proto::TextStyle& textstyle); + TextStyle(const TextStyle& text_style); TextStyle& operator=(const TextStyle& text_style); @@ -137,6 +146,9 @@ public: explicit FooterStyle(ARGBColor text_color, ARGBColor logo_color); + // Creates a FooterStyle from a protobuf FooterStyle object. + explicit FooterStyle(const proto::FooterStyle& footerstyle); + ARGBColor text_color() const { return text_color_; } ARGBColor logo_color() const { return logo_color_; }
diff --git a/components/custom_handlers/protocol_handler.cc b/components/custom_handlers/protocol_handler.cc index 625f4529..aedebf6 100644 --- a/components/custom_handlers/protocol_handler.cc +++ b/components/custom_handlers/protocol_handler.cc
@@ -73,24 +73,11 @@ } bool ProtocolHandler::IsValid() const { - // This matches VerifyCustomHandlerURLSecurity() in blink's - // NavigatorContentUtils. - // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters DCHECK_CURRENTLY_ON(BrowserThread::UI); - bool has_valid_scheme = - url_.SchemeIsHTTPOrHTTPS() || - (security_level_ == - blink::ProtocolHandlerSecurityLevel::kExtensionFeatures && - blink::CommonSchemeRegistry::IsExtensionScheme(url_.scheme())); - if (!has_valid_scheme || !network::IsUrlPotentiallyTrustworthy(url_)) + if (!blink::IsAllowedCustomHandlerURL(url_, security_level_)) return false; - bool has_custom_scheme_prefix = false; - bool allow_ext_plus_prefix = - (security_level_ >= - blink::ProtocolHandlerSecurityLevel::kExtensionFeatures); - return blink::IsValidCustomHandlerScheme(protocol_, allow_ext_plus_prefix, - has_custom_scheme_prefix); + return blink::IsValidCustomHandlerScheme(protocol_, security_level_); } bool ProtocolHandler::IsSameOrigin(const ProtocolHandler& handler) const {
diff --git a/components/custom_handlers/protocol_handler.h b/components/custom_handlers/protocol_handler.h index 3df097d..31895949 100644 --- a/components/custom_handlers/protocol_handler.h +++ b/components/custom_handlers/protocol_handler.h
@@ -56,6 +56,9 @@ static bool IsValidDict(const base::Value::Dict& value); // Return true if the protocol handler meets security constraints. + // Verify custom handler URLs security and syntax as well as the schemes + // safelist as described in steps 1, 2, 6 and 7 (except same origin). + // https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers. bool IsValid() const; // Returns true if this handler's url has the same origin as the given one.
diff --git a/components/download/internal/background_service/controller_impl.cc b/components/download/internal/background_service/controller_impl.cc index 8b99916..4a655b85 100644 --- a/components/download/internal/background_service/controller_impl.cc +++ b/components/download/internal/background_service/controller_impl.cc
@@ -1201,6 +1201,7 @@ entry->url_chain, entry->response_headers); completion_info.blob_handle = driver_entry->blob_handle; completion_info.hash256 = driver_entry->hash256; + completion_info.custom_data = entry->custom_data; entry->last_cleanup_check_time = driver_entry->completion_time; base::ThreadTaskRunnerHandle::Get()->PostTask( @@ -1213,6 +1214,7 @@ CompletionInfo completion_info; completion_info.url_chain = entry->url_chain; completion_info.response_headers = entry->response_headers; + completion_info.custom_data = entry->custom_data; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE,
diff --git a/components/download/internal/background_service/controller_impl_unittest.cc b/components/download/internal/background_service/controller_impl_unittest.cc index 40a2e41..b8d27375 100644 --- a/components/download/internal/background_service/controller_impl_unittest.cc +++ b/components/download/internal/background_service/controller_impl_unittest.cc
@@ -51,6 +51,8 @@ const base::FilePath::CharType kDownloadDirPath[] = FILE_PATH_LITERAL("/test/downloads"); +constexpr char kKey[] = "k"; +constexpr char kValue[] = "v"; bool GuidInEntryList(const std::vector<Entry>& entries, const std::string& guid) { @@ -793,16 +795,18 @@ TEST_F(DownloadServiceControllerImplTest, OnDownloadFailed) { // Setup download service test data. Entry entry = test::BuildBasicEntry(Entry::State::ACTIVE); + entry.custom_data = {{kKey, kValue}}; std::vector<Entry> entries = {entry}; // Setup download driver test data. DriverEntry dentry = BuildDriverEntry(entry, DriverEntry::State::IN_PROGRESS); driver_->AddTestData(std::vector<DriverEntry>{dentry}); + CompletionInfo completion_info; EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1); EXPECT_CALL(*client_, OnDownloadFailed(entry.guid, _, Client::FailureReason::NETWORK)) - .Times(1); + .WillOnce(SaveArg<1>(&completion_info)); device_status_listener_->SetDeviceStatus( DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED)); @@ -815,6 +819,9 @@ EXPECT_EQ(nullptr, model_->Get(entry.guid)); task_runner_->RunUntilIdle(); + + EXPECT_EQ(1u, completion_info.custom_data.size()); + EXPECT_EQ(kValue, completion_info.custom_data[kKey]); } TEST_F(DownloadServiceControllerImplTest, OnDownloadFailedFromDriverCancel) { @@ -978,6 +985,7 @@ TEST_F(DownloadServiceControllerImplTest, OnDownloadSucceeded) { // Setup download service test data. Entry entry = test::BuildBasicEntry(Entry::State::ACTIVE); + entry.custom_data[kKey] = kValue; std::vector<Entry> entries = {entry}; // Setup download driver test data. @@ -988,9 +996,9 @@ dentry.bytes_downloaded, entry.url_chain, entry.response_headers); completion_info.hash256 = "01234567ABCDEF"; + completion_info.custom_data[kKey] = kValue; EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1); - EXPECT_CALL(*client_, OnDownloadSucceeded(entry.guid, completion_info)) - .Times(1); + EXPECT_CALL(*client_, OnDownloadSucceeded(entry.guid, completion_info)); device_status_listener_->SetDeviceStatus( DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
diff --git a/components/download/internal/background_service/entry.cc b/components/download/internal/background_service/entry.cc index f893f9d..1028938 100644 --- a/components/download/internal/background_service/entry.cc +++ b/components/download/internal/background_service/entry.cc
@@ -35,6 +35,7 @@ create_time(base::Time::Now()), scheduling_params(params.scheduling_params), request_params(params.request_params), + custom_data(params.custom_data), bytes_downloaded(0u), bytes_uploaded(0u), attempt_count(0), @@ -72,7 +73,8 @@ AreHeadersEqual(response_headers.get(), other.response_headers.get()) && did_received_response == other.did_received_response && - require_response_headers == other.require_response_headers; + require_response_headers == other.require_response_headers && + custom_data == other.custom_data; } size_t Entry::EstimateMemoryUsage() const { @@ -82,7 +84,8 @@ base::trace_event::EstimateMemoryUsage(request_params.method) + base::trace_event::EstimateMemoryUsage( request_params.request_headers.ToString()) + - base::trace_event::EstimateMemoryUsage(target_file_path.value()); + base::trace_event::EstimateMemoryUsage(target_file_path.value()) + + base::trace_event::EstimateMemoryUsage(custom_data); } } // namespace download
diff --git a/components/download/internal/background_service/entry.h b/components/download/internal/background_service/entry.h index 3b37f0b..13b3945 100644 --- a/components/download/internal/background_service/entry.h +++ b/components/download/internal/background_service/entry.h
@@ -73,6 +73,10 @@ // The parameters that define the actual download request to make. RequestParams request_params; + // Custom key value pair provided by client and will sent back to client. See + // |custom_data| in DownloadParams for more details. + DownloadParams::CustomData custom_data; + // The state of the download to help the scheduler and loggers make the right // decisions about the download object. State state = State::NEW;
diff --git a/components/download/internal/background_service/entry_utils.cc b/components/download/internal/background_service/entry_utils.cc index 7bb6cbc3..9e8466d 100644 --- a/components/download/internal/background_service/entry_utils.cc +++ b/components/download/internal/background_service/entry_utils.cc
@@ -97,6 +97,8 @@ meta_data.completion_info = CompletionInfo(entry->target_file_path, entry->bytes_downloaded, entry->url_chain, entry->response_headers); + meta_data.completion_info->custom_data = entry->custom_data; + // If the download is completed, the |current_size| needs to pull from entry // since the history db record has been deleted. meta_data.current_size = entry->bytes_downloaded;
diff --git a/components/download/internal/background_service/entry_utils_unittest.cc b/components/download/internal/background_service/entry_utils_unittest.cc index 75f36af..a022250 100644 --- a/components/download/internal/background_service/entry_utils_unittest.cc +++ b/components/download/internal/background_service/entry_utils_unittest.cc
@@ -14,6 +14,11 @@ namespace download { +namespace { +constexpr char kKey[] = "k"; +constexpr char kValue[] = "v"; +} // namespace + TEST(DownloadServiceEntryUtilsTest, TestGetNumberOfLiveEntriesForClient) { Entry entry1 = test::BuildBasicEntry(); Entry entry2 = test::BuildBasicEntry(); @@ -155,10 +160,13 @@ entry = test::BuildBasicEntry(Entry::State::COMPLETE); entry.target_file_path = base::FilePath::FromUTF8Unsafe("123"); entry.bytes_downloaded = 100u; + entry.custom_data = {{kKey, kValue}}; meta_data = util::BuildDownloadMetaData(&entry, &driver); EXPECT_EQ(entry.guid, meta_data.guid); EXPECT_TRUE(meta_data.completion_info.has_value()); EXPECT_EQ(entry.target_file_path, meta_data.completion_info->path); + EXPECT_EQ(1u, meta_data.completion_info->custom_data.size()); + EXPECT_EQ(kValue, meta_data.completion_info->custom_data[kKey]); EXPECT_EQ(entry.bytes_downloaded, meta_data.completion_info->bytes_downloaded); }
diff --git a/components/download/internal/background_service/proto/entry.proto b/components/download/internal/background_service/proto/entry.proto index f699afc..e918538 100644 --- a/components/download/internal/background_service/proto/entry.proto +++ b/components/download/internal/background_service/proto/entry.proto
@@ -29,8 +29,17 @@ BOUNDARY = 10; } +// Custom key value pair provided by the client and will be sent back to client +// when download is completed. +// Next tag: 3 +message CustomData { + optional string key = 1; + optional string value = 2; +} + // Stores the request params, internal state, metrics and metadata associated // with a download request. +// Next tag: 21 message Entry { // This should stay in sync with the State enum // (components/download/internal/background_service/entry.h). @@ -76,4 +85,5 @@ optional string response_headers = 17; optional bool did_received_response = 18; optional bool require_response_headers = 19; + repeated CustomData custom_data = 20; }
diff --git a/components/download/internal/background_service/proto_conversions.cc b/components/download/internal/background_service/proto_conversions.cc index 1768714..6070dae 100644 --- a/components/download/internal/background_service/proto_conversions.cc +++ b/components/download/internal/background_service/proto_conversions.cc
@@ -319,6 +319,10 @@ entry.did_received_response = proto.did_received_response(); entry.require_response_headers = proto.require_response_headers(); + for (const auto& kv : proto.custom_data()) { + entry.custom_data[kv.key()] = kv.value(); + } + return entry; } @@ -347,6 +351,11 @@ proto.set_response_headers(entry.response_headers->raw_headers()); proto.set_did_received_response(entry.did_received_response); proto.set_require_response_headers(entry.require_response_headers); + for (const auto& kv : entry.custom_data) { + auto* custom_data_proto = proto.add_custom_data(); + custom_data_proto->set_key(kv.first); + custom_data_proto->set_value(kv.second); + } return proto; }
diff --git a/components/download/internal/background_service/proto_conversions_unittest.cc b/components/download/internal/background_service/proto_conversions_unittest.cc index ef4b561b..bb37472b 100644 --- a/components/download/internal/background_service/proto_conversions_unittest.cc +++ b/components/download/internal/background_service/proto_conversions_unittest.cc
@@ -13,8 +13,9 @@ #include "testing/gtest/include/gtest/gtest.h" namespace { -std::string TEST_URL = "https://google.com"; - +constexpr char kTestUrl[] = "https://www.example.com"; +constexpr char kKey[] = "k"; +constexpr char kValue[] = "v"; } // namespace namespace download { @@ -96,7 +97,7 @@ TEST_F(ProtoConversionsTest, RequestParamsWithHeadersConversion) { RequestParams expected; - expected.url = GURL(TEST_URL); + expected.url = GURL(kTestUrl); expected.method = "GET"; expected.fetch_error_body = true; expected.require_safety_checks = false; @@ -125,7 +126,7 @@ TEST_F(ProtoConversionsTest, RequestParamsWithMissingCredentialsMode) { RequestParams expected; - expected.url = GURL(TEST_URL); + expected.url = GURL(kTestUrl); expected.method = "GET"; protodb::RequestParams proto; @@ -145,9 +146,10 @@ DownloadClient::TEST, base::GenerateGUID(), base::Time::Now(), SchedulingParams::NetworkRequirements::OPTIMISTIC, SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE, - SchedulingParams::Priority::HIGH, GURL(TEST_URL), "GET", + SchedulingParams::Priority::HIGH, GURL(kTestUrl), "GET", Entry::State::ACTIVE, base::FilePath(FILE_PATH_LITERAL("/test/xyz")), base::Time::Now(), base::Time::Now(), base::Time::Now(), 1024u, 3, 8); + expected.custom_data = {{kKey, kValue}}; actual = EntryFromProto(EntryToProto(expected)); EXPECT_TRUE(test::CompareEntry(&expected, &actual)); } @@ -162,7 +164,7 @@ DownloadClient::TEST, base::GenerateGUID(), base::Time::Now(), SchedulingParams::NetworkRequirements::OPTIMISTIC, SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE, - SchedulingParams::Priority::HIGH, GURL(TEST_URL), "GET", + SchedulingParams::Priority::HIGH, GURL(kTestUrl), "GET", Entry::State::ACTIVE, base::FilePath(FILE_PATH_LITERAL("/test/xyz")), base::Time::Now(), base::Time::Now(), base::Time::Now(), 1024u, 2, 8));
diff --git a/components/download/internal/common/download_file_impl.cc b/components/download/internal/common/download_file_impl.cc index 6dd78c24..fd951fbc 100644 --- a/components/download/internal/common/download_file_impl.cc +++ b/components/download/internal/common/download_file_impl.cc
@@ -337,6 +337,13 @@ void DownloadFileImpl::RenameAndUniquify(const base::FilePath& full_path, RenameCompletionCallback callback) { +#if BUILDFLAG(IS_ANDROID) + if (full_path.IsContentUri()) { + DownloadInterruptReason reason = file_.Rename(full_path); + OnRenameComplete(full_path, std::move(callback), reason); + return; + } +#endif // BUILDFLAG(IS_ANDROID) std::unique_ptr<RenameParameters> parameters( new RenameParameters(UNIQUIFY, full_path, std::move(callback))); RenameWithRetryInternal(std::move(parameters)); @@ -359,39 +366,10 @@ } #if BUILDFLAG(IS_ANDROID) -void DownloadFileImpl::RenameToIntermediateUri( - const GURL& original_url, - const GURL& referrer_url, - const base::FilePath& file_name, - const std::string& mime_type, - const base::FilePath& current_path, - RenameCompletionCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // Create new content URI if |current_path| is not content URI - // or if it is already deleted. - base::FilePath content_path = - current_path.IsContentUri() && base::ContentUriExists(current_path) - ? current_path - : DownloadCollectionBridge::CreateIntermediateUriForPublish( - original_url, referrer_url, file_name, mime_type); - DownloadInterruptReason reason = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - if (!content_path.empty()) { - reason = file_.Rename(content_path); - display_name_ = DownloadCollectionBridge::GetDisplayName(content_path); - } - if (display_name_.empty()) - display_name_ = file_name; - OnRenameComplete(content_path, std::move(callback), reason); -} - void DownloadFileImpl::PublishDownload(RenameCompletionCallback callback) { DownloadInterruptReason reason = file_.PublishDownload(); OnRenameComplete(file_.full_path(), std::move(callback), reason); } - -base::FilePath DownloadFileImpl::GetDisplayName() { - return display_name_; -} #endif // BUILDFLAG(IS_ANDROID) base::TimeDelta DownloadFileImpl::GetRetryDelayForFailedRename(
diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc index e044cb05..1ab2c5c 100644 --- a/components/download/internal/common/download_item_impl.cc +++ b/components/download/internal/common/download_item_impl.cc
@@ -1758,6 +1758,7 @@ DownloadDangerType danger_type, MixedContentStatus mixed_content_status, const base::FilePath& intermediate_path, + const base::FilePath& display_name, absl::optional<DownloadSchedule> download_schedule, DownloadInterruptReason interrupt_reason) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -1802,6 +1803,8 @@ destination_info_.target_disposition = disposition; SetDangerType(danger_type); mixed_content_status_ = mixed_content_status; + if (!display_name.empty()) + SetDisplayName(display_name); // This was an interrupted download that was looking for a filename. Resolve // early without performing the intermediate rename. If there is a @@ -1836,31 +1839,15 @@ // filename. Unnecessary renames may cause bugs like // http://crbug.com/74187. DCHECK(!IsSavePackageDownload()); - DownloadFile::RenameCompletionCallback callback = - base::BindOnce(&DownloadItemImpl::OnDownloadRenamedToIntermediateName, - weak_ptr_factory_.GetWeakPtr()); -#if BUILDFLAG(IS_ANDROID) - if ((download_type_ == TYPE_ACTIVE_DOWNLOAD && !transient_ && - DownloadCollectionBridge::ShouldPublishDownload(GetTargetFilePath())) || - GetTargetFilePath().IsContentUri()) { - GetDownloadTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce(&DownloadFile::RenameToIntermediateUri, - // Safe because we control download file lifetime. - base::Unretained(download_file_.get()), GetOriginalUrl(), - GetReferrerUrl(), GetFileNameToReportUser(), - GetMimeType(), GetTargetFilePath(), - std::move(callback))); - return; - } -#endif // BUILDFLAG(IS_ANDROID) GetDownloadTaskRunner()->PostTask( FROM_HERE, - base::BindOnce(&DownloadFile::RenameAndUniquify, - // Safe because we control download file lifetime. - base::Unretained(download_file_.get()), intermediate_path, - std::move(callback))); + base::BindOnce( + &DownloadFile::RenameAndUniquify, + // Safe because we control download file lifetime. + base::Unretained(download_file_.get()), intermediate_path, + base::BindOnce(&DownloadItemImpl::OnDownloadRenamedToIntermediateName, + weak_ptr_factory_.GetWeakPtr()))); } void DownloadItemImpl::OnDownloadRenamedToIntermediateName( @@ -1874,14 +1861,6 @@ if (DOWNLOAD_INTERRUPT_REASON_NONE == reason) { SetFullPath(full_path); -#if BUILDFLAG(IS_ANDROID) - // For content URIs, target file path is the same as the current path. - if (full_path.IsContentUri()) { - destination_info_.target_path = full_path; - if (display_name_.empty()) - SetDisplayName(download_file_->GetDisplayName()); - } -#endif // BUILDFLAG(IS_ANDROID) } else { // TODO(asanka): Even though the rename failed, it may still be possible to // recover the partial state from the 'before' name.
diff --git a/components/download/internal/common/download_item_impl_delegate.cc b/components/download/internal/common/download_item_impl_delegate.cc index 1c36aee..3a2f6e4 100644 --- a/components/download/internal/common/download_item_impl_delegate.cc +++ b/components/download/internal/common/download_item_impl_delegate.cc
@@ -37,7 +37,7 @@ std::move(callback).Run( target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, - DownloadItem::MixedContentStatus::UNKNOWN, target_path, + DownloadItem::MixedContentStatus::UNKNOWN, target_path, base::FilePath(), absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); }
diff --git a/components/download/internal/common/download_item_impl_unittest.cc b/components/download/internal/common/download_item_impl_unittest.cc index 0eae1df..0420d31 100644 --- a/components/download/internal/common/download_item_impl_unittest.cc +++ b/components/download/internal/common/download_item_impl_unittest.cc
@@ -40,10 +40,6 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#if BUILDFLAG(IS_ANDROID) -#include "components/download/internal/common/android/download_collection_bridge.h" -#endif // BUILDFLAG(IS_ANDROID) - using ::testing::_; using ::testing::ByMove; using ::testing::DoAll; @@ -376,7 +372,7 @@ std::move(callback).Run( target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, danger_type, DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path, - download_schedule, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), download_schedule, DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); } @@ -385,28 +381,12 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner, const base::FilePath& new_file_path, DownloadInterruptReason reason) { - bool use_download_collection = false; -#if BUILDFLAG(IS_ANDROID) - if (DownloadCollectionBridge::ShouldPublishDownload(new_file_path)) { - use_download_collection = true; - EXPECT_CALL(*download_file, RenameToIntermediateUri(_, _, _, _, _, _)) - .WillOnce(WithArg<5>([task_runner, new_file_path, reason]( - DownloadFile::RenameCompletionCallback cb) { - task_runner->PostTask( - FROM_HERE, - base::BindOnce(std::move(cb), reason, new_file_path)); - })); - } -#endif - if (!use_download_collection) { - EXPECT_CALL(*download_file, RenameAndUniquify(_, _)) - .WillOnce(WithArg<1>([task_runner, new_file_path, reason]( - DownloadFile::RenameCompletionCallback cb) { - task_runner->PostTask( - FROM_HERE, - base::BindOnce(std::move(cb), reason, new_file_path)); - })); - } + EXPECT_CALL(*download_file, RenameAndUniquify(_, _)) + .WillOnce(WithArg<1>([task_runner, new_file_path, reason]( + DownloadFile::RenameCompletionCallback cb) { + task_runner->PostTask( + FROM_HERE, base::BindOnce(std::move(cb), reason, new_file_path)); + })); } void DoDestinationComplete(DownloadItemImpl* item, @@ -653,7 +633,8 @@ target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path, - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); EXPECT_FALSE(observer.CheckAndResetDownloadUpdated()); task_environment_.RunUntilIdle(); EXPECT_TRUE(observer.CheckAndResetDownloadUpdated()); @@ -942,7 +923,8 @@ target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path, - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); // Use a continuable interrupt. @@ -1035,7 +1017,7 @@ DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, - base::FilePath(kDummyIntermediatePath), + base::FilePath(kDummyIntermediatePath), base::FilePath(), absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); @@ -1238,7 +1220,8 @@ target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path, - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); EXPECT_EQ(FILE_PATH_LITERAL("foo.bar"), item->GetFileNameToReportUser().value()); @@ -1291,7 +1274,7 @@ DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, - base::FilePath(kDummyIntermediatePath), + base::FilePath(kDummyIntermediatePath), base::FilePath(), absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); @@ -1335,7 +1318,8 @@ .Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, target_path, - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); // Interrupt reason carried in create info should be recorded. @@ -1366,7 +1350,8 @@ final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path, - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); // All the callbacks should have happened by now. ::testing::Mock::VerifyAndClearExpectations(download_file); @@ -1416,7 +1401,8 @@ final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path, - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); // All the callbacks should have happened by now. ::testing::Mock::VerifyAndClearExpectations(download_file); @@ -1481,7 +1467,8 @@ final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path, - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); // All the callbacks should have happened by now. ::testing::Mock::VerifyAndClearExpectations(download_file); @@ -1527,7 +1514,8 @@ final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path, - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); // All the callbacks should have happened by now. ::testing::Mock::VerifyAndClearExpectations(download_file); @@ -1568,7 +1556,8 @@ final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path, - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); // All the callbacks should have happened by now. ::testing::Mock::VerifyAndClearExpectations(download_file); @@ -1611,7 +1600,7 @@ DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, base::FilePath(FILE_PATH_LITERAL("bar")), - absl::nullopt /*download_schedule*/, + base::FilePath(), absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); EXPECT_EQ(DownloadItem::CANCELLED, item->GetState()); } @@ -1626,7 +1615,8 @@ base::FilePath(), DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, base::FilePath(), - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); EXPECT_EQ(DownloadItem::CANCELLED, item->GetState()); } @@ -1637,12 +1627,12 @@ base::FilePath target_path(FILE_PATH_LITERAL("/foo/bar")); EXPECT_CALL(*download_file, Cancel()); - std::move(callback).Run(target_path, - DownloadItem::TARGET_DISPOSITION_OVERWRITE, - DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, - DownloadItem::MixedContentStatus::UNKNOWN, - target_path, absl::nullopt /*download_schedule*/, - DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE); + std::move(callback).Run( + target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DownloadItem::MixedContentStatus::UNKNOWN, target_path, base::FilePath(), + absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE); EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE, item->GetLastReason()); @@ -2421,19 +2411,8 @@ DownloadFile::RenameCompletionCallback GetRenameCompletionCallback( MockDownloadFile* download_file) { DownloadFile::RenameCompletionCallback intermediate_rename_callback; - bool use_download_collection = false; -#if BUILDFLAG(IS_ANDROID) - if (DownloadCollectionBridge::ShouldPublishDownload( - base::FilePath(kDummyIntermediatePath))) { - use_download_collection = true; - EXPECT_CALL(*download_file, RenameToIntermediateUri(_, _, _, _, _, _)) - .WillOnce(MoveArg<5>(&intermediate_rename_callback)); - } -#endif - if (!use_download_collection) { - EXPECT_CALL(*download_file, RenameAndUniquify(_, _)) - .WillOnce(MoveArg<1>(&intermediate_rename_callback)); - } + EXPECT_CALL(*download_file, RenameAndUniquify(_, _)) + .WillOnce(MoveArg<1>(&intermediate_rename_callback)); return intermediate_rename_callback; } @@ -2521,7 +2500,8 @@ .Run(base::FilePath(), DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, base::FilePath(), - absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); + base::FilePath(), absl::nullopt /*download_schedule*/, + DOWNLOAD_INTERRUPT_REASON_NONE); EXPECT_EQ(DownloadItem::CANCELLED, item_->GetState()); EXPECT_TRUE(canceled()); task_environment_.RunUntilIdle(); @@ -2570,7 +2550,7 @@ DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, - base::FilePath(kDummyIntermediatePath), + base::FilePath(kDummyIntermediatePath), base::FilePath(), absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle(); @@ -2635,7 +2615,7 @@ DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, DownloadItem::MixedContentStatus::UNKNOWN, - base::FilePath(kDummyIntermediatePath), + base::FilePath(kDummyIntermediatePath), base::FilePath(), absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE); task_environment_.RunUntilIdle();
diff --git a/components/download/internal/common/download_utils.cc b/components/download/internal/common/download_utils.cc index e5eb9aa..21c34f13 100644 --- a/components/download/internal/common/download_utils.cc +++ b/components/download/internal/common/download_utils.cc
@@ -131,6 +131,43 @@ headers->SetHeader(net::HttpRequestHeaders::kRange, range_header); } +#if BUILDFLAG(IS_ANDROID) +struct CreateIntermediateUriResult { + public: + CreateIntermediateUriResult(const base::FilePath& content_uri, + const base::FilePath& file_name) + : content_uri(content_uri), file_name(file_name) {} + + base::FilePath content_uri; + base::FilePath file_name; +}; + +CreateIntermediateUriResult CreateIntermediateUri( + const GURL& original_url, + const GURL& referrer_url, + const base::FilePath& current_path, + const base::FilePath& suggested_name, + const std::string& mime_type) { + base::FilePath content_path = + current_path.IsContentUri() && base::ContentUriExists(current_path) + ? current_path + : DownloadCollectionBridge::CreateIntermediateUriForPublish( + original_url, referrer_url, suggested_name, mime_type); + base::FilePath file_name; + if (!content_path.empty()) { + file_name = DownloadCollectionBridge::GetDisplayName(content_path); + } + if (file_name.empty()) + file_name = suggested_name; + return CreateIntermediateUriResult(content_path, file_name); +} + +void OnInterMediateUriCreated(LocalPathCallback callback, + const CreateIntermediateUriResult& result) { + std::move(callback).Run(result.content_uri, result.file_name); +} +#endif // BUILDFLAG(IS_ANDROID) + } // namespace const uint32_t DownloadItem::kInvalidId = 0; @@ -736,4 +773,28 @@ kDefaultDownloadFileBufferSize); } +void DetermineLocalPath(DownloadItem* download, + const base::FilePath& virtual_path, + LocalPathCallback callback) { +#if BUILDFLAG(IS_ANDROID) + if ((!download->IsTransient() && + DownloadCollectionBridge::ShouldPublishDownload(virtual_path)) || + virtual_path.IsContentUri()) { + GetDownloadTaskRunner()->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&CreateIntermediateUri, + // Safe because we control download file lifetime. + download->GetOriginalUrl(), download->GetReferrerUrl(), + virtual_path, + virtual_path.IsContentUri() + ? download->GetFileNameToReportUser() + : virtual_path.BaseName(), + download->GetMimeType()), + base::BindOnce(&OnInterMediateUriCreated, std::move(callback))); + return; + } +#endif // BUILDFLAG(IS_ANDROID) + std::move(callback).Run(virtual_path, base::FilePath()); +} + } // namespace download
diff --git a/components/download/internal/common/in_progress_download_manager.cc b/components/download/internal/common/in_progress_download_manager.cc index 58daaba8..33440613 100644 --- a/components/download/internal/common/in_progress_download_manager.cc +++ b/components/download/internal/common/in_progress_download_manager.cc
@@ -184,7 +184,8 @@ : BackgroudTargetDeterminationResultTypes::kSuccess); std::move(callback).Run( target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, danger_type, - mixed_content_status, intermediate_path, std::move(download_schedule), + mixed_content_status, intermediate_path, base::FilePath(), + std::move(download_schedule), intermediate_path.empty() ? DOWNLOAD_INTERRUPT_REASON_FILE_FAILED : DOWNLOAD_INTERRUPT_REASON_NONE); } @@ -404,7 +405,7 @@ std::move(callback).Run( target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, download->GetDangerType(), download->GetMixedContentStatus(), - target_path, download->GetDownloadSchedule(), + target_path, base::FilePath(), download->GetDownloadSchedule(), DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); RecordBackgroundTargetDeterminationResult( BackgroudTargetDeterminationResultTypes::kTargetPathMissing); @@ -417,7 +418,7 @@ std::move(callback).Run( target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, download->GetDangerType(), download->GetMixedContentStatus(), - target_path, download->GetDownloadSchedule(), + target_path, base::FilePath(), download->GetDownloadSchedule(), DOWNLOAD_INTERRUPT_REASON_NONE); RecordBackgroundTargetDeterminationResult( BackgroudTargetDeterminationResultTypes::kSuccess); @@ -441,7 +442,7 @@ std::move(callback).Run( target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, download->GetDangerType(), download->GetMixedContentStatus(), - intermediate_path, download->GetDownloadSchedule(), + intermediate_path, base::FilePath(), download->GetDownloadSchedule(), DOWNLOAD_INTERRUPT_REASON_NONE); #endif // BUILDFLAG(IS_ANDROID) }
diff --git a/components/download/public/background_service/download_metadata.cc b/components/download/public/background_service/download_metadata.cc index 100a49d..ba9c2add 100644 --- a/components/download/public/background_service/download_metadata.cc +++ b/components/download/public/background_service/download_metadata.cc
@@ -43,7 +43,7 @@ url_chain == other.url_chain && AreResponseHeadersEqual(response_headers.get(), other.response_headers.get()) && - hash256 == other.hash256; + hash256 == other.hash256 && custom_data == other.custom_data; } DownloadMetaData::DownloadMetaData() : current_size(0u), paused(false) {}
diff --git a/components/download/public/common/download_file.h b/components/download/public/common/download_file.h index e6cb459e..05196e4 100644 --- a/components/download/public/common/download_file.h +++ b/components/download/public/common/download_file.h
@@ -115,23 +115,9 @@ virtual void Resume() = 0; #if BUILDFLAG(IS_ANDROID) - // Renames the download file to an intermediate URI. If current_path is a - // content URI, it will be used for the renaming. Otherwise, A new - // intermediate URI will be created to write the download file. Once - // completes, |callback| is called with a content URI to be written into. - virtual void RenameToIntermediateUri(const GURL& original_url, - const GURL& referrer_url, - const base::FilePath& file_name, - const std::string& mime_type, - const base::FilePath& current_path, - RenameCompletionCallback callback) = 0; - // Publishes the download to public. Once completes, |callback| is called with // the final content URI. virtual void PublishDownload(RenameCompletionCallback callback) = 0; - - // Returns the suggested file path from the system. - virtual base::FilePath GetDisplayName() = 0; #endif // BUILDFLAG(IS_ANDROID) };
diff --git a/components/download/public/common/download_file_impl.h b/components/download/public/common/download_file_impl.h index d793270..a43cc5721 100644 --- a/components/download/public/common/download_file_impl.h +++ b/components/download/public/common/download_file_impl.h
@@ -80,14 +80,7 @@ void Resume() override; #if BUILDFLAG(IS_ANDROID) - void RenameToIntermediateUri(const GURL& original_url, - const GURL& referrer_url, - const base::FilePath& file_name, - const std::string& mime_type, - const base::FilePath& current_path, - RenameCompletionCallback callback) override; void PublishDownload(RenameCompletionCallback callback) override; - base::FilePath GetDisplayName() override; #endif // BUILDFLAG(IS_ANDROID) // Wrapper of a ByteStreamReader or ScopedDataPipeConsumerHandle, and the meta @@ -387,10 +380,6 @@ // TaskRunner this object lives on after initialization. scoped_refptr<base::SequencedTaskRunner> task_runner_; -#if BUILDFLAG(IS_ANDROID) - base::FilePath display_name_; -#endif // BUILDFLAG(IS_ANDROID) - SEQUENCE_CHECKER(sequence_checker_); base::WeakPtr<DownloadDestinationObserver> observer_;
diff --git a/components/download/public/common/download_item_impl.h b/components/download/public/common/download_item_impl.h index 88a52cb..d82d11f43 100644 --- a/components/download/public/common/download_item_impl.h +++ b/components/download/public/common/download_item_impl.h
@@ -596,12 +596,15 @@ // be used (see TargetDisposition). |danger_type| is the danger level of // |target_path| as determined by the caller. |intermediate_path| is the path // to use to store the download until OnDownloadCompleting() is called. + // If |display_name| is empty, the download should keep its current display + // name. Otherwise, the new display name should be used. virtual void OnDownloadTargetDetermined( const base::FilePath& target_path, TargetDisposition disposition, DownloadDangerType danger_type, MixedContentStatus mixed_content_status, const base::FilePath& intermediate_path, + const base::FilePath& display_name, absl::optional<DownloadSchedule> download_schedule, DownloadInterruptReason interrupt_reason);
diff --git a/components/download/public/common/download_item_impl_delegate.h b/components/download/public/common/download_item_impl_delegate.h index 87da60e..37df534 100644 --- a/components/download/public/common/download_item_impl_delegate.h +++ b/components/download/public/common/download_item_impl_delegate.h
@@ -52,6 +52,7 @@ DownloadDangerType danger_type, DownloadItem::MixedContentStatus mixed_content_status, const base::FilePath& intermediate_path, + const base::FilePath& display_name, absl::optional<DownloadSchedule> download_schedule, DownloadInterruptReason interrupt_reason)>; // Request determination of the download target from the delegate.
diff --git a/components/download/public/common/download_utils.h b/components/download/public/common/download_utils.h index 454274b..7d8e49f 100644 --- a/components/download/public/common/download_utils.h +++ b/components/download/public/common/download_utils.h
@@ -106,6 +106,26 @@ RenameDownloadedFile(const base::FilePath& from_path, const base::FilePath& display_name); +// Callback to be invoked when DetermineLocalPath() completes. The argument +// |file_path| should be the determined local path. It should be non-empty +// on success. +// On Android, |file_path| could be a content Uri (e.g. content://media/1234). +// In such cases, |file_name| is provided for displaying the file to the user +// (e.g. test.apk). If |file_path| is not a content Uri, file name could +// be empty and should be ignored. +using LocalPathCallback = + base::OnceCallback<void(const base::FilePath& file_path, + const base::FilePath& file_name)>; + +// If |virtual_path| is not a local path, should return a possibly temporary +// local path to use for storing the downloaded file. If |virtual_path| is +// already local, then it should return the same path. |callback| should be +// invoked to return the path. +COMPONENTS_DOWNLOAD_EXPORT +void DetermineLocalPath(DownloadItem* download, + const base::FilePath& virtual_path, + LocalPathCallback callback); + // Finch parameter key value for number of bytes used for content validation // during resumption. constexpr char kDownloadContentValidationLengthFinchKey[] =
diff --git a/components/download/public/common/mock_download_item_impl.h b/components/download/public/common/mock_download_item_impl.h index 12b1e88..f7f38a3 100644 --- a/components/download/public/common/mock_download_item_impl.h +++ b/components/download/public/common/mock_download_item_impl.h
@@ -27,12 +27,13 @@ explicit MockDownloadItemImpl(DownloadItemImplDelegate* delegate); ~MockDownloadItemImpl() override; - MOCK_METHOD7(OnDownloadTargetDetermined, + MOCK_METHOD8(OnDownloadTargetDetermined, void(const base::FilePath&, TargetDisposition, DownloadDangerType, MixedContentStatus, const base::FilePath&, + const base::FilePath&, absl::optional<DownloadSchedule>, DownloadInterruptReason)); MOCK_METHOD1(AddObserver, void(DownloadItem::Observer*));
diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc index 3b579b81..b2b7711 100644 --- a/components/guest_view/browser/guest_view_base.cc +++ b/components/guest_view/browser/guest_view_base.cc
@@ -72,17 +72,10 @@ Destroy(); } - void DidFinishNavigation( - content::NavigationHandle* navigation_handle) override { + void PrimaryPageChanged(content::Page& page) override { // TODO(1206312, 1205920): It is incorrect to assume that a navigation will // destroy the embedder. // If the embedder navigates to a different page then destroy the guest. - if (!navigation_handle->IsInMainFrame() || - !navigation_handle->HasCommitted() || - navigation_handle->IsSameDocument()) { - return; - } - Destroy(); }
diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h index b23f5e3..6f0a354db 100644 --- a/components/history/core/browser/history_types.h +++ b/components/history/core/browser/history_types.h
@@ -873,6 +873,12 @@ // A suitable label for the cluster. Will be nullopt if no suitable label // could be determined. absl::optional<std::u16string> label; + + // A floating point score that's positive if the cluster matches the user's + // search query, and zero otherwise. This score changes depending on the + // entered search query, so this should never be persisted. It's a + // UI-state-specific score that's convenient to buffer here. + float search_match_score = 0.0; }; // A minimal representation of `Cluster` used when retrieving them from
diff --git a/components/history_clusters/core/config.cc b/components/history_clusters/core/config.cc index 0872340d..1015803c 100644 --- a/components/history_clusters/core/config.cc +++ b/components/history_clusters/core/config.cc
@@ -59,6 +59,11 @@ internal::kJourneys, "JourneysRescoreVisitsWithinClustersForQuery", rescore_visits_within_clusters_for_query); + sort_clusters_within_batch_for_query = + base::GetFieldTrialParamByFeatureAsBool( + internal::kJourneys, "JourneysSortClustersWithinBatchForQuery", + sort_clusters_within_batch_for_query); + alternate_omnibox_action_text = base::GetFieldTrialParamByFeatureAsBool( internal::kJourneys, "JourneysAlternateOmniboxActionText", alternate_omnibox_action_text);
diff --git a/components/history_clusters/core/config.h b/components/history_clusters/core/config.h index f754f58e..e24d7888 100644 --- a/components/history_clusters/core/config.h +++ b/components/history_clusters/core/config.h
@@ -57,6 +57,11 @@ // visits within a cluster to account for whether or not that visit matches. bool rescore_visits_within_clusters_for_query = true; + // If enabled, sorts clusters WITHIN a single batch from most search matches + // to least search matches. The batches themselves will still be ordered + // reverse chronologically, but the clusters within batches will be resorted. + bool sort_clusters_within_batch_for_query = false; + // If enabled, changes the History Clusters omnibox action text to be: // "Resume your research" instead of "Resume your journey". bool alternate_omnibox_action_text = true;
diff --git a/components/history_clusters/core/history_clusters_util.cc b/components/history_clusters/core/history_clusters_util.cc index 5a3309bf..1bff3dcf 100644 --- a/components/history_clusters/core/history_clusters_util.cc +++ b/components/history_clusters/core/history_clusters_util.cc
@@ -38,12 +38,13 @@ // Flags any elements within `cluster_visits` that match `find_nodes`. The // matching is deliberately meant to closely mirror the History implementation. -// Returns true if any elements match, and returns false if none of them do. -bool FlagMatchingVisits(const query_parser::QueryNodeVector& find_nodes, - std::vector<history::ClusterVisit>* cluster_visits) { +// Returns the total score of matching visits, and returns 0 if no visits match. +float ComputeTotalMatchScore( + const query_parser::QueryNodeVector& find_nodes, + std::vector<history::ClusterVisit>* cluster_visits) { DCHECK(cluster_visits); - bool any_visits_match = false; + float total_matching_visit_score = 0.0; for (auto& visit : *cluster_visits) { query_parser::QueryWordVector find_in_words; @@ -67,11 +68,12 @@ if (query_parser::QueryParser::DoesQueryMatch(find_in_words, find_nodes)) { visit.matches_search_query = true; - any_visits_match = true; + DCHECK_GE(visit.score, 0); + total_matching_visit_score += visit.score; } } - return any_visits_match; + return total_matching_visit_score; } // Re-scores and re-sorts `cluster_visits` so that all visits that match the @@ -160,19 +162,34 @@ std::swap(all_clusters, *clusters); for (auto& cluster : all_clusters) { - bool any_visits_match = FlagMatchingVisits(find_nodes, &cluster.visits); - if (any_visits_match && + const float total_matching_visit_score = + ComputeTotalMatchScore(find_nodes, &cluster.visits); + DCHECK_GE(total_matching_visit_score, 0); + if (total_matching_visit_score > 0 && GetConfig().rescore_visits_within_clusters_for_query) { PromoteMatchingVisitsAboveNonMatchingVisits(&cluster.visits); } - // If any of the visits match, or if any of the `cluster.keywords` match, - // move this cluster into the list of surviving clusters. - if (any_visits_match || - DoesQueryMatchClusterKeywords(find_nodes, cluster.keywords)) { + cluster.search_match_score = total_matching_visit_score; + if (DoesQueryMatchClusterKeywords(find_nodes, cluster.keywords)) { + // Arbitrarily chosen that cluster keyword matches are worth three points. + // TODO(crbug.com/1307071): Use relevancy score for each cluster keyword + // once support for that is added to the backend. + cluster.search_match_score += 3.0; + } + + if (cluster.search_match_score > 0) { + // Move the matching clusters into the final list. clusters->push_back(std::move(cluster)); } } + + if (GetConfig().sort_clusters_within_batch_for_query) { + base::ranges::stable_sort(*clusters, [](auto& c1, auto& c2) { + // Use c1 > c2 to get higher scored clusters BEFORE lower scored clusters. + return c1.search_match_score > c2.search_match_score; + }); + } } void CullNonProminentOrDuplicateClusters(
diff --git a/components/history_clusters/core/history_clusters_util_unittest.cc b/components/history_clusters/core/history_clusters_util_unittest.cc index d03776b..fa11c78 100644 --- a/components/history_clusters/core/history_clusters_util_unittest.cc +++ b/components/history_clusters/core/history_clusters_util_unittest.cc
@@ -5,6 +5,7 @@ #include "components/history_clusters/core/history_clusters_util.h" #include "base/strings/stringprintf.h" +#include "components/history_clusters/core/config.h" #include "components/history_clusters/core/history_clusters_service_test_api.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -147,5 +148,70 @@ } } +TEST(HistoryClustersUtilTest, SortClustersWithinBatchForQuery) { + std::vector<history::Cluster> all_clusters; + all_clusters.push_back( + history::Cluster(1, + { + GetHardcodedClusterVisit(1), + GetHardcodedClusterVisit(2), + }, + {u"apples", u"Red Oranges"}, + /*should_show_on_prominent_ui_surfaces=*/false)); + all_clusters.push_back( + history::Cluster(2, + { + GetHardcodedClusterVisit(1), + }, + {u"search"}, + /*should_show_on_prominent_ui_surfaces=*/false)); + + // When the flag is off, leave the initial ordering alone. + { + Config config; + config.sort_clusters_within_batch_for_query = false; + SetConfigForTesting(config); + + std::vector clusters = all_clusters; + ApplySearchQuery("search", &clusters); + ASSERT_EQ(clusters.size(), 2U); + EXPECT_EQ(clusters[0].cluster_id, 1); + EXPECT_EQ(clusters[1].cluster_id, 2); + EXPECT_FLOAT_EQ(clusters[0].search_match_score, 0.5); + EXPECT_FLOAT_EQ(clusters[1].search_match_score, 3.5); + } + + // When the flag is on, second cluster should be sorted above the first one, + // because the second one has a match on both the keyword and visit. + { + Config config; + config.sort_clusters_within_batch_for_query = true; + SetConfigForTesting(config); + + std::vector clusters = all_clusters; + ApplySearchQuery("search", &clusters); + ASSERT_EQ(clusters.size(), 2U); + EXPECT_EQ(clusters[0].cluster_id, 2); + EXPECT_EQ(clusters[1].cluster_id, 1); + EXPECT_FLOAT_EQ(clusters[0].search_match_score, 3.5); + EXPECT_FLOAT_EQ(clusters[1].search_match_score, 0.5); + } + + // With flag on, if both scores are equal, the ordering should be preserved. + { + Config config; + config.sort_clusters_within_batch_for_query = true; + SetConfigForTesting(config); + + std::vector clusters = all_clusters; + ApplySearchQuery("google", &clusters); + ASSERT_EQ(clusters.size(), 2U); + EXPECT_EQ(clusters[0].cluster_id, 1); + EXPECT_EQ(clusters[1].cluster_id, 2); + EXPECT_FLOAT_EQ(clusters[0].search_match_score, 0.5); + EXPECT_FLOAT_EQ(clusters[1].search_match_score, 0.5); + } +} + } // namespace } // namespace history_clusters
diff --git a/components/invalidation/impl/fake_invalidation_handler.cc b/components/invalidation/impl/fake_invalidation_handler.cc index eb9f68b8..3c8cedb9 100644 --- a/components/invalidation/impl/fake_invalidation_handler.cc +++ b/components/invalidation/impl/fake_invalidation_handler.cc
@@ -6,15 +6,8 @@ namespace invalidation { -FakeInvalidationHandler::FakeInvalidationHandler() - : state_(DEFAULT_INVALIDATION_ERROR), - invalidation_count_(0), - owner_name_("Fake") {} - FakeInvalidationHandler::FakeInvalidationHandler(const std::string& owner_name) - : FakeInvalidationHandler() { - owner_name_ = owner_name; -} + : owner_name_(owner_name) {} FakeInvalidationHandler::~FakeInvalidationHandler() = default;
diff --git a/components/invalidation/impl/fake_invalidation_handler.h b/components/invalidation/impl/fake_invalidation_handler.h index 001abd2c..6c7c813 100644 --- a/components/invalidation/impl/fake_invalidation_handler.h +++ b/components/invalidation/impl/fake_invalidation_handler.h
@@ -14,7 +14,6 @@ class FakeInvalidationHandler : public InvalidationHandler { public: - FakeInvalidationHandler(); explicit FakeInvalidationHandler(const std::string& owner); FakeInvalidationHandler(const FakeInvalidationHandler& other) = delete; FakeInvalidationHandler& operator=(const FakeInvalidationHandler& other) = @@ -35,9 +34,9 @@ void OnInvalidatorClientIdChange(const std::string& client_id) override; private: - InvalidatorState state_; + InvalidatorState state_ = DEFAULT_INVALIDATION_ERROR; TopicInvalidationMap last_invalidation_map_; - int invalidation_count_; + int invalidation_count_ = 0; std::string owner_name_; std::string client_id_; };
diff --git a/components/invalidation/impl/fcm_invalidation_service_unittest.cc b/components/invalidation/impl/fcm_invalidation_service_unittest.cc index 1f53441c..9ea80e6 100644 --- a/components/invalidation/impl/fcm_invalidation_service_unittest.cc +++ b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
@@ -221,7 +221,7 @@ ASSERT_TRUE(invalidation_service->GetInvalidatorClientId().empty()); // Register a handler *before* initializing the invalidation service. - FakeInvalidationHandler handler; + FakeInvalidationHandler handler("owner_1"); invalidation_service->RegisterInvalidationHandler(&handler); // Because the invalidation service hasn't been initialized, the client ID is @@ -252,7 +252,7 @@ // Another handler that gets registered should immediately be informed of the // client ID. - FakeInvalidationHandler handler2; + FakeInvalidationHandler handler2(/*owner=*/"owner_2"); invalidation_service->RegisterInvalidationHandler(&handler2); EXPECT_EQ(handler2.GetInvalidatorClientId(), "FreshInstanceID");
diff --git a/components/invalidation/impl/invalidation_logger.cc b/components/invalidation/impl/invalidation_logger.cc index 2662273..4ddbd82 100644 --- a/components/invalidation/impl/invalidation_logger.cc +++ b/components/invalidation/impl/invalidation_logger.cc
@@ -19,6 +19,8 @@ InvalidationLogger::~InvalidationLogger() = default; void InvalidationLogger::OnRegistration(const std::string& registrar_name) { + DCHECK(registered_handlers_.find(registrar_name) == + registered_handlers_.end()); registered_handlers_.insert(registrar_name); EmitRegisteredHandlers(); }
diff --git a/components/invalidation/impl/invalidation_logger.h b/components/invalidation/impl/invalidation_logger.h index 1d979451..41a9934 100644 --- a/components/invalidation/impl/invalidation_logger.h +++ b/components/invalidation/impl/invalidation_logger.h
@@ -90,9 +90,7 @@ // The name of all invalidatorHandler registered (note that this is not // necessarily the same as the keys of latest_topics_, because they might // have not registered any Topic). - // TODO(crbug.com/1049591): it should be std::set, once handlers names are - // unique. - std::multiset<std::string> registered_handlers_; + std::set<std::string> registered_handlers_; }; } // namespace invalidation
diff --git a/components/invalidation/impl/invalidation_logger_observer.h b/components/invalidation/impl/invalidation_logger_observer.h index 1767b7e..56a8b2c 100644 --- a/components/invalidation/impl/invalidation_logger_observer.h +++ b/components/invalidation/impl/invalidation_logger_observer.h
@@ -27,7 +27,7 @@ class InvalidationLoggerObserver { public: virtual void OnRegistrationChange( - const std::multiset<std::string>& registered_handlers) = 0; + const std::set<std::string>& registered_handlers) = 0; virtual void OnStateChange(const InvalidatorState& new_state, const base::Time& last_change_timestamp) = 0; virtual void OnUpdatedTopics(const std::string& handler_name,
diff --git a/components/invalidation/impl/invalidation_logger_unittest.cc b/components/invalidation/impl/invalidation_logger_unittest.cc index b531041..3c16a72 100644 --- a/components/invalidation/impl/invalidation_logger_unittest.cc +++ b/components/invalidation/impl/invalidation_logger_unittest.cc
@@ -23,11 +23,10 @@ invalidation_received = false; detailed_status_received = false; updated_topics_replicated = std::map<std::string, TopicCountMap>(); - registered_handlers = std::multiset<std::string>(); + registered_handlers = std::set<std::string>(); } - void OnRegistrationChange( - const std::multiset<std::string>& handlers) override { + void OnRegistrationChange(const std::set<std::string>& handlers) override { registered_handlers = handlers; registration_change_received = true; } @@ -62,7 +61,7 @@ bool invalidation_received; bool detailed_status_received; std::map<std::string, TopicCountMap> updated_topics_replicated; - std::multiset<std::string> registered_handlers; + std::set<std::string> registered_handlers; }; // Test that the callbacks are actually being called when observers are @@ -263,22 +262,22 @@ log.RegisterObserver(&observer_test); log.OnRegistration(std::string("FakeHandler1")); - std::multiset<std::string> test_multiset; - test_multiset.insert("FakeHandler1"); + std::set<std::string> test_set; + test_set.insert("FakeHandler1"); EXPECT_TRUE(observer_test.registration_change_received); - EXPECT_EQ(observer_test.registered_handlers, test_multiset); + EXPECT_EQ(observer_test.registered_handlers, test_set); observer_test.ResetStates(); log.OnRegistration(std::string("FakeHandler2")); - test_multiset.insert("FakeHandler2"); + test_set.insert("FakeHandler2"); EXPECT_TRUE(observer_test.registration_change_received); - EXPECT_EQ(observer_test.registered_handlers, test_multiset); + EXPECT_EQ(observer_test.registered_handlers, test_set); observer_test.ResetStates(); log.OnUnregistration(std::string("FakeHandler2")); - test_multiset.erase("FakeHandler2"); + test_set.erase("FakeHandler2"); EXPECT_TRUE(observer_test.registration_change_received); - EXPECT_EQ(observer_test.registered_handlers, test_multiset); + EXPECT_EQ(observer_test.registered_handlers, test_set); log.UnregisterObserver(&observer_test); }
diff --git a/components/invalidation/impl/invalidation_service_test_template.cc b/components/invalidation/impl/invalidation_service_test_template.cc index 65eda862..ef0c20b 100644 --- a/components/invalidation/impl/invalidation_service_test_template.cc +++ b/components/invalidation/impl/invalidation_service_test_template.cc
@@ -7,8 +7,10 @@ namespace invalidation { BoundFakeInvalidationHandler::BoundFakeInvalidationHandler( - const InvalidationService& invalidator) - : invalidator_(invalidator), + const InvalidationService& invalidator, + const std::string& owner) + : FakeInvalidationHandler(owner), + invalidator_(invalidator), last_retrieved_state_(DEFAULT_INVALIDATION_ERROR) {} BoundFakeInvalidationHandler::~BoundFakeInvalidationHandler() = default;
diff --git a/components/invalidation/impl/invalidation_service_test_template.h b/components/invalidation/impl/invalidation_service_test_template.h index 9df58226..64d8921 100644 --- a/components/invalidation/impl/invalidation_service_test_template.h +++ b/components/invalidation/impl/invalidation_service_test_template.h
@@ -111,7 +111,7 @@ InvalidationService* const invalidator = this->CreateAndInitializeInvalidationService(); - FakeInvalidationHandler handler; + FakeInvalidationHandler handler("owner"); invalidator->RegisterInvalidationHandler(&handler); @@ -175,10 +175,10 @@ InvalidationService* const invalidator = this->CreateAndInitializeInvalidationService(); - FakeInvalidationHandler handler1; - FakeInvalidationHandler handler2; - FakeInvalidationHandler handler3; - FakeInvalidationHandler handler4; + FakeInvalidationHandler handler1(/*owner=*/"owner_1"); + FakeInvalidationHandler handler2(/*owner=*/"owner_2"); + FakeInvalidationHandler handler3(/*owner=*/"owner_3"); + FakeInvalidationHandler handler4(/*owner=*/"owner_4"); invalidator->RegisterInvalidationHandler(&handler1); invalidator->RegisterInvalidationHandler(&handler2); @@ -256,8 +256,8 @@ InvalidationService* const invalidator = this->CreateAndInitializeInvalidationService(); - FakeInvalidationHandler handler1; - FakeInvalidationHandler handler2; + FakeInvalidationHandler handler1(/*owner=*/"owner_1"); + FakeInvalidationHandler handler2(/*owner=*/"owner_2"); invalidator->RegisterInvalidationHandler(&handler1); invalidator->RegisterInvalidationHandler(&handler2); @@ -279,10 +279,10 @@ InvalidationService* const invalidator = this->CreateAndInitializeInvalidationService(); - FakeInvalidationHandler handler1; + FakeInvalidationHandler handler1(/*owner=*/"owner_1"); // Control observer. - FakeInvalidationHandler handler2; + FakeInvalidationHandler handler2(/*owner=*/"owner_2"); invalidator->RegisterInvalidationHandler(&handler1); invalidator->RegisterInvalidationHandler(&handler2); @@ -331,7 +331,8 @@ // the bound InvalidationService. class BoundFakeInvalidationHandler : public FakeInvalidationHandler { public: - explicit BoundFakeInvalidationHandler(const InvalidationService& invalidator); + BoundFakeInvalidationHandler(const InvalidationService& invalidator, + const std::string& owner); BoundFakeInvalidationHandler(const BoundFakeInvalidationHandler&) = delete; BoundFakeInvalidationHandler& operator=(const BoundFakeInvalidationHandler&) = @@ -356,7 +357,7 @@ InvalidationService* const invalidator = this->CreateAndInitializeInvalidationService(); - BoundFakeInvalidationHandler handler(*invalidator); + BoundFakeInvalidationHandler handler(*invalidator, "owner"); invalidator->RegisterInvalidationHandler(&handler); this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
diff --git a/components/invalidation/impl/invalidator_registrar_with_memory_unittest.cc b/components/invalidation/impl/invalidator_registrar_with_memory_unittest.cc index 55aa68fd..24b020f3 100644 --- a/components/invalidation/impl/invalidator_registrar_with_memory_unittest.cc +++ b/components/invalidation/impl/invalidator_registrar_with_memory_unittest.cc
@@ -36,7 +36,7 @@ auto invalidator = std::make_unique<InvalidatorRegistrarWithMemory>( &pref_service, "sender_id", /*migrate_old_prefs=*/false); - FakeInvalidationHandler handler; + FakeInvalidationHandler handler("owner"); invalidator->RegisterHandler(&handler); TopicInvalidationMap invalidation_map; @@ -105,10 +105,10 @@ auto invalidator = std::make_unique<InvalidatorRegistrarWithMemory>( &pref_service, "sender_id", /*migrate_old_prefs=*/false); - FakeInvalidationHandler handler1; - FakeInvalidationHandler handler2; - FakeInvalidationHandler handler3; - FakeInvalidationHandler handler4; + FakeInvalidationHandler handler1("owner_1"); + FakeInvalidationHandler handler2("owner_2"); + FakeInvalidationHandler handler3("owner_3"); + FakeInvalidationHandler handler4("owner_4"); invalidator->RegisterHandler(&handler1); invalidator->RegisterHandler(&handler2); @@ -208,10 +208,10 @@ auto invalidator = std::make_unique<InvalidatorRegistrarWithMemory>( &pref_service, "sender_id", /*migrate_old_prefs=*/false); - FakeInvalidationHandler handler1; + FakeInvalidationHandler handler1("owner_1"); // Control observer. - FakeInvalidationHandler handler2; + FakeInvalidationHandler handler2("owner_2"); invalidator->RegisterHandler(&handler1); invalidator->RegisterHandler(&handler2);
diff --git a/components/invalidation/public/invalidation_handler.h b/components/invalidation/public/invalidation_handler.h index 0ac1903..00dabd4b 100644 --- a/components/invalidation/public/invalidation_handler.h +++ b/components/invalidation/public/invalidation_handler.h
@@ -32,8 +32,6 @@ // Returned value must be unique for the handlers using the same invalidation // service. - // TODO(crbug.com/1049591): this is currently not the case for - // CloudPolicyInvalidator. virtual std::string GetOwnerName() const = 0; // Called on change of |client_id|. Client id is used to identify the
diff --git a/components/lens/lens_features.cc b/components/lens/lens_features.cc index 82b1d47d..a324c58b 100644 --- a/components/lens/lens_features.cc +++ b/components/lens/lens_features.cc
@@ -13,52 +13,42 @@ const base::Feature kLensStandalone{"LensStandalone", base::FEATURE_ENABLED_BY_DEFAULT}; -const base::Feature kLensRegionSearch{"LensRegionSearch", - base::FEATURE_ENABLED_BY_DEFAULT}; - const base::FeatureParam<bool> kRegionSearchMacCursorFix{ - &kLensRegionSearch, "region-search-mac-cursor-fix", true}; + &kLensStandalone, "region-search-mac-cursor-fix", true}; const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText1{ - &kLensRegionSearch, "use-menu-item-alt-text-1", false}; + &kLensStandalone, "use-menu-item-alt-text-1", false}; const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText2{ - &kLensRegionSearch, "use-menu-item-alt-text-2", false}; + &kLensStandalone, "use-menu-item-alt-text-2", false}; const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText3{ - &kLensRegionSearch, "use-menu-item-alt-text-3", false}; + &kLensStandalone, "use-menu-item-alt-text-3", false}; const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText4{ - &kLensRegionSearch, "use-menu-item-alt-text-4", true}; + &kLensStandalone, "use-menu-item-alt-text-4", true}; const base::FeatureParam<bool> kEnableUKMLoggingForRegionSearch{ - &kLensRegionSearch, "region-search-enable-ukm-logging", true}; + &kLensStandalone, "region-search-enable-ukm-logging", true}; const base::FeatureParam<bool> kEnableUKMLoggingForImageSearch{ &kLensStandalone, "enable-ukm-logging", true}; -const base::FeatureParam<bool> kEnableSidePanelForLensRegionSearch{ - &kLensRegionSearch, "region-search-enable-side-panel", false}; - -const base::FeatureParam<bool> kEnableSidePanelForLensImageSearch{ +const base::FeatureParam<bool> kEnableSidePanelForLens{ &kLensStandalone, "enable-side-panel", false}; constexpr base::FeatureParam<int> kMaxPixelsForRegionSearch{ - &kLensRegionSearch, "region-search-dimensions-max-pixels", 1000}; + &kLensStandalone, "region-search-dimensions-max-pixels", 1000}; constexpr base::FeatureParam<int> kMaxAreaForRegionSearch{ - &kLensRegionSearch, "region-search-dimensions-max-area", 1000000}; + &kLensStandalone, "region-search-dimensions-max-area", 1000000}; constexpr base::FeatureParam<int> kMaxPixelsForImageSearch{ &kLensStandalone, "dimensions-max-pixels", 1000}; -constexpr base::FeatureParam<std::string> kHomepageURLForImageSearch{ +constexpr base::FeatureParam<std::string> kHomepageURLForLens{ &kLensStandalone, "lens-homepage-url", "https://lens.google.com/"}; -constexpr base::FeatureParam<std::string> kHomepageURLForRegionSearch{ - &kLensRegionSearch, "region-search-lens-homepage-url", - "https://lens.google.com/"}; - bool GetEnableUKMLoggingForRegionSearch() { return kEnableUKMLoggingForRegionSearch.Get(); } @@ -79,12 +69,13 @@ return kMaxPixelsForImageSearch.Get(); } -std::string GetHomepageURLForImageSearch() { - return kHomepageURLForImageSearch.Get(); +std::string GetHomepageURLForLens() { + return kHomepageURLForLens.Get(); } -std::string GetHomepageURLForRegionSearch() { - return kHomepageURLForRegionSearch.Get(); +bool IsLensSidePanelEnabled() { + return base::FeatureList::IsEnabled(kLensStandalone) && + kEnableSidePanelForLens.Get(); } } // namespace features
diff --git a/components/lens/lens_features.h b/components/lens/lens_features.h index 5e9b072..73a7988a 100644 --- a/components/lens/lens_features.h +++ b/components/lens/lens_features.h
@@ -41,11 +41,8 @@ // Enables UKM logging for the LensStandalone feature. extern const base::FeatureParam<bool> kEnableUKMLoggingForImageSearch; -// Enables the side panel for Lens Region Search. -extern const base::FeatureParam<bool> kEnableSidePanelForLensRegionSearch; - -// Enables the side panel for Lens Image Search. -extern const base::FeatureParam<bool> kEnableSidePanelForLensImageSearch; +// Enables the side panel for Lens features on Chrome where supported. +extern const base::FeatureParam<bool> kEnableSidePanelForLens; // Returns whether to enable UKM logging for Lens Region Search feature. extern bool GetEnableUKMLoggingForRegionSearch(); @@ -64,10 +61,10 @@ extern int GetMaxPixelsForImageSearch(); // The URL for the Lens home page. -extern std::string GetHomepageURLForImageSearch(); +extern std::string GetHomepageURLForLens(); -// The URL for the Lens home page as defined by Lens Region Search feature. -extern std::string GetHomepageURLForRegionSearch(); +// Returns whether the Lens side panel is enabled. +extern bool IsLensSidePanelEnabled(); } // namespace features } // namespace lens
diff --git a/components/mirroring/service/mirroring_features.cc b/components/mirroring/service/mirroring_features.cc index 0d6bfda..28df046 100644 --- a/components/mirroring/service/mirroring_features.cc +++ b/components/mirroring/service/mirroring_features.cc
@@ -25,6 +25,18 @@ const base::Feature kCastStreamingVp9{"CastStreamingVp9", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls whether the allow list (legacy behavior) or blocklist is used to +// determine whether remoting capabilities should be queried for as part of +// configuring a mirroring session. +const base::Feature kCastUseBlocklistForRemotingQuery{ + "CastUseBlocklistForRemotingQuery", base::FEATURE_DISABLED_BY_DEFAULT}; + +// Enables querying for remoting capabilities against ALL devices, as opposed to +// just those controlled by the allow or blocklist. When set, this flag takes +// precedence over the above flag. +const base::Feature kCastForceEnableRemotingQuery{ + "CastForceEnableRemotingQuery", base::FEATURE_DISABLED_BY_DEFAULT}; + bool IsCastStreamingAV1Enabled() { #if BUILDFLAG(ENABLE_LIBAOM) return base::FeatureList::IsEnabled(features::kCastStreamingAv1);
diff --git a/components/mirroring/service/mirroring_features.h b/components/mirroring/service/mirroring_features.h index 418bdef1..167b3fa43 100644 --- a/components/mirroring/service/mirroring_features.h +++ b/components/mirroring/service/mirroring_features.h
@@ -21,6 +21,12 @@ COMPONENT_EXPORT(MIRRORING_SERVICE) extern const base::Feature kCastStreamingVp9; +COMPONENT_EXPORT(MIRRORING_SERVICE) +extern const base::Feature kCastUseBlocklistForRemotingQuery; + +COMPONENT_EXPORT(MIRRORING_SERVICE) +extern const base::Feature kCastForceEnableRemotingQuery; + bool IsCastStreamingAV1Enabled(); } // namespace features
diff --git a/components/mirroring/service/session.cc b/components/mirroring/service/session.cc index ccd24b4..68382d70 100644 --- a/components/mirroring/service/session.cc +++ b/components/mirroring/service/session.cc
@@ -321,6 +321,29 @@ return sink_metadata; } +bool ShouldQueryForRemotingCapabilities( + const std::string& receiver_model_name) { + if (base::FeatureList::IsEnabled(features::kCastForceEnableRemotingQuery)) { + return true; + } + + if (base::FeatureList::IsEnabled( + features::kCastUseBlocklistForRemotingQuery)) { + // The blocklist has not yet been fully determined. + // TODO(b/224993260): Implement this blocklist. + NOTREACHED(); + return false; + } + + // This is a workaround for Nest Hub devices, which do not support remoting. + // TODO(crbug.com/1198616): filtering hack should be removed. See + // issuetracker.google.com/135725157 for more information. + return base::StartsWith(receiver_model_name, "Chromecast", + base::CompareCase::SENSITIVE) || + base::StartsWith(receiver_model_name, "Eureka Dongle", + base::CompareCase::SENSITIVE); +} + } // namespace class Session::AudioCapturingCallback final @@ -791,14 +814,8 @@ media_remoter_->OnMirroringResumed(); } - // This is a workaround for Nest Hub devices, which do not support remoting. - // TODO(crbug.com/1198616): filtering hack should be removed. See - // issuetracker.google.com/135725157 for more information. if (initially_starting_session && - (base::StartsWith(session_params_.receiver_model_name, "Chromecast", - base::CompareCase::SENSITIVE) || - base::StartsWith(session_params_.receiver_model_name, "Eureka Dongle", - base::CompareCase::SENSITIVE))) { + ShouldQueryForRemotingCapabilities(session_params_.receiver_model_name)) { QueryCapabilitiesForRemoting(); }
diff --git a/components/nacl/renderer/nexe_load_manager.cc b/components/nacl/renderer/nexe_load_manager.cc index 20b92af..b1e19fa 100644 --- a/components/nacl/renderer/nexe_load_manager.cc +++ b/components/nacl/renderer/nexe_load_manager.cc
@@ -8,6 +8,7 @@ #include <utility> #include "base/command_line.h" +#include "base/containers/buffer_iterator.h" #include "base/logging.h" #include "base/memory/shared_memory_mapping.h" #include "base/metrics/histogram.h" @@ -260,8 +261,8 @@ base::ReadOnlySharedMemoryMapping shmem_mapping = crash_info_shmem_region_.MapAt(0, kNaClCrashInfoShmemSize); if (shmem_mapping.IsValid()) { - base::BufferIterator<const uint8_t> buffer = - shmem_mapping.GetMemoryAsBufferIterator<uint8_t>(); + auto buffer = base::BufferIterator<const uint8_t>( + shmem_mapping.GetMemoryAsSpan<uint8_t>()); const uint32_t* crash_log_length = buffer.Object<uint32_t>(); base::span<const uint8_t> data = buffer.Span<uint8_t>( std::min<uint32_t>(*crash_log_length, kNaClCrashInfoMaxLogSize));
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java index 7ea80915..bac57962 100644 --- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java +++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java
@@ -357,7 +357,7 @@ } @NativeMethods - interface Natives { + public interface Natives { int[] parseForEmphasizeComponents( String text, AutocompleteSchemeClassifier autocompleteSchemeClassifier); }
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc index d456488..20e80e4 100644 --- a/components/omnibox/browser/omnibox_field_trial.cc +++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -482,30 +482,30 @@ } int OmniboxFieldTrial::MaxNumHQPUrlsIndexedAtStartup() { - const char* param = kMaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevicesParam; - const bool is_low_end_device = base::SysInfo::IsLowEndDevice(); - if (is_low_end_device) - param = kMaxNumHQPUrlsIndexedAtStartupOnLowEndDevicesParam; - std::string param_value(variations::GetVariationParamValue( - kBundledExperimentFieldTrialName, param)); - int num_urls; - if (base::StringToInt(param_value, &num_urls)) - return num_urls; - #if BUILDFLAG(IS_ANDROID) // Limits on Android are chosen based on experiment results. See // crbug.com/715852#c18. - constexpr int kMaxNumHQPUrlsIndexedAtStartupOnLowEndDevices = 100; - constexpr int kMaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevices = 1000; - if (is_low_end_device) - return kMaxNumHQPUrlsIndexedAtStartupOnLowEndDevices; - return kMaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevices; + constexpr int kDefaultOnLowEndDevices = 100; + constexpr int kDefaultOnNonLowEndDevices = 1000; #else // Use 20,000 entries as a safety cap for users with spammed history, // such as users who were stuck in a redirect loop with autogenerated URLs. // This limit will only affect 0.01% of Windows users. crbug.com/750845. - return 20000; + constexpr int kDefaultOnLowEndDevices = 20000; + constexpr int kDefaultOnNonLowEndDevices = 20000; #endif // BUILDFLAG(IS_ANDROID) + + if (base::SysInfo::IsLowEndDevice()) { + return variations::GetVariationParamByFeatureAsInt( + omnibox::kHistoryQuickProviderAblateInMemoryURLIndexCacheFile, + kMaxNumHQPUrlsIndexedAtStartupOnLowEndDevicesParam, + kDefaultOnLowEndDevices); + } else { + return variations::GetVariationParamByFeatureAsInt( + omnibox::kHistoryQuickProviderAblateInMemoryURLIndexCacheFile, + kMaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevicesParam, + kDefaultOnNonLowEndDevices); + } } size_t OmniboxFieldTrial::HQPMaxVisitsToScore() {
diff --git a/components/omnibox/browser/open_tab_provider.cc b/components/omnibox/browser/open_tab_provider.cc index 52a5fb9..b5a92a1 100644 --- a/components/omnibox/browser/open_tab_provider.cc +++ b/components/omnibox/browser/open_tab_provider.cc
@@ -37,13 +37,6 @@ } #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) - constexpr size_t kMinQueryLength = 2; - if (input.text().length() < kMinQueryLength) { - // Exit early if the query is too short. This is to mitigate a short query - // matching a large volume of results with low confidence. - return; - } - // TODO(crbug.com/1293702): Open tab search is currently implemented using // the history model to score open tabs. This is an interim implementation, // which is intended to be replaced with a scoring mechanism using only the
diff --git a/components/optimization_guide/content/browser/page_content_annotations_model_manager.h b/components/optimization_guide/content/browser/page_content_annotations_model_manager.h index dc43f85..780fc083 100644 --- a/components/optimization_guide/content/browser/page_content_annotations_model_manager.h +++ b/components/optimization_guide/content/browser/page_content_annotations_model_manager.h
@@ -58,6 +58,9 @@ const std::vector<std::string>& inputs, AnnotationType annotation_type) override; + absl::optional<ModelInfo> GetModelInfoForType( + AnnotationType type) const override; + // Requests that the given model for |type| be loaded in the background and // then runs |callback| with true when the model is ready to execute. If the // model is ready now, the callback is run immediately. If the model file will @@ -66,10 +69,6 @@ AnnotationType type, base::OnceCallback<void(bool)> callback); - // Returns the model info associated with the given AnnotationType, if it is - // available and loaded. - absl::optional<ModelInfo> GetModelInfoForType(AnnotationType type) const; - // Returns the version of the page topics model that is currently being used // to annotate page content. Will return |absl::nullopt| if no model is being // used to annotate page topics for received page content.
diff --git a/components/optimization_guide/content/browser/page_content_annotations_service.cc b/components/optimization_guide/content/browser/page_content_annotations_service.cc index 818f5f0..96cb5dc 100644 --- a/components/optimization_guide/content/browser/page_content_annotations_service.cc +++ b/components/optimization_guide/content/browser/page_content_annotations_service.cc
@@ -239,36 +239,37 @@ } // static -std::string PageContentAnnotationsService::StringInputForPageTopicsDomain( - const GURL& url) { - std::string domain = base::ToLowerASCII(url.host()); +std::string PageContentAnnotationsService::StringInputForPageTopicsHost( + const std::string& host) { + std::string output = base::ToLowerASCII(host); // Strip the 'www.' if it exists. - if (base::StartsWith(domain, "www.")) { - domain = domain.substr(4); + if (base::StartsWith(output, "www.")) { + output = output.substr(4); } - for (char c : std::vector<char>{'-', '_', '.', '+'}) { - std::replace(domain.begin(), domain.end(), c, ' '); + const char kCharsToReplaceWithSpace[] = {'-', '_', '.', '+'}; + for (char c : kCharsToReplaceWithSpace) { + std::replace(output.begin(), output.end(), c, ' '); } - return domain; + return output; } void PageContentAnnotationsService::BatchAnnotatePageTopics( BatchAnnotationCallback callback, - const std::vector<GURL>& inputs) { - std::vector<std::string> domains; - for (const GURL& url : inputs) { - domains.emplace_back(StringInputForPageTopicsDomain(url)); + const std::vector<std::string>& hosts) { + std::vector<std::string> tokenized_hosts; + for (const std::string& host : hosts) { + tokenized_hosts.emplace_back(StringInputForPageTopicsHost(host)); } if (!annotator_) { - std::move(callback).Run(CreateEmptyBatchAnnotationResults(domains)); + std::move(callback).Run(CreateEmptyBatchAnnotationResults(tokenized_hosts)); return; } - annotator_->Annotate(std::move(callback), domains, + annotator_->Annotate(std::move(callback), tokenized_hosts, AnnotationType::kPageTopics); } @@ -289,8 +290,8 @@ absl::optional<ModelInfo> PageContentAnnotationsService::GetModelInfoForType( AnnotationType type) const { #if BUILDFLAG(BUILD_WITH_TFLITE_LIB) - DCHECK(model_manager_); - return model_manager_->GetModelInfoForType(type); + DCHECK(annotator_); + return annotator_->GetModelInfoForType(type); #else return absl::nullopt; #endif @@ -482,11 +483,7 @@ return; } - std::vector<GURL> urls; - for (const std::string& domain : dummy_inputs) { - urls.emplace_back(GURL("https://" + domain)); - } - BatchAnnotatePageTopics(base::DoNothing(), urls); + BatchAnnotatePageTopics(base::DoNothing(), dummy_inputs); } // static
diff --git a/components/optimization_guide/content/browser/page_content_annotations_service.h b/components/optimization_guide/content/browser/page_content_annotations_service.h index fc3b6dd..76c64aa 100644 --- a/components/optimization_guide/content/browser/page_content_annotations_service.h +++ b/components/optimization_guide/content/browser/page_content_annotations_service.h
@@ -107,10 +107,10 @@ const std::vector<std::string>& inputs, AnnotationType annotation_type); - // Calls |BatchAnnotate| with pre-processing the urls into its domain string, - // all specific to PageTopics. + // Calls |BatchAnnotate| with pre-processing the hosts into tokens, all + // specific to PageTopics. void BatchAnnotatePageTopics(BatchAnnotationCallback callback, - const std::vector<GURL>& inputs); + const std::vector<std::string>& inputs); // Requests that the given model for |type| be loaded in the background and // then runs |callback| with true when the model is ready to execute. If the @@ -135,7 +135,7 @@ private: friend class PageContentAnnotationsServiceTest; - static std::string StringInputForPageTopicsDomain(const GURL& url); + static std::string StringInputForPageTopicsHost(const std::string& host); #if BUILDFLAG(BUILD_WITH_TFLITE_LIB) // Callback invoked when |visit| has been annotated.
diff --git a/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc b/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc index 99a681e..5d493192 100644 --- a/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc +++ b/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc
@@ -8,7 +8,6 @@ #include <vector> #include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" namespace optimization_guide { @@ -17,29 +16,29 @@ PageContentAnnotationsServiceTest() = default; ~PageContentAnnotationsServiceTest() override = default; - std::string CallStringInputForPageTopicsDomain(const GURL& url) { - return PageContentAnnotationsService::StringInputForPageTopicsDomain(url); + std::string CallStringInputForPageTopicsHost(const std::string& host) { + return PageContentAnnotationsService::StringInputForPageTopicsHost(host); } }; -TEST_F(PageContentAnnotationsServiceTest, PageTopicsDomain) { - std::vector<std::pair<GURL, std::string>> tests = { - {GURL("https://www.chromium.org/path?q=a"), "chromium org"}, - {GURL("https://foo-bar.com/"), "foo bar com"}, - {GURL("https://foo_bar.com/"), "foo bar com"}, - {GURL("https://cats.co.uk/"), "cats co uk"}, - {GURL("https://cats+dogs.com"), "cats dogs com"}, - {GURL("https://www.foo-bar_.baz.com"), "foo bar baz com"}, - {GURL("https://www.foo-bar-baz.com"), "foo bar baz com"}, - {GURL("https://WwW.LOWER-CASE.com"), "lower case com"}, +TEST_F(PageContentAnnotationsServiceTest, PageTopicsHost) { + std::vector<std::pair<std::string, std::string>> tests = { + {"www.chromium.org", "chromium org"}, + {"foo-bar.com", "foo bar com"}, + {"foo_bar.com", "foo bar com"}, + {"cats.co.uk", "cats co uk"}, + {"cats+dogs.com", "cats dogs com"}, + {"www.foo-bar_.baz.com", "foo bar baz com"}, + {"www.foo-bar-baz.com", "foo bar baz com"}, + {"WwW.LOWER-CASE.com", "lower case com"}, }; for (const auto& test : tests) { - GURL url = test.first; + std::string host = test.first; std::string expected = test.second; - std::string got = CallStringInputForPageTopicsDomain(url); + std::string got = CallStringInputForPageTopicsHost(host); - EXPECT_EQ(expected, got) << url; + EXPECT_EQ(expected, got) << host; } }
diff --git a/components/optimization_guide/content/browser/page_content_annotator.h b/components/optimization_guide/content/browser/page_content_annotator.h index bf86383..f76b870 100644 --- a/components/optimization_guide/content/browser/page_content_annotator.h +++ b/components/optimization_guide/content/browser/page_content_annotator.h
@@ -9,7 +9,9 @@ #include <vector> #include "base/callback.h" +#include "components/optimization_guide/core/model_info.h" #include "components/optimization_guide/core/page_content_annotations_common.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace optimization_guide { @@ -28,6 +30,11 @@ virtual void Annotate(BatchAnnotationCallback callback, const std::vector<std::string>& inputs, AnnotationType annotation_type) = 0; + + // Returns the model info associated with the given AnnotationType, if it is + // available and loaded. + virtual absl::optional<ModelInfo> GetModelInfoForType( + AnnotationType annotation_type) const = 0; }; } // namespace optimization_guide
diff --git a/components/optimization_guide/content/browser/test_page_content_annotator.cc b/components/optimization_guide/content/browser/test_page_content_annotator.cc index f1c12bed..62d7ba8f 100644 --- a/components/optimization_guide/content/browser/test_page_content_annotator.cc +++ b/components/optimization_guide/content/browser/test_page_content_annotator.cc
@@ -53,20 +53,40 @@ std::move(callback).Run(results); } +absl::optional<ModelInfo> TestPageContentAnnotator::GetModelInfoForType( + AnnotationType annotation_type) const { + if (annotation_type == AnnotationType::kPageTopics) + return topics_model_info_; + + if (annotation_type == AnnotationType::kPageEntities) + return entities_model_info_; + + if (annotation_type == AnnotationType::kPageEntities) + return visibility_scores_model_info_; + + return absl::nullopt; +} + void TestPageContentAnnotator::UsePageTopics( + const absl::optional<ModelInfo>& model_info, const base::flat_map<std::string, std::vector<WeightedIdentifier>>& topics_by_input) { + topics_model_info_ = model_info; topics_by_input_ = topics_by_input; } void TestPageContentAnnotator::UsePageEntities( + const absl::optional<ModelInfo>& model_info, const base::flat_map<std::string, std::vector<ScoredEntityMetadata>>& entities_by_input) { + entities_model_info_ = model_info; entities_by_input_ = entities_by_input; } void TestPageContentAnnotator::UseVisibilityScores( + const absl::optional<ModelInfo>& model_info, const base::flat_map<std::string, double>& visibility_scores_for_input) { + visibility_scores_model_info_ = model_info; visibility_scores_for_input_ = visibility_scores_for_input; }
diff --git a/components/optimization_guide/content/browser/test_page_content_annotator.h b/components/optimization_guide/content/browser/test_page_content_annotator.h index a38e1cc..d91a5590 100644 --- a/components/optimization_guide/content/browser/test_page_content_annotator.h +++ b/components/optimization_guide/content/browser/test_page_content_annotator.h
@@ -23,18 +23,21 @@ // The given page topics are used for the matching BatchAnnotationResults by // input string. If the input is not found, the output is left as nullopt. void UsePageTopics( + const absl::optional<ModelInfo>& model_info, const base::flat_map<std::string, std::vector<WeightedIdentifier>>& topics_by_input); // The given page entities are used for the matching BatchAnnotationResults by // input string. If the input is not found, the output is left as nullopt. void UsePageEntities( + const absl::optional<ModelInfo>& model_info, const base::flat_map<std::string, std::vector<ScoredEntityMetadata>>& entities_by_input); // The given visibility score is used for the matching BatchAnnotationResults // by input string. If the input is not found, the output is left as nullopt. void UseVisibilityScores( + const absl::optional<ModelInfo>& model_info, const base::flat_map<std::string, double>& visibility_scores_for_input); // PageContentAnnotator: @@ -42,10 +45,18 @@ const std::vector<std::string>& inputs, AnnotationType annotation_type) override; + absl::optional<ModelInfo> GetModelInfoForType( + AnnotationType annotation_type) const override; + private: + absl::optional<ModelInfo> topics_model_info_; base::flat_map<std::string, std::vector<WeightedIdentifier>> topics_by_input_; + + absl::optional<ModelInfo> entities_model_info_; base::flat_map<std::string, std::vector<ScoredEntityMetadata>> entities_by_input_; + + absl::optional<ModelInfo> visibility_scores_model_info_; base::flat_map<std::string, double> visibility_scores_for_input_; };
diff --git a/components/optimization_guide/core/optimization_guide_features.cc b/components/optimization_guide/core/optimization_guide_features.cc index c798be2e..b42818e 100644 --- a/components/optimization_guide/core/optimization_guide_features.cc +++ b/components/optimization_guide/core/optimization_guide_features.cc
@@ -120,6 +120,10 @@ const base::Feature kPageEntitiesModelBypassFilters{ "PageEntitiesModelBypassFilters", base::FEATURE_DISABLED_BY_DEFAULT}; +// This feature flag enables resetting the entities model on shutdown. +const base::Feature kPageEntitiesModelResetOnShutdown{ + "PageEntitiesModelResetOnShutdown", base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables push notification of hints. const base::Feature kPushNotifications{"OptimizationGuidePushNotifications", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -473,6 +477,10 @@ return !base::FeatureList::IsEnabled(kPageEntitiesModelBypassFilters); } +bool ShouldResetPageEntitiesModelOnShutdown() { + return base::FeatureList::IsEnabled(kPageEntitiesModelResetOnShutdown); +} + bool ShouldExecutePageVisibilityModelOnPageContent(const std::string& locale) { return base::FeatureList::IsEnabled(kPageVisibilityPageContentAnnotations) && IsSupportedLocaleForFeature(locale,
diff --git a/components/optimization_guide/core/optimization_guide_features.h b/components/optimization_guide/core/optimization_guide_features.h index bc760ec..f90021a1 100644 --- a/components/optimization_guide/core/optimization_guide_features.h +++ b/components/optimization_guide/core/optimization_guide_features.h
@@ -237,6 +237,9 @@ // model. bool ShouldProvideFilterPathForPageEntitiesModel(); +// Returns whether the page entities model should be reset on shutdown. +bool ShouldResetPageEntitiesModelOnShutdown(); + // Returns whether the page visibility model should be executed on page content // for a user using |locale| as their browser language. bool ShouldExecutePageVisibilityModelOnPageContent(const std::string& locale);
diff --git a/components/optimization_guide/core/page_content_annotations_common.h b/components/optimization_guide/core/page_content_annotations_common.h index 4f77386..0a7f14c 100644 --- a/components/optimization_guide/core/page_content_annotations_common.h +++ b/components/optimization_guide/core/page_content_annotations_common.h
@@ -86,12 +86,12 @@ BatchAnnotationResult(const BatchAnnotationResult&); ~BatchAnnotationResult(); - std::string input() const { return input_; } + const std::string& input() const { return input_; } AnnotationType type() const { return type_; } - absl::optional<std::vector<WeightedIdentifier>> topics() const { + const absl::optional<std::vector<WeightedIdentifier>>& topics() const { return topics_; } - absl::optional<std::vector<ScoredEntityMetadata>> entities() const { + const absl::optional<std::vector<ScoredEntityMetadata>>& entities() const { return entities_; } absl::optional<double> visibility_score() const { return visibility_score_; }
diff --git a/components/optimization_guide/core/page_entities_model_executor_impl.cc b/components/optimization_guide/core/page_entities_model_executor_impl.cc index f4da349..d3a3483 100644 --- a/components/optimization_guide/core/page_entities_model_executor_impl.cc +++ b/components/optimization_guide/core/page_entities_model_executor_impl.cc
@@ -8,6 +8,7 @@ #include "base/threading/sequenced_task_runner_handle.h" #include "base/timer/elapsed_timer.h" #include "components/optimization_guide/core/entity_annotator_native_library.h" +#include "components/optimization_guide/core/optimization_guide_features.h" #include "components/optimization_guide/core/optimization_guide_model_provider.h" #include "components/optimization_guide/proto/page_entities_model_metadata.pb.h" @@ -30,7 +31,9 @@ EntityAnnotatorHolder::~EntityAnnotatorHolder() { DCHECK(background_task_runner_->RunsTasksInCurrentSequence()); - ResetEntityAnnotator(); + if (features::ShouldResetPageEntitiesModelOnShutdown()) { + ResetEntityAnnotator(); + } } void EntityAnnotatorHolder::
diff --git a/components/page_info/OWNERS b/components/page_info/OWNERS index 7c601fd..535ff1d 100644 --- a/components/page_info/OWNERS +++ b/components/page_info/OWNERS
@@ -1 +1,3 @@ file://chrome/browser/ui/page_info/OWNERS + +per-file ...about_this_site*=dullweber@chromium.org
diff --git a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc index 824e73d..6140b5b 100644 --- a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc +++ b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc
@@ -122,8 +122,8 @@ // is needed. Even for syncing users we still should do the initial migration // to move local-only data that aren't synced to GMSCore and do the rolling // migration to ensure deletions aren’t resurrected. - if (is_initial_migration_needed || - base::FeatureList::IsEnabled(features::kUnifiedPasswordManagerAndroid)) { + if (is_initial_migration_needed && + features::RequiresInitialMigrationForUnifiedPasswordManager()) { metrics_reporter_ = std::make_unique<MigrationMetricsReporter>( is_initial_migration_needed ? "InitialMigration" : "RollingMigration"); PrepareForMigration();
diff --git a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc index c5ad3d93..9e0e90a 100644 --- a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc +++ b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc
@@ -93,8 +93,8 @@ TEST_F(BuiltInBackendToAndroidBackendMigratorTest, CurrentMigrationVersionIsUpdatedWhenMigrationIsNeeded_SyncOn) { feature_list().InitAndEnableFeatureWithParameters( - /*enabled_feature=*/features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}); + /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}); Init(); EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled) .WillRepeatedly(Return(true)); @@ -112,8 +112,8 @@ TEST_F(BuiltInBackendToAndroidBackendMigratorTest, AllPrefsAreUpdatedWhenMigrationIsNeeded_SyncOff) { feature_list().InitAndEnableFeatureWithParameters( - /*enabled_feature=*/features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}); + /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}); Init(); EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled) @@ -132,7 +132,8 @@ TEST_F(BuiltInBackendToAndroidBackendMigratorTest, PrefsUnchangedWhenAttemptedMigrationEarlierToday) { feature_list().InitAndEnableFeatureWithParameters( - features::kUnifiedPasswordManagerMigration, {{"migration_version", "1"}}); + features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}); Init(); prefs()->SetDouble(password_manager::prefs::kTimeOfLastMigrationAttempt, @@ -152,9 +153,9 @@ LastAttemptUnchangedWhenRollingMigrationDisabled) { // Setup the pref to indicate that the initial migration has happened already. feature_list().InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}}}, - /*disabled_features=*/{features::kUnifiedPasswordManagerAndroid}); + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "1"}}}}, + /*disabled_features=*/{}); Init(/*current_migration_version=*/1); migrator()->StartMigrationIfNecessary(); @@ -166,13 +167,13 @@ password_manager::prefs::kTimeOfLastMigrationAttempt)); } +// TODO(crbug.com/1306001): Reenable rolling migration or clean up. TEST_F(BuiltInBackendToAndroidBackendMigratorTest, - LastAttemptUpdatedInPrefsWhenRollingMigrationEnabled) { + DISABLED_LastAttemptUpdatedInPrefsWhenRollingMigrationEnabled) { // Setup the pref to indicate that the initial migration has happened already. feature_list().InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}}, - {features::kUnifiedPasswordManagerAndroid, {{}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}}}, /*disabled_features=*/{}); Init(/*current_migration_version=*/1); @@ -193,8 +194,8 @@ "PasswordManager.UnifiedPasswordManager.WasMigrationDone"; feature_list().InitAndEnableFeatureWithParameters( - /*enabled_feature=*/features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}); + /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}); Init(); histogram_tester.ExpectTotalCount(kMigrationFinishedMetric, 1); @@ -208,8 +209,8 @@ "PasswordManager.UnifiedPasswordManager.WasMigrationDone"; feature_list().InitAndEnableFeatureWithParameters( - /*enabled_feature=*/features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}); + /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}); Init(/*current_migration_version=*/1); histogram_tester.ExpectTotalCount(kMigrationFinishedMetric, 1); @@ -223,8 +224,8 @@ "PasswordManager.UnifiedPasswordManager.WasMigrationDone"; feature_list().InitAndEnableFeatureWithParameters( - /*enabled_feature=*/features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}); + /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "0"}}); Init(/*current_migration_version=*/1); @@ -235,8 +236,8 @@ TEST_F(BuiltInBackendToAndroidBackendMigratorTest, InitialMigrationForSyncingUserShouldMoveLocalOnlyDataToAndroidBackend) { feature_list().InitAndEnableFeatureWithParameters( - /*enabled_feature=*/features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}); + /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}); EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled) .WillRepeatedly(Return(true)); @@ -324,8 +325,8 @@ .WillRepeatedly(Return(false)); feature_list().InitAndEnableFeatureWithParameters( - /*enabled_feature=*/features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}); + /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}); const MigrationParam& p = GetParam(); @@ -349,14 +350,14 @@ } } +// TODO(crbug.com/1306001): Reenable or clean up for local-only users. TEST_P(BuiltInBackendToAndroidBackendMigratorTestWithMigrationParams, - RollingMigration) { + DISABLED_RollingMigration) { // Setup the pref to indicate that the initial migration has happened already. // This implies that rolling migration will take place! feature_list().InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}}, - {features::kUnifiedPasswordManagerAndroid, {{}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}}}, /*disabled_features=*/{}); BuiltInBackendToAndroidBackendMigratorTest::Init( /*current_migration_version=*/1); @@ -429,18 +430,16 @@ 0.0); if (GetParam().is_initial_migration) { feature_list().InitAndEnableFeatureWithParameters( - /*enabled_feature=*/features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}); + /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}); latency_metric_ = "PasswordManager.UnifiedPasswordManager.InitialMigration.Latency"; success_metric_ = "PasswordManager.UnifiedPasswordManager.InitialMigration.Success"; } else { feature_list().InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}}, - {features::kUnifiedPasswordManagerAndroid, - {{}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}}}, /*disabled_features=*/{}); // Setup the pref to indicate that the initial migration has happened // already. @@ -508,17 +507,14 @@ !GetParam().is_successful_migration); } +// TODO(crbug.com/1306001): Add test cases rolling migration or clean up. INSTANTIATE_TEST_SUITE_P( BuiltInBackendToAndroidBackendMigratorTest, BuiltInBackendToAndroidBackendMigratorTestMetrics, testing::Values(MigrationParamForMetrics{.is_initial_migration = true, .is_successful_migration = true}, - MigrationParamForMetrics{.is_initial_migration = true, - .is_successful_migration = false}, - MigrationParamForMetrics{.is_initial_migration = false, - .is_successful_migration = true}, MigrationParamForMetrics{ - .is_initial_migration = false, + .is_initial_migration = true, .is_successful_migration = false})); class BuiltInBackendToAndroidBackendMigratorWithMockAndroidBackendTest @@ -530,8 +526,8 @@ prefs()->registry()->RegisterDoublePref(prefs::kTimeOfLastMigrationAttempt, 0.0); feature_list().InitAndEnableFeatureWithParameters( - /*enabled_feature=*/features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}); + /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}); migrator_ = std::make_unique<BuiltInBackendToAndroidBackendMigrator>( &built_in_backend_, &android_backend_, prefs(), &sync_delegate_);
diff --git a/components/password_manager/core/browser/password_store_backend_migration_decorator.cc b/components/password_manager/core/browser/password_store_backend_migration_decorator.cc index e7d1565c..2e636b7 100644 --- a/components/password_manager/core/browser/password_store_backend_migration_decorator.cc +++ b/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
@@ -68,8 +68,10 @@ active_backend_->InitBackend(std::move(remote_form_changes_received), std::move(sync_enabled_or_disabled_cb), std::move(completion)); - if (base::FeatureList::IsEnabled( - features::kUnifiedPasswordManagerMigration)) { + + // Only start the migration when launching the UPM which needs chrome-local + // data in the remote store. For shadow traffic, this doesn't matter. + if (features::RequiresInitialMigrationForUnifiedPasswordManager()) { migrator_ = std::make_unique<BuiltInBackendToAndroidBackendMigrator>( built_in_backend_.get(), android_backend_.get(), prefs_, sync_delegate_.get()); @@ -201,12 +203,16 @@ } void PasswordStoreBackendMigrationDecorator::SyncStatusChanged() { - if (!base::FeatureList::IsEnabled(features::kUnifiedPasswordManagerMigration)) + if (!features::RequiresInitialMigrationForUnifiedPasswordManager()) return; if (sync_delegate_->IsSyncingPasswordsEnabled()) { - // Sync was enabled. Delete all the passwords from GMS Core local storage. - android_backend_->ClearAllLocalPasswords(); + // During initial rollout, local passwords remain untouched. Only the use of + // a different local storage requires explicit migration. + if (features::ManagesLocalPasswordsInUnifiedPasswordManager()) { + // Sync was enabled. Delete all the passwords from GMS Core local storage. + android_backend_->ClearAllLocalPasswords(); + } } else { // Clear migration pref to force rerun of initial migration of passwords // from Chrome to GMS Core local storage.
diff --git a/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc b/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc index 9288d4b4..3ff1bc5d 100644 --- a/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc +++ b/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc
@@ -36,8 +36,8 @@ prefs::kCurrentMigrationVersionToGoogleMobileServices, 0); feature_list_.InitAndEnableFeatureWithParameters( - /*enabled_feature=*/features::kUnifiedPasswordManagerMigration, - {{"migration_version", "1"}}); + /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "1"}, {"stage", "0"}}); backend_migration_decorator_ = std::make_unique<PasswordStoreBackendMigrationDecorator>( @@ -126,8 +126,9 @@ prefs::kCurrentMigrationVersionToGoogleMobileServices)); } +// TODO(crbug.com/1306001): Reenable or clean up for local-only users. TEST_F(PasswordStoreBackendMigrationDecoratorTest, - LocalAndroidPasswordsClearedWhenSyncEnabled) { + DISABLED_LocalAndroidPasswordsClearedWhenSyncEnabled) { base::MockCallback<base::OnceCallback<void(bool)>> mock_completion_callback; base::RepeatingClosure sync_status_changed_closure;
diff --git a/components/password_manager/core/browser/password_store_proxy_backend.cc b/components/password_manager/core/browser/password_store_proxy_backend.cc index 31b0701..23369493 100644 --- a/components/password_manager/core/browser/password_store_proxy_backend.cc +++ b/components/password_manager/core/browser/password_store_proxy_backend.cc
@@ -28,20 +28,8 @@ bool ShouldExecuteModifyOperationsOnShadowBackend(PrefService* prefs, bool is_syncing) { - if (!base::FeatureList::IsEnabled( - features::kUnifiedPasswordManagerShadowWriteOperationsAndroid)) { - return false; - } - if (is_syncing) - return false; - if (features::kMigrationVersion.Get() > - prefs->GetInteger( - prefs::kCurrentMigrationVersionToGoogleMobileServices)) { - // If initial migration isn't completed yet, we shouldn't modify the shadow - // backend. - return false; - } - return true; + // TODO(crbug.com/1306001): Reenable or clean up for local-only users. + return false; } bool ShouldExecuteReadOperationsOnShadowBackend(PrefService* prefs, @@ -51,8 +39,11 @@ // i.e. necessary migrations have happened and appropriate flags are set. return true; } - return is_syncing && base::FeatureList::IsEnabled( - features::kUnifiedPasswordManagerShadowAndroid); + return is_syncing && + base::FeatureList::IsEnabled( + features::kUnifiedPasswordManagerAndroid) && + features::kUpmExperimentVariationParam.Get() == + features::UpmExperimentVariation::kShadowSyncingUsers; } using MethodName = base::StrongAlias<struct MethodNameTag, std::string>;
diff --git a/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc b/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc index 471be652..cc21114c 100644 --- a/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc +++ b/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
@@ -74,10 +74,9 @@ proxy_backend_ = std::make_unique<PasswordStoreProxyBackend>( &main_backend_, &shadow_backend_, &prefs_, &sync_delegate_); - feature_list_.InitWithFeatures( - /*enabled_features=*/ - {features::kUnifiedPasswordManagerShadowAndroid, - features::kUnifiedPasswordManagerShadowWriteOperationsAndroid}, + feature_list_.InitWithFeaturesAndParameters( + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"stage", "1"}}}}, /*disabled_features=*/{}); prefs_.registry()->RegisterIntegerPref( @@ -321,8 +320,8 @@ NoShadowGetAllLoginsAsyncWithoutSyncOrMigration) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1); @@ -357,8 +356,8 @@ NoShadowGetAutofillableLoginsAsyncWithoutSyncOrMigration) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1); EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled) @@ -373,8 +372,8 @@ NoShadowFillMatchingLoginsAsyncWithoutSyncOrMigration) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1); EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled) @@ -401,8 +400,8 @@ NoShadowAddLoginAsyncWhenSyncDisabledAndInitialMigrationIncomplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1); @@ -415,12 +414,14 @@ /*callback=*/base::DoNothing()); } -TEST_F(PasswordStoreProxyBackendTest, - ShadowAddLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) { +// TODO(crbug.com/1306001): Reenable or clean up for local-only users. +TEST_F( + PasswordStoreProxyBackendTest, + DISABLED_ShadowAddLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2); @@ -433,14 +434,16 @@ /*callback=*/base::DoNothing()); } -TEST_F(PasswordStoreProxyBackendTest, ShadowAddLoginAsyncBasicMetricsTesting) { +// TODO(crbug.com/1306001): Reenable or clean up for local-only users. +TEST_F(PasswordStoreProxyBackendTest, + DISABLED_ShadowAddLoginAsyncBasicMetricsTesting) { base::HistogramTester histogram_tester; // Set the prefs such that no initial migration is required to allow shadow // write operations. base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2); // Shadow write operations run only for non-syncing users. @@ -493,8 +496,8 @@ NoShadowUpdateLoginAsyncWhenSyncDisabledAndInitialMigrationIncomplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1); @@ -507,12 +510,14 @@ /*callback=*/base::DoNothing()); } -TEST_F(PasswordStoreProxyBackendTest, - ShadowGetAllLoginsAsyncWhenSyncDisabledAndInitialMigrationComplete) { +// TODO(crbug.com/1306001): Reenable or clean up for local-only users. +TEST_F( + PasswordStoreProxyBackendTest, + DISABLED_ShadowGetAllLoginsAsyncWhenSyncDisabledAndInitialMigrationComplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2); EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled) @@ -546,12 +551,14 @@ histogram_tester.ExpectTotalCount(prefix + "InconsistentPasswords.Rel", 1); } -TEST_F(PasswordStoreProxyBackendTest, - ShadowUpdateLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) { +// TODO(crbug.com/1306001): Reenable or clean up for local-only users. +TEST_F( + PasswordStoreProxyBackendTest, + DISABLED_ShadowUpdateLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2); @@ -578,8 +585,8 @@ NoShadowRemoveLoginAsyncWhenSyncDisabledAndInitialMigrationIncomplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1); @@ -592,12 +599,14 @@ /*callback=*/base::DoNothing()); } -TEST_F(PasswordStoreProxyBackendTest, - ShadowRemoveLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) { +// TODO(crbug.com/1306001): Reenable or clean up for local-only users. +TEST_F( + PasswordStoreProxyBackendTest, + DISABLED_ShadowRemoveLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2); @@ -630,8 +639,8 @@ NoShadowRemoveLoginsByURLAndTimeAsyncWhenSyncDisabledAndInitialMigrationIncomplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1); @@ -648,13 +657,14 @@ /*callback=*/base::DoNothing()); } +// TODO(crbug.com/1306001): Reenable or clean up for local-only users. TEST_F( PasswordStoreProxyBackendTest, - ShadowRemoveLoginsByURLAndTimeAsyncWhenSyncDisabledAndInitialMigrationComplete) { + DISABLED_ShadowRemoveLoginsByURLAndTimeAsyncWhenSyncDisabledAndInitialMigrationComplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2); @@ -689,8 +699,8 @@ NoShadowRemoveLoginsCreatedBetweenAsyncWhenSyncDisabledAndInitialMigrationIncomplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1); @@ -705,13 +715,14 @@ /*callback=*/base::DoNothing()); } +// TODO(crbug.com/1306001): Reenable or clean up for local-only users. TEST_F( PasswordStoreProxyBackendTest, - ShadowRemoveLoginsCreatedBetweenAsyncWhenSyncDisabledAndInitialMigrationComplete) { + DISABLED_ShadowRemoveLoginsCreatedBetweenAsyncWhenSyncDisabledAndInitialMigrationComplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2); @@ -742,8 +753,8 @@ NoShadowDisableAutoSignInForOriginsAsyncWhenSyncDisabledAndInitialMigrationIncomplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1); @@ -756,13 +767,14 @@ base::BindRepeating(&FilterNoUrl), /*completion=*/base::DoNothing()); } +// TODO(crbug.com/1306001): Reenable or clean up for local-only users. TEST_F( PasswordStoreProxyBackendTest, - ShadowDisableAutoSignInForOriginsAsyncWhenSyncDisabledAndInitialMigrationComplete) { + DISABLED_ShadowDisableAutoSignInForOriginsAsyncWhenSyncDisabledAndInitialMigrationComplete) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeaturesAndParameters( - /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration, - {{"migration_version", "2"}}}}, + /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid, + {{"migration_version", "2"}, {"stage", "1"}}}}, /*disabled_features=*/{}); prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc index e493272..a454f16 100644 --- a/components/password_manager/core/common/password_manager_features.cc +++ b/components/password_manager/core/common/password_manager_features.cc
@@ -193,23 +193,6 @@ const base::Feature kUnifiedPasswordManagerAndroid{ "UnifiedPasswordManagerAndroid", base::FEATURE_DISABLED_BY_DEFAULT}; -// Enables migration of passwords from built in storage to Google Mobile -// Services. -const base::Feature kUnifiedPasswordManagerMigration{ - "UnifiedPasswordManagerMigration", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Sends shadow traffic to Google Mobile Services for password storage. This -// allows to check stability without switching away from the local storage as -// source of truth. -const base::Feature kUnifiedPasswordManagerShadowAndroid{ - "UnifiedPasswordManagerShadowAndroid", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Similar to kUnifiedPasswordManagerShadowAndroid but send modify operations -// instead of read operations.Relevant only for non-sync'ing users. -const base::Feature kUnifiedPasswordManagerShadowWriteOperationsAndroid{ - "UnifiedPasswordManagerShadowWriteOperationsAndroid", - base::FEATURE_DISABLED_BY_DEFAULT}; - // If enabled, the built-in sync functionality in PasswordSyncBridge becomes // unused, meaning that SyncService/SyncEngine will no longer download or // upload changes to/from the Sync server. Instead, an external Android-specific @@ -249,7 +232,7 @@ // Current migration version to Google Mobile Services. If version saved in pref // is lower than 'kMigrationVersion' passwords will be re-uploaded. extern const base::FeatureParam<int> kMigrationVersion = { - &kUnifiedPasswordManagerMigration, "migration_version", 1}; + &kUnifiedPasswordManagerAndroid, "migration_version", 1}; #endif // Field trial identifier for password generation requirements. @@ -294,9 +277,44 @@ #if BUILDFLAG(IS_ANDROID) bool UsesUnifiedPasswordManagerUi() { - return base::FeatureList::IsEnabled(kUnifiedPasswordManagerAndroid) && - kUpmExperimentVariationParam.Get() != - UpmExperimentVariation::kShadowSyncingUsers; + if (!base::FeatureList::IsEnabled(kUnifiedPasswordManagerAndroid)) + return false; + UpmExperimentVariation variation = kUpmExperimentVariationParam.Get(); + switch (variation) { + case UpmExperimentVariation::kEnableForSyncingUsers: + return true; + case UpmExperimentVariation::kShadowSyncingUsers: + return false; + } + NOTREACHED() << "Define explicitly whether UI is required!"; + return false; +} + +bool RequiresInitialMigrationForUnifiedPasswordManager() { + if (!base::FeatureList::IsEnabled(kUnifiedPasswordManagerAndroid)) + return false; + UpmExperimentVariation variation = kUpmExperimentVariationParam.Get(); + switch (variation) { + case UpmExperimentVariation::kEnableForSyncingUsers: + return true; + case UpmExperimentVariation::kShadowSyncingUsers: + return false; + } + NOTREACHED() << "Define explicitly whether migration is required!"; + return false; +} + +bool ManagesLocalPasswordsInUnifiedPasswordManager() { + if (!base::FeatureList::IsEnabled(kUnifiedPasswordManagerAndroid)) + return false; + UpmExperimentVariation variation = kUpmExperimentVariationParam.Get(); + switch (variation) { + case UpmExperimentVariation::kEnableForSyncingUsers: + case UpmExperimentVariation::kShadowSyncingUsers: + return false; + } + NOTREACHED() << "Define explicitly whether migration is required!"; + return false; } #endif // IS_ANDROID
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h index 3d997607..540549c 100644 --- a/components/password_manager/core/common/password_manager_features.h +++ b/components/password_manager/core/common/password_manager_features.h
@@ -64,9 +64,6 @@ #if BUILDFLAG(IS_ANDROID) extern const base::Feature kUnifiedCredentialManagerDryRun; extern const base::Feature kUnifiedPasswordManagerAndroid; -extern const base::Feature kUnifiedPasswordManagerMigration; -extern const base::Feature kUnifiedPasswordManagerShadowAndroid; -extern const base::Feature kUnifiedPasswordManagerShadowWriteOperationsAndroid; extern const base::Feature kUnifiedPasswordManagerSyncUsingAndroidBackendOnly; #endif extern const base::Feature kUnifiedPasswordManagerDesktop; @@ -120,6 +117,15 @@ // Returns true if the unified password manager feature is active and in a stage // that allows to use the new UI. bool UsesUnifiedPasswordManagerUi(); + +// Returns true if the unified password manager feature is active and in a stage +// that requires migrating existing credentials initially. Independent of +// whether only non-syncable data needs to be migrated or full credentials. +bool RequiresInitialMigrationForUnifiedPasswordManager(); + +// Returns true if the unified password manager feature is active and in a stage +// that uses the unified storage for passwords that remain local on the device. +bool ManagesLocalPasswordsInUnifiedPasswordManager(); #endif // IS_ANDROID } // namespace password_manager::features
diff --git a/components/policy/core/common/management/management_service.cc b/components/policy/core/common/management/management_service.cc index f5f50bf..87b149f 100644 --- a/components/policy/core/common/management/management_service.cc +++ b/components/policy/core/common/management/management_service.cc
@@ -107,16 +107,17 @@ ManagementAuthorityTrustworthiness previous = GetManagementAuthorityTrustworthiness(); for (const auto& provider : management_status_providers_) { - if (provider->RequiresCache()) + if (provider->RequiresCache()) { provider->UpdateCache(provider->FetchAuthority()); + } - ManagementAuthorityTrustworthiness next = - GetManagementAuthorityTrustworthiness(); - base::UmaHistogramBoolean( - "Enterprise.ManagementAuthorityTrustworthiness.Cache.ValueChange", - previous != next); - if (callback) - std::move(callback).Run(previous, next); + ManagementAuthorityTrustworthiness next = + GetManagementAuthorityTrustworthiness(); + base::UmaHistogramBoolean( + "Enterprise.ManagementAuthorityTrustworthiness.Cache.ValueChange", + previous != next); + if (callback) + std::move(callback).Run(previous, next); } } @@ -190,4 +191,10 @@ management_status_providers_ = std::move(providers); } +void ManagementService::AddManagementStatusProvider( + std::unique_ptr<ManagementStatusProvider> provider) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + management_status_providers_.push_back(std::move(provider)); +} + } // namespace policy
diff --git a/components/policy/core/common/management/management_service.h b/components/policy/core/common/management/management_service.h index 4ce5dc8..6836d27f 100644 --- a/components/policy/core/common/management/management_service.h +++ b/components/policy/core/common/management/management_service.h
@@ -131,6 +131,10 @@ // Sets the management status providers to be used by the service. void SetManagementStatusProvider( std::vector<std::unique_ptr<ManagementStatusProvider>> providers); + + void AddManagementStatusProvider( + std::unique_ptr<ManagementStatusProvider> provider); + const std::vector<std::unique_ptr<ManagementStatusProvider>>& management_status_providers() { return management_status_providers_;
diff --git a/components/policy/core/common/management/platform_management_service.cc b/components/policy/core/common/management/platform_management_service.cc index 6e663f8f..a68d3787 100644 --- a/components/policy/core/common/management/platform_management_service.cc +++ b/components/policy/core/common/management/platform_management_service.cc
@@ -51,6 +51,14 @@ PlatformManagementService::~PlatformManagementService() = default; +#if BUILDFLAG(IS_CHROMEOS_ASH) +void PlatformManagementService::AddChromeOsStatusProvider( + std::unique_ptr<ManagementStatusProvider> provider) { + AddManagementStatusProvider(std::move(provider)); + has_cros_status_provider_ = true; +} +#endif + void PlatformManagementService::RefreshCache(CacheRefreshCallback callback) { if (!base::FeatureList::IsEnabled(features::kEnableCachedManagementStatus)) return;
diff --git a/components/policy/core/common/management/platform_management_service.h b/components/policy/core/common/management/platform_management_service.h index 1afd29d..ea542f71 100644 --- a/components/policy/core/common/management/platform_management_service.h +++ b/components/policy/core/common/management/platform_management_service.h
@@ -7,6 +7,7 @@ #include "base/containers/flat_map.h" #include "base/no_destructor.h" +#include "build/chromeos_buildflags.h" #include "components/policy/core/common/management/management_service.h" #include "components/policy/policy_export.h" @@ -22,6 +23,12 @@ // Returns the singleton instance of PlatformManagementService. static PlatformManagementService* GetInstance(); +#if BUILDFLAG(IS_CHROMEOS_ASH) + void AddChromeOsStatusProvider( + std::unique_ptr<ManagementStatusProvider> provider); + bool has_cros_status_provider() const { return has_cros_status_provider_; } +#endif + void RefreshCache(CacheRefreshCallback callback) override; private: @@ -42,6 +49,10 @@ PlatformManagementService(); ~PlatformManagementService() override; + +#if BUILDFLAG(IS_CHROMEOS_ASH) + bool has_cros_status_provider_; +#endif }; } // namespace policy
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm index b1f7683..6394980 100644 --- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm +++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -1645,10 +1645,6 @@ } void NativeWidgetNSWindowBridge::UpdateWindowGeometry() { - if (fullscreen_controller_ && - fullscreen_controller_->IsInFullscreenTransition()) - return; - gfx::Rect window_in_screen = gfx::ScreenRectFromNSRect([window_ frame]); gfx::Rect content_in_screen = gfx::ScreenRectFromNSRect( [window_ contentRectForFrameRect:[window_ frame]]);
diff --git a/components/search/BUILD.gn b/components/search/BUILD.gn index ae69d747d..df271d3 100644 --- a/components/search/BUILD.gn +++ b/components/search/BUILD.gn
@@ -14,7 +14,6 @@ deps = [ "//base", - "//components/commerce/core:feature_list", "//components/google/core/common", "//components/history/core/browser", "//components/keyed_service/core",
diff --git a/components/search/ntp_features.cc b/components/search/ntp_features.cc index 84c9b02..791a360 100644 --- a/components/search/ntp_features.cc +++ b/components/search/ntp_features.cc
@@ -10,7 +10,6 @@ #include "base/strings/string_split.h" #include "base/time/time.h" #include "build/build_config.h" -#include "components/commerce/core/commerce_feature_list.h" namespace ntp_features { @@ -181,98 +180,6 @@ const char kRealboxMatchSearchboxThemeParam[] = "RealboxMatchSearchboxThemeParam"; -// Params for Discount Consent V2 in the NTP Cart module. -const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[] = - "discount-consent-ntp-variation"; -const base::FeatureParam<int> kNtpChromeCartModuleDiscountConsentNtpVariation{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentNtpVariationParam, 0}; -const char kNtpChromeCartModuleDiscountConsentReshowTimeParam[] = - "discount-consent-ntp-reshow-time"; -const base::FeatureParam<base::TimeDelta> - kNtpChromeCartModuleDiscountConsentReshowTime{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentReshowTimeParam, base::Days(28)}; -const char kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam[] = - "discount-consent-ntp-max-dismiss-count"; -const base::FeatureParam<int> - kNtpChromeCartModuleDiscountConsentMaxDismissalCount{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam, 1}; - -// String change variation params. -const char kNtpChromeCartModuleDiscountConsentStringChangeContentParam[] = - "string-change-content"; -const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentStringChangeContent{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentStringChangeContentParam, ""}; - -const char kNtpChromeCartModuleDiscountConsentInlineShowCloseButtonParam[] = - "inline-card-show-button"; -const base::FeatureParam<bool> - kNtpChromeCartModuleDiscountConsentInlineShowCloseButton{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentStringChangeContentParam, true}; - -// Discount consent v2 step 1 params. -const char - kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam[] = - "step-one-use-static-content"; -const base::FeatureParam<bool> - kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam, - false}; -const char kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[] = - "step-one-static-content"; -const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam, ""}; -const char kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam[] = - "step-one-one-cart-content"; -const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam, ""}; -const char kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam[] = - "step-one-two-carts-content"; -const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam, ""}; -const char - kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam[] = - "step-one-three-carts-content"; -const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam, - ""}; - -// Discount consent v2 step 2 params. -const char kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam[] = - "step-two-content"; -const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpStepTwoContent{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam, ""}; -const char - kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam[] = - "step-two-different-color"; -const base::FeatureParam<bool> - kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam, - false}; -const char kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam[] = - "dialog-content-title"; -const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle{ - &commerce::kDiscountConsentV2, - kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam, ""}; - base::TimeDelta GetModulesLoadTimeout() { std::string param_value = base::GetFieldTrialParamValueByFeature( kModules, kNtpModulesLoadTimeoutMillisecondsParam);
diff --git a/components/search/ntp_features.h b/components/search/ntp_features.h index 6b29afbb..eb51b2f 100644 --- a/components/search/ntp_features.h +++ b/components/search/ntp_features.h
@@ -111,93 +111,6 @@ // Parameter determining the variations of searchbox theme matching. extern const char kRealboxMatchSearchboxThemeParam[]; -// The following are Feature params for Discount user consent v2. -// This indicates the Discount Consent v2 variation on the NTP Cart module. -enum class DiscountConsentNtpVariation { - // These values are persisted to logs. Entries should not be renumbered and - // numeric values should never be reused. - kDefault = 0, - kStringChange = 1, - kInline = 2, - kDialog = 3, - kMaxValue = kDialog -}; - -// Param indicates the ConsentV2 variation. See DiscountConsentNtpVariation -// enum. -extern const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[]; -extern const base::FeatureParam<int> - kNtpChromeCartModuleDiscountConsentNtpVariation; -// The time interval, after the last dismissal, before reshowing the consent. -extern const char kNtpChromeCartModuleDiscountConsentReshowTimeParam[]; -extern const base::FeatureParam<base::TimeDelta> - kNtpChromeCartModuleDiscountConsentReshowTime; -// The max number of dismisses allowed. -extern const char kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam[]; -extern const base::FeatureParam<int> - kNtpChromeCartModuleDiscountConsentMaxDismissalCount; - -// String change variation params. This string is replacing the content string -// of the v1 consent. -extern const char kNtpChromeCartModuleDiscountConsentStringChangeContentParam[]; -extern const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentStringChangeContent; - -// DiscountConsentNtpVariation::kInline and DiscountConsentNtpVariation::kDialog -// params. This indicate whether the 'x' button should show. -extern const char - kNtpChromeCartModuleDiscountConsentInlineShowCloseButtonParam[]; -extern const base::FeatureParam<bool> - kNtpChromeCartModuleDiscountConsentInlineShowCloseButton; - -// The following are discount consent step 1 params. -// This indicates whether the content in step 1 is a static string that does not -// contain any merchant names. -extern const char - kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam[]; -extern const base::FeatureParam<bool> - kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent; -// This the content string use in step 1 if -// kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent.Get() is true. -extern const char - kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[]; -extern const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent; -// This is a string template that takes in one merchant name, and it's used when -// there is only 1 Chrome Cart. -extern const char - kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam[]; -extern const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart; -// This is a string template that takes in two merchant names, and it's used -// when there are only 2 Chrome Carts. -extern const char - kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam[]; -extern const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts; -// This is a string template that takes in two merchant names, and it's used -// when there are 3 or more Chrome Carts. -extern const char - kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam[]; -extern const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts; - -// The following are discount consent step 2 params. -// This is the content string used in step 2. This is the actual consent string. -extern const char kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam[]; -extern const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpStepTwoContent; -// This is used to indicate whether the backgound-color of step 2 should change. -extern const char - kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam[]; -extern const base::FeatureParam<bool> - kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor; -// This is the content title use in the dialog consent. -extern const char - kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam[]; -extern const base::FeatureParam<std::string> - kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle; - // Returns the timeout after which the load of a module should be aborted. base::TimeDelta GetModulesLoadTimeout();
diff --git a/components/search_engines/search_terms_data.cc b/components/search_engines/search_terms_data.cc index 018730d..17224159b 100644 --- a/components/search_engines/search_terms_data.cc +++ b/components/search_engines/search_terms_data.cc
@@ -114,12 +114,8 @@ const std::string kGoogleHomepageURLPath = std::string("searchbyimage/"); #if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) - // If both LensStandalone and LensRegionSearch features are enabled, - // LensStandalone parameters will take precedence even if the values differ. if (base::FeatureList::IsEnabled(lens::features::kLensStandalone)) { - return lens::features::GetHomepageURLForImageSearch(); - } else if (base::FeatureList::IsEnabled(lens::features::kLensRegionSearch)) { - return lens::features::GetHomepageURLForRegionSearch(); + return lens::features::GetHomepageURLForLens(); } #endif // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
diff --git a/components/security_interstitials/content/insecure_form_navigation_throttle.cc b/components/security_interstitials/content/insecure_form_navigation_throttle.cc index 111af0d0..a1edb4dd 100644 --- a/components/security_interstitials/content/insecure_form_navigation_throttle.cc +++ b/components/security_interstitials/content/insecure_form_navigation_throttle.cc
@@ -96,9 +96,8 @@ // Do not set special error page HTML for insecure forms in subframes; those // are already hard blocked. - if (!handle->IsInMainFrame()) { + if (handle->GetParentFrameOrOuterDocument()) return content::NavigationThrottle::PROCEED; - } url::Origin form_originating_origin = handle->GetInitiatorOrigin().value_or(url::Origin());
diff --git a/components/security_interstitials/content/security_interstitial_tab_helper.cc b/components/security_interstitials/content/security_interstitial_tab_helper.cc index a996c61..2d040dc 100644 --- a/components/security_interstitials/content/security_interstitial_tab_helper.cc +++ b/components/security_interstitials/content/security_interstitial_tab_helper.cc
@@ -54,8 +54,8 @@ content::NavigationHandle* navigation_handle, std::unique_ptr<security_interstitials::SecurityInterstitialPage> blocking_page) { - // An interstitial should not be shown in a prerendered page. The prerender - // should just be canceled. + // An interstitial should not be shown in a prerendered page or in a fenced + // frame. The prerender should just be canceled. DCHECK(navigation_handle->IsInPrimaryMainFrame()); // CreateForWebContents() creates a tab helper if it doesn't yet exist for the
diff --git a/components/segmentation_platform/components_unittests.filter b/components/segmentation_platform/components_unittests.filter index c7b8036..7509640b 100644 --- a/components/segmentation_platform/components_unittests.filter +++ b/components/segmentation_platform/components_unittests.filter
@@ -9,6 +9,7 @@ ModelExecutionManagerFactoryTest.* ModelExecutionManagerTest.* ModelExecutionSchedulerTest.* +OptimizationGuideSegmentationModelProviderTest.* SegmentationModelExecutorTest.* SegmentationPlatformDummyUkmManagerTest.* SegmentationPlatformServiceImplEmptyConfigTest.*
diff --git a/components/segmentation_platform/internal/BUILD.gn b/components/segmentation_platform/internal/BUILD.gn index 741e6bd..062d542f 100644 --- a/components/segmentation_platform/internal/BUILD.gn +++ b/components/segmentation_platform/internal/BUILD.gn
@@ -79,6 +79,8 @@ "segmentation_platform_service_impl.h", "segmentation_ukm_helper.cc", "segmentation_ukm_helper.h", + "selection/segment_result_provider.cc", + "selection/segment_result_provider.h", "selection/segment_score_provider.cc", "selection/segment_score_provider.h", "selection/segment_selector.h", @@ -117,6 +119,7 @@ "//components/history/core/browser:browser", "//components/keyed_service/core", "//components/leveldb_proto", + "//components/optimization_guide:machine_learning_tflite_buildflags", "//components/prefs", "//components/segmentation_platform/internal/proto", "//components/segmentation_platform/public", @@ -127,25 +130,15 @@ "//url:url", ] - public_deps = [ - "//components/optimization_guide/core", - "//components/optimization_guide/proto:optimization_guide_proto", - ] + public_deps = + [ "//components/optimization_guide/proto:optimization_guide_proto" ] if (build_with_tflite_lib) { sources += [ "execution/model_execution_manager_impl.cc", "execution/model_execution_manager_impl.h", - "execution/segmentation_model_executor.cc", - "execution/segmentation_model_executor.h", - "execution/segmentation_model_handler.cc", - "execution/segmentation_model_handler.h", ] - - deps += [ - "//third_party/tflite:tflite_public_headers", - "//third_party/tflite_support", - ] + deps += [ "//third_party/tflite:tflite_public_headers" ] } if (is_android) { @@ -158,6 +151,26 @@ } } +if (build_with_tflite_lib) { + static_library("optimization_guide_segmentation_handler") { + sources = [ + "execution/optimization_guide/optimization_guide_segmentation_model_handler.cc", + "execution/optimization_guide/optimization_guide_segmentation_model_handler.h", + "execution/optimization_guide/optimization_guide_segmentation_model_provider.cc", + "execution/optimization_guide/optimization_guide_segmentation_model_provider.h", + "execution/optimization_guide/segmentation_model_executor.cc", + "execution/optimization_guide/segmentation_model_executor.h", + ] + deps = [ + ":internal", + "//base", + "//components/optimization_guide/core", + "//components/segmentation_platform/internal/proto", + "//components/segmentation_platform/public", + ] + } +} + source_set("unit_tests") { testonly = true @@ -195,7 +208,10 @@ "execution/mock_feature_aggregator.h", "execution/mock_feature_list_query_processor.cc", "execution/mock_feature_list_query_processor.h", + "execution/mock_model_provider.cc", + "execution/mock_model_provider.h", "execution/model_execution_manager_factory_unittest.cc", + "execution/query_processor.h", "mock_ukm_data_manager.cc", "mock_ukm_data_manager.h", "scheduler/model_execution_scheduler_unittest.cc", @@ -223,6 +239,7 @@ "//base/test:test_support", "//components/history/core/browser:browser", "//components/leveldb_proto:test_support", + "//components/optimization_guide/core", "//components/optimization_guide/core:test_support", "//components/prefs", "//components/prefs:test_support", @@ -243,9 +260,13 @@ # tests in //components/segmentation_platform/components_unittests.filter sources += [ "execution/model_execution_manager_impl_unittest.cc", - "execution/segmentation_model_executor_unittest.cc", + "execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc", + "execution/optimization_guide/segmentation_model_executor_unittest.cc", ] - deps += [ ":unit_tests_bundle_data" ] + deps += [ + ":optimization_guide_segmentation_handler", + ":unit_tests_bundle_data", + ] } }
diff --git a/components/segmentation_platform/internal/execution/custom_input_processor.cc b/components/segmentation_platform/internal/execution/custom_input_processor.cc index 09925052..09a20c3 100644 --- a/components/segmentation_platform/internal/execution/custom_input_processor.cc +++ b/components/segmentation_platform/internal/execution/custom_input_processor.cc
@@ -4,6 +4,7 @@ #include "components/segmentation_platform/internal/execution/custom_input_processor.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "components/segmentation_platform/internal/database/metadata_utils.h" #include "components/segmentation_platform/internal/database/ukm_types.h" #include "components/segmentation_platform/internal/execution/feature_processor_state.h"
diff --git a/components/segmentation_platform/internal/execution/feature_processor_state.cc b/components/segmentation_platform/internal/execution/feature_processor_state.cc index a9e3ea9..e794fd6 100644 --- a/components/segmentation_platform/internal/execution/feature_processor_state.cc +++ b/components/segmentation_platform/internal/execution/feature_processor_state.cc
@@ -4,6 +4,7 @@ #include "components/segmentation_platform/internal/execution/feature_processor_state.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "components/segmentation_platform/internal/database/ukm_types.h" namespace segmentation_platform {
diff --git a/components/segmentation_platform/internal/execution/mock_model_provider.cc b/components/segmentation_platform/internal/execution/mock_model_provider.cc new file mode 100644 index 0000000..23efe60d --- /dev/null +++ b/components/segmentation_platform/internal/execution/mock_model_provider.cc
@@ -0,0 +1,57 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/segmentation_platform/internal/execution/mock_model_provider.h" + +#include <utility> +#include "base/callback.h" + +namespace segmentation_platform { + +namespace { + +using ::testing::_; +using ::testing::Invoke; + +// Stores the client callbacks to |data|. +void StoreClientCallback( + optimization_guide::proto::OptimizationTarget segment_id, + TestModelProviderFactory::Data* data, + const ModelProvider::ModelUpdatedCallback& model_updated_callback) { + data->model_providers_callbacks.emplace( + std::make_pair(segment_id, model_updated_callback)); +} + +} // namespace + +MockModelProvider::MockModelProvider( + optimization_guide::proto::OptimizationTarget segment_id, + base::RepeatingCallback<void(const ModelProvider::ModelUpdatedCallback&)> + get_client_callback) + : ModelProvider(segment_id), get_client_callback_(get_client_callback) { + ON_CALL(*this, InitAndFetchModel(_)) + .WillByDefault( + Invoke([&](const ModelUpdatedCallback& model_updated_callback) { + get_client_callback_.Run(model_updated_callback); + })); +} +MockModelProvider::~MockModelProvider() = default; + +TestModelProviderFactory::Data::Data() = default; +TestModelProviderFactory::Data::~Data() = default; + +std::unique_ptr<ModelProvider> TestModelProviderFactory::CreateProvider( + optimization_guide::proto::OptimizationTarget segment_id) { + auto provider = std::make_unique<MockModelProvider>( + segment_id, base::BindRepeating(&StoreClientCallback, segment_id, data_)); + data_->model_providers.emplace(std::make_pair(segment_id, provider.get())); + return provider; +} + +std::unique_ptr<ModelProvider> TestModelProviderFactory::CreateDefaultProvider( + optimization_guide::proto::OptimizationTarget) { + return nullptr; +} + +} // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/mock_model_provider.h b/components/segmentation_platform/internal/execution/mock_model_provider.h new file mode 100644 index 0000000..7bf0ce6 --- /dev/null +++ b/components/segmentation_platform/internal/execution/mock_model_provider.h
@@ -0,0 +1,83 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_MODEL_PROVIDER_H_ +#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_MODEL_PROVIDER_H_ + +#include <map> +#include <memory> +#include "base/memory/raw_ptr.h" +#include "components/segmentation_platform/public/model_provider.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace segmentation_platform { + +// Mock model provider for testing, to be used with TestModelProviderFactory. +class MockModelProvider : public ModelProvider { + public: + MockModelProvider( + optimization_guide::proto::OptimizationTarget segment_id, + base::RepeatingCallback<void(const ModelProvider::ModelUpdatedCallback&)> + get_client_callback); + ~MockModelProvider() override; + + MOCK_METHOD(void, + ExecuteModelWithInput, + (const std::vector<float>& input, + base::OnceCallback<void(const absl::optional<float>&)> callback), + (override)); + + MOCK_METHOD(void, + InitAndFetchModel, + (const ModelUpdatedCallback& model_updated_callback), + (override)); + + MOCK_METHOD(bool, ModelAvailable, (), (override)); + + private: + base::RepeatingCallback<void(const ModelProvider::ModelUpdatedCallback&)> + get_client_callback_; +}; + +// Test factory for providers, keeps track of model requests, but does not run +// the model callbacks. +class TestModelProviderFactory : public ModelProviderFactory { + public: + // List of model requests given to the providers. + struct Data { + Data(); + ~Data(); + + // Map of targets to model providers, added when provider is created. The + // list is not cleared when providers are destroyed. + std::map<optimization_guide::proto::OptimizationTarget, MockModelProvider*> + model_providers; + // Map from target to updated callback, recorded when InitAndFetchModel() + // was called on any provider. + std::map<optimization_guide::proto::OptimizationTarget, + ModelProvider::ModelUpdatedCallback> + model_providers_callbacks; + }; + + // Records requests to `data`. `data` is not owned, and the caller must ensure + // it is valid when the factory or provider is in use. Note that providers can + // live longer than factory. + explicit TestModelProviderFactory(Data* data) : data_(data) {} + + // ModelProviderFactory impl, that keeps track of the created provider and + // callbacks in |data_|. + std::unique_ptr<ModelProvider> CreateProvider( + optimization_guide::proto::OptimizationTarget segment_id) override; + + std::unique_ptr<ModelProvider> CreateDefaultProvider( + optimization_guide::proto::OptimizationTarget) override; + + private: + raw_ptr<Data> data_; +}; + +} // namespace segmentation_platform + +#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_MODEL_PROVIDER_H_
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_factory.cc b/components/segmentation_platform/internal/execution/model_execution_manager_factory.cc index 17979f0..cd303396a 100644 --- a/components/segmentation_platform/internal/execution/model_execution_manager_factory.cc +++ b/components/segmentation_platform/internal/execution/model_execution_manager_factory.cc
@@ -11,74 +11,34 @@ #include "base/task/sequenced_task_runner.h" #include "components/optimization_guide/machine_learning_tflite_buildflags.h" #include "components/optimization_guide/proto/models.pb.h" -#include "components/segmentation_platform/internal/execution/feature_list_query_processor.h" #include "components/segmentation_platform/internal/execution/model_execution_manager.h" +#include "components/segmentation_platform/public/model_provider.h" #if BUILDFLAG(BUILD_WITH_TFLITE_LIB) #include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h" -#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h" #else #include "components/segmentation_platform/internal/execution/dummy_model_execution_manager.h" #endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) -namespace base { -class Clock; -} // namespace base - -namespace optimization_guide { -class OptimizationGuideModelProvider; -} // namespace optimization_guide - namespace segmentation_platform { -class SegmentInfoDatabase; -class SignalDatabase; - -#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) - -const char kSegmentationModelMetadataTypeUrl[] = - "type.googleapis.com/" - "google.internal.chrome.optimizationguide.v1.SegmentationModelMetadata"; - -// CreateModelHandler makes it possible to pass in any creator of the -// SegmentationModelHandler, which makes it possible to create mock versions. -std::unique_ptr<SegmentationModelHandler> CreateModelHandler( - optimization_guide::OptimizationGuideModelProvider* model_provider, - scoped_refptr<base::SequencedTaskRunner> background_task_runner, - optimization_guide::proto::OptimizationTarget optimization_target, - const SegmentationModelHandler::ModelUpdatedCallback& - model_updated_callback) { - // Preparing the version data to be sent to server along with the request to - // download the model. - optimization_guide::proto::Any any_metadata; - any_metadata.set_type_url(kSegmentationModelMetadataTypeUrl); - proto::SegmentationModelMetadata model_metadata; - proto::VersionInfo* version_info = model_metadata.mutable_version_info(); - version_info->set_metadata_cur_version( - proto::CurrentVersion::METADATA_VERSION); - model_metadata.SerializeToString(any_metadata.mutable_value()); - return std::make_unique<SegmentationModelHandler>( - model_provider, background_task_runner, optimization_target, - model_updated_callback, std::move(any_metadata)); -} -#endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) std::unique_ptr<ModelExecutionManager> CreateModelExecutionManager( - optimization_guide::OptimizationGuideModelProvider* model_provider, + ModelProviderFactory* model_provider_factory, scoped_refptr<base::SequencedTaskRunner> background_task_runner, - base::flat_set<optimization_guide::proto::OptimizationTarget> segment_ids, + const base::flat_set<optimization_guide::proto::OptimizationTarget>& + segment_ids, base::Clock* clock, SegmentInfoDatabase* segment_database, SignalDatabase* signal_database, FeatureListQueryProcessor* feature_list_query_processor, const ModelExecutionManager::SegmentationModelUpdatedCallback& model_updated_callback) { + // TODO(ssid): The execution manager should always be created, since it can + // run default models. #if BUILDFLAG(BUILD_WITH_TFLITE_LIB) return std::make_unique<ModelExecutionManagerImpl>( - segment_ids, - base::BindRepeating(&CreateModelHandler, model_provider, - background_task_runner), - clock, segment_database, signal_database, feature_list_query_processor, - model_updated_callback); + segment_ids, model_provider_factory, clock, segment_database, + signal_database, feature_list_query_processor, model_updated_callback); #else return std::make_unique<DummyModelExecutionManager>(); #endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_factory.h b/components/segmentation_platform/internal/execution/model_execution_manager_factory.h index 1000adc..5c4c0cb 100644 --- a/components/segmentation_platform/internal/execution/model_execution_manager_factory.h +++ b/components/segmentation_platform/internal/execution/model_execution_manager_factory.h
@@ -17,12 +17,9 @@ class Clock; } // namespace base -namespace optimization_guide { -class OptimizationGuideModelProvider; -} // namespace optimization_guide - namespace segmentation_platform { class FeatureListQueryProcessor; +class ModelProviderFactory; class SegmentInfoDatabase; class SignalDatabase; @@ -31,9 +28,10 @@ // BUILDFLAG(BUILD_WITH_TFLITE_LIB) is not set, in case of the full // implementation provided by ModelExecutionManagerImpl. std::unique_ptr<ModelExecutionManager> CreateModelExecutionManager( - optimization_guide::OptimizationGuideModelProvider* model_provider, + ModelProviderFactory* model_provider_factory, scoped_refptr<base::SequencedTaskRunner> background_task_runner, - base::flat_set<optimization_guide::proto::OptimizationTarget> segment_ids, + const base::flat_set<optimization_guide::proto::OptimizationTarget>& + segment_ids, base::Clock* clock, SegmentInfoDatabase* segment_database, SignalDatabase* signal_database,
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc b/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc index e2ed325..c7de750 100644 --- a/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc +++ b/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc
@@ -13,25 +13,24 @@ #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" #include "base/time/time.h" -#include "components/optimization_guide/core/test_optimization_guide_model_provider.h" #include "components/optimization_guide/proto/models.pb.h" #include "components/segmentation_platform/internal/database/mock_signal_database.h" #include "components/segmentation_platform/internal/database/signal_database.h" #include "components/segmentation_platform/internal/database/test_segment_info_database.h" #include "components/segmentation_platform/internal/execution/feature_aggregator_impl.h" #include "components/segmentation_platform/internal/execution/feature_list_query_processor.h" +#include "components/segmentation_platform/internal/execution/mock_model_provider.h" #include "components/segmentation_platform/internal/execution/model_execution_status.h" #include "testing/gtest/include/gtest/gtest.h" namespace segmentation_platform { + class ModelExecutionManagerFactoryTest : public testing::Test { public: ModelExecutionManagerFactoryTest() = default; ~ModelExecutionManagerFactoryTest() override = default; void SetUp() override { - optimization_guide_model_provider_ = std::make_unique< - optimization_guide::TestOptimizationGuideModelProvider>(); segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>(); feature_list_query_processor_ = std::make_unique<FeatureListQueryProcessor>( &mock_signal_database_, std::make_unique<FeatureAggregatorImpl>()); @@ -45,8 +44,6 @@ } base::test::TaskEnvironment task_environment_; - std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider> - optimization_guide_model_provider_; base::SimpleTestClock test_clock_; std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_; MockSignalDatabase mock_signal_database_; @@ -54,15 +51,29 @@ }; TEST_F(ModelExecutionManagerFactoryTest, CreateModelExecutionManager) { + TestModelProviderFactory::Data data; + TestModelProviderFactory factory(&data); auto model_execution_manager = CreateModelExecutionManager( - optimization_guide_model_provider_.get(), - task_environment_.GetMainThreadTaskRunner(), + &factory, task_environment_.GetMainThreadTaskRunner(), {OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB}, &test_clock_, segment_database_.get(), &mock_signal_database_, feature_list_query_processor_.get(), base::DoNothing()); // This should work regardless of whether a DummyModelExecutionManager or // ModelExecutionManagerImpl is returned. - CHECK(model_execution_manager); + ASSERT_TRUE(model_execution_manager); + + // Executing model with any provider should not crash. + base::RunLoop wait_for_execution; + proto::SegmentInfo segment_info; + segment_info.set_segment_id( + OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB); + model_execution_manager->ExecuteModel( + segment_info, + base::BindOnce( + [](base::RepeatingClosure quit, + const std::pair<float, ModelExecutionStatus>&) { quit.Run(); }, + wait_for_execution.QuitClosure())); + wait_for_execution.Run(); } } // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc index bce51f6..2318c6c2 100644 --- a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc +++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
@@ -12,6 +12,7 @@ #include "base/bind.h" #include "base/callback_helpers.h" #include "base/location.h" +#include "base/logging.h" #include "base/memory/raw_ptr.h" #include "base/task/sequenced_task_runner.h" #include "base/threading/sequenced_task_runner_handle.h" @@ -24,13 +25,13 @@ #include "components/segmentation_platform/internal/execution/feature_list_query_processor.h" #include "components/segmentation_platform/internal/execution/model_execution_manager.h" #include "components/segmentation_platform/internal/execution/model_execution_status.h" -#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h" #include "components/segmentation_platform/internal/proto/aggregation.pb.h" #include "components/segmentation_platform/internal/proto/model_metadata.pb.h" #include "components/segmentation_platform/internal/proto/model_prediction.pb.h" #include "components/segmentation_platform/internal/proto/types.pb.h" #include "components/segmentation_platform/internal/segmentation_ukm_helper.h" #include "components/segmentation_platform/internal/stats.h" +#include "components/segmentation_platform/public/model_provider.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/perfetto/include/perfetto/tracing/track.h" @@ -74,7 +75,8 @@ std::unique_ptr<ModelExecutionTraceEvent> trace_event; OptimizationTarget segment_id; - raw_ptr<SegmentationModelHandler> model_handler = nullptr; + int64_t model_version = 0; + raw_ptr<ModelProvider> model_provider = nullptr; ModelExecutionCallback callback; std::vector<float> input_tensor; base::Time total_execution_start_time; @@ -97,7 +99,7 @@ ModelExecutionManagerImpl::ModelExecutionManagerImpl( const base::flat_set<OptimizationTarget>& segment_ids, - ModelHandlerCreator model_handler_creator, + ModelProviderFactory* model_provider_factory, base::Clock* clock, SegmentInfoDatabase* segment_database, SignalDatabase* signal_database, @@ -109,13 +111,12 @@ model_updated_callback_(model_updated_callback) { feature_list_query_processor_ = feature_list_query_processor; for (OptimizationTarget segment_id : segment_ids) { - model_handlers_.emplace(std::make_pair( - segment_id, - model_handler_creator.Run( - segment_id, - base::BindRepeating( - &ModelExecutionManagerImpl::OnSegmentationModelUpdated, - weak_ptr_factory_.GetWeakPtr())))); + std::unique_ptr<ModelProvider> provider = + model_provider_factory->CreateProvider(segment_id); + provider->InitAndFetchModel(base::BindRepeating( + &ModelExecutionManagerImpl::OnSegmentationModelUpdated, + weak_ptr_factory_.GetWeakPtr())); + model_providers_.emplace(std::make_pair(segment_id, std::move(provider))); } } @@ -125,23 +126,23 @@ const proto::SegmentInfo& segment_info, ModelExecutionCallback callback) { OptimizationTarget segment_id = segment_info.segment_id(); - auto model_handler_it = model_handlers_.find(segment_id); - DCHECK(model_handler_it != model_handlers_.end()); + auto model_provider_it = model_providers_.find(segment_id); + DCHECK(model_provider_it != model_providers_.end()); - SegmentationModelHandler& handler = *model_handler_it->second; + ModelProvider& provider = *model_provider_it->second; // Create an ExecutionState that will stay with this request until it has been // fully processed. auto state = std::make_unique<ExecutionState>(); state->segment_id = segment_id; - state->model_handler = &handler; + state->model_provider = &provider; state->callback = std::move(callback); state->total_execution_start_time = clock_->Now(); ModelExecutionTraceEvent trace_event( "ModelExecutionManagerImpl::ExecuteModel", *state); - if (!handler.ModelAvailable()) { + if (!provider.ModelAvailable()) { RunModelExecutionCallback(std::move(state), 0, ModelExecutionStatus::kSkippedModelNotReady); return; @@ -155,6 +156,7 @@ return; } + state->model_version = segment_info.model_version(); feature_list_query_processor_->ProcessFeatureList( segment_info.model_metadata(), segment_id, clock_->Now(), base::BindOnce( @@ -182,10 +184,10 @@ std::unique_ptr<ExecutionState> state) { ModelExecutionTraceEvent trace_event( "ModelExecutionManagerImpl::ExecuteModel", *state); - auto it = model_handlers_.find(state->segment_id); - DCHECK(it != model_handlers_.end()); + auto it = model_providers_.find(state->segment_id); + DCHECK(it != model_providers_.end()); - SegmentationModelHandler* handler = (*it).second.get(); + ModelProvider* handler = (*it).second.get(); if (VLOG_IS_ON(1)) { std::stringstream log_input; @@ -201,9 +203,9 @@ const_input_tensor); state->model_execution_start_time = clock_->Now(); handler->ExecuteModelWithInput( + const_input_tensor, base::BindOnce(&ModelExecutionManagerImpl::OnModelExecutionComplete, - weak_ptr_factory_.GetWeakPtr(), std::move(state)), - const_input_tensor); + weak_ptr_factory_.GetWeakPtr(), std::move(state))); } void ModelExecutionManagerImpl::OnModelExecutionComplete( @@ -219,10 +221,10 @@ << optimization_guide::proto::OptimizationTarget_Name( state->segment_id); stats::RecordModelExecutionResult(state->segment_id, result.value()); - if (state->model_handler->GetModelInfo()) { + if (state->model_version) { SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult( - state->segment_id, state->model_handler->GetModelInfo()->GetVersion(), - state->input_tensor, result.value()); + state->segment_id, state->model_version, state->input_tensor, + result.value()); } RunModelExecutionCallback(std::move(state), *result, ModelExecutionStatus::kSuccess);
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl.h b/components/segmentation_platform/internal/execution/model_execution_manager_impl.h index bce08664..957140d 100644 --- a/components/segmentation_platform/internal/execution/model_execution_manager_impl.h +++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
@@ -15,12 +15,10 @@ #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/task/sequenced_task_runner.h" -#include "components/optimization_guide/core/model_executor.h" #include "components/optimization_guide/proto/models.pb.h" #include "components/segmentation_platform/internal/database/segment_info_database.h" #include "components/segmentation_platform/internal/database/signal_database.h" #include "components/segmentation_platform/internal/execution/model_execution_manager.h" -#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h" #include "components/segmentation_platform/internal/proto/aggregation.pb.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -30,6 +28,8 @@ namespace segmentation_platform { class FeatureListQueryProcessor; +class ModelProvider; +class ModelProviderFactory; class SignalDatabase; namespace proto { @@ -51,14 +51,9 @@ // so the SegmentationModelHandler instances can be created early. class ModelExecutionManagerImpl : public ModelExecutionManager { public: - using ModelHandlerCreator = - base::RepeatingCallback<std::unique_ptr<SegmentationModelHandler>( - optimization_guide::proto::OptimizationTarget, - const SegmentationModelHandler::ModelUpdatedCallback&)>; - ModelExecutionManagerImpl( const base::flat_set<OptimizationTarget>& segment_ids, - ModelHandlerCreator model_handler_creator, + ModelProviderFactory* model_provider_factory, base::Clock* clock, SegmentInfoDatabase* segment_database, SignalDatabase* signal_database, @@ -135,8 +130,8 @@ bool success); // All the relevant handlers for each of the segments. - std::map<OptimizationTarget, std::unique_ptr<SegmentationModelHandler>> - model_handlers_; + // TODO(ssid): Move the ownership of providers outside this class. + std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> model_providers_; // Used to access the current time. raw_ptr<base::Clock> clock_;
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc index e4f8356..2b197ca9 100644 --- a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc +++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
@@ -18,24 +18,25 @@ #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" #include "base/time/time.h" -#include "components/optimization_guide/core/test_optimization_guide_model_provider.h" #include "components/optimization_guide/proto/models.pb.h" #include "components/segmentation_platform/internal/database/metadata_utils.h" #include "components/segmentation_platform/internal/database/mock_signal_database.h" #include "components/segmentation_platform/internal/database/signal_database.h" #include "components/segmentation_platform/internal/database/test_segment_info_database.h" #include "components/segmentation_platform/internal/execution/feature_list_query_processor.h" +#include "components/segmentation_platform/internal/execution/mock_model_provider.h" #include "components/segmentation_platform/internal/execution/model_execution_manager.h" #include "components/segmentation_platform/internal/execution/model_execution_status.h" -#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h" #include "components/segmentation_platform/internal/proto/aggregation.pb.h" #include "components/segmentation_platform/internal/proto/model_metadata.pb.h" #include "components/segmentation_platform/internal/proto/types.pb.h" +#include "components/segmentation_platform/public/model_provider.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::base::test::RunOnceCallback; using testing::_; +using testing::Invoke; using testing::Return; using testing::SaveArg; using testing::SetArgReferee; @@ -77,29 +78,6 @@ (override)); }; -class MockSegmentationModelHandler : public SegmentationModelHandler { - public: - MockSegmentationModelHandler( - optimization_guide::OptimizationGuideModelProvider* model_provider, - scoped_refptr<base::SequencedTaskRunner> background_task_runner, - optimization_guide::proto::OptimizationTarget optimization_target, - const SegmentationModelHandler::ModelUpdatedCallback& - model_updated_callback) - : SegmentationModelHandler(model_provider, - background_task_runner, - optimization_target, - model_updated_callback, - absl::nullopt) {} - - MOCK_METHOD(void, - ExecuteModelWithInput, - (base::OnceCallback<void(const absl::optional<float>&)> callback, - const std::vector<float>& input), - (override)); - - MOCK_METHOD(bool, ModelAvailable, (), (const override)); -}; - // TODO(ssid): Use mock_feature_list_query_processor.h. class MockFeatureListQueryProcessor : public FeatureListQueryProcessor { public: @@ -119,12 +97,11 @@ class ModelExecutionManagerTest : public testing::Test { public: - ModelExecutionManagerTest() = default; + ModelExecutionManagerTest() + : model_provider_factory_(&model_provider_data_) {} ~ModelExecutionManagerTest() override = default; void SetUp() override { - optimization_guide_model_provider_ = std::make_unique< - optimization_guide::TestOptimizationGuideModelProvider>(); segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>(); signal_database_ = std::make_unique<MockSignalDatabase>(); clock_.SetNow(base::Time::Now()); @@ -132,7 +109,7 @@ void TearDown() override { model_execution_manager_.reset(); - // Allow for the SegmentationModelExecutor owned by SegmentationModelHandler + // Allow for the SegmentationModelExecutor owned by ModelProvider // to be destroyed. RunUntilIdle(); } @@ -143,25 +120,8 @@ feature_list_query_processor_ = std::make_unique<MockFeatureListQueryProcessor>(); model_execution_manager_ = std::make_unique<ModelExecutionManagerImpl>( - segment_ids, - base::BindRepeating(&ModelExecutionManagerTest::CreateModelHandler, - base::Unretained(this)), - &clock_, segment_database_.get(), signal_database_.get(), - feature_list_query_processor_.get(), callback); - } - - std::unique_ptr<SegmentationModelHandler> CreateModelHandler( - optimization_guide::proto::OptimizationTarget segment_id, - const SegmentationModelHandler::ModelUpdatedCallback& - model_updated_callback) { - auto handler = std::make_unique<MockSegmentationModelHandler>( - optimization_guide_model_provider_.get(), - task_environment_.GetMainThreadTaskRunner(), segment_id, - model_updated_callback); - model_handlers_.emplace(std::make_pair(segment_id, handler.get())); - model_handlers_callbacks_.emplace( - std::make_pair(segment_id, model_updated_callback)); - return handler; + segment_ids, &model_provider_factory_, &clock_, segment_database_.get(), + signal_database_.get(), feature_list_query_processor_.get(), callback); } void RunUntilIdle() { task_environment_.RunUntilIdle(); } @@ -186,22 +146,21 @@ std::move(closure).Run(); } - MockSegmentationModelHandler& FindHandler( + MockModelProvider& FindHandler( optimization_guide::proto::OptimizationTarget segment_id) { - return *(*model_handlers_.find(segment_id)).second; + return *(*model_provider_data_.model_providers.find(segment_id)).second; } base::Time StartTime(base::TimeDelta bucket_duration, int64_t bucket_count) { return clock_.Now() - bucket_duration * bucket_count; } + protected: base::test::TaskEnvironment task_environment_; - std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider> - optimization_guide_model_provider_; - std::map<OptimizationTarget, MockSegmentationModelHandler*> model_handlers_; - std::map<OptimizationTarget, SegmentationModelHandler::ModelUpdatedCallback> - model_handlers_callbacks_; + TestModelProviderFactory::Data model_provider_data_; + TestModelProviderFactory model_provider_factory_; + base::SimpleTestClock clock_; std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_; std::unique_ptr<MockSignalDatabase> signal_database_; @@ -268,8 +227,8 @@ // SegmentInfoDatabase, nor invokes the callback. EXPECT_CALL(*mock_segment_database_ptr, GetSegmentInfo(_, _)).Times(0); EXPECT_CALL(callback, Run(_)).Times(0); - model_handlers_callbacks_[segment_id].Run(segment_id, metadata, - kModelVersion); + model_provider_data_.model_providers_callbacks[segment_id].Run( + segment_id, metadata, kModelVersion); } TEST_F(ModelExecutionManagerTest, OnSegmentationModelUpdatedNoOldMetadata) { @@ -284,8 +243,8 @@ metadata.set_bucket_duration(42u); metadata.set_time_unit(proto::TimeUnit::DAY); EXPECT_CALL(callback, Run(_)).WillOnce(SaveArg<0>(&segment_info)); - model_handlers_callbacks_[segment_id].Run(segment_id, metadata, - kModelVersion); + model_provider_data_.model_providers_callbacks[segment_id].Run( + segment_id, metadata, kModelVersion); // Verify that the resulting callback was invoked correctly. EXPECT_EQ(segment_id, segment_info.segment_id()); @@ -367,8 +326,8 @@ // Invoke the callback and store the resulting invocation of the outer // callback for verification. EXPECT_CALL(callback, Run(_)).WillOnce(SaveArg<0>(&segment_info)); - model_handlers_callbacks_[segment_id].Run(segment_id, metadata, - kModelVersion); + model_provider_data_.model_providers_callbacks[segment_id].Run( + segment_id, metadata, kModelVersion); // Should now have the metadata from the new proto. EXPECT_EQ(segment_id, segment_info.segment_id()); @@ -462,8 +421,8 @@ EXPECT_CALL(FindHandler(segment_id), ModelAvailable()) .WillRepeatedly(Return(true)); EXPECT_CALL(FindHandler(segment_id), - ExecuteModelWithInput(_, std::vector<float>{1, 2, 3, 4, 5, 6, 7})) - .WillOnce(RunOnceCallback<0>(absl::make_optional(0.8))); + ExecuteModelWithInput(std::vector<float>{1, 2, 3, 4, 5, 6, 7}, _)) + .WillOnce(RunOnceCallback<1>(absl::make_optional(0.8))); ExecuteModel(std::make_pair(0.8, ModelExecutionStatus::kSuccess)); }
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_handler.cc b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc similarity index 75% rename from components/segmentation_platform/internal/execution/segmentation_model_handler.cc rename to components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc index bdd16cc..949d6a60b 100644 --- a/components/segmentation_platform/internal/execution/segmentation_model_handler.cc +++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h" +#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h" #include <memory> #include <vector> @@ -10,18 +10,19 @@ #include "components/optimization_guide/core/model_executor.h" #include "components/optimization_guide/proto/common_types.pb.h" #include "components/optimization_guide/proto/models.pb.h" -#include "components/segmentation_platform/internal/execution/segmentation_model_executor.h" +#include "components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h" #include "components/segmentation_platform/internal/proto/model_metadata.pb.h" #include "components/segmentation_platform/internal/stats.h" namespace segmentation_platform { -SegmentationModelHandler::SegmentationModelHandler( - optimization_guide::OptimizationGuideModelProvider* model_provider, - scoped_refptr<base::SequencedTaskRunner> background_task_runner, - optimization_guide::proto::OptimizationTarget optimization_target, - const ModelUpdatedCallback& model_updated_callback, - absl::optional<optimization_guide::proto::Any>&& model_metadata) +OptimizationGuideSegmentationModelHandler:: + OptimizationGuideSegmentationModelHandler( + optimization_guide::OptimizationGuideModelProvider* model_provider, + scoped_refptr<base::SequencedTaskRunner> background_task_runner, + optimization_guide::proto::OptimizationTarget optimization_target, + const ModelUpdatedCallback& model_updated_callback, + absl::optional<optimization_guide::proto::Any>&& model_metadata) : optimization_guide::ModelHandler<float, const std::vector<float>&>( model_provider, background_task_runner, @@ -34,9 +35,10 @@ stats::SegmentationModelAvailability::kModelHandlerCreated); } -SegmentationModelHandler::~SegmentationModelHandler() = default; +OptimizationGuideSegmentationModelHandler:: + ~OptimizationGuideSegmentationModelHandler() = default; -void SegmentationModelHandler::OnModelUpdated( +void OptimizationGuideSegmentationModelHandler::OnModelUpdated( optimization_guide::proto::OptimizationTarget optimization_target, const optimization_guide::ModelInfo& model_info) { // First invoke parent to update internal status.
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_handler.h b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h similarity index 71% rename from components/segmentation_platform/internal/execution/segmentation_model_handler.h rename to components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h index 4a07b9b3..4eb60ff 100644 --- a/components/segmentation_platform/internal/execution/segmentation_model_handler.h +++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_HANDLER_H_ -#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_HANDLER_H_ +#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_HANDLER_H_ +#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_HANDLER_H_ #include <memory> #include <vector> @@ -25,7 +25,7 @@ // its parent class. // See documentation for SegmentationModelExecutor for details on the // requirements for the ML model and the inputs to execution. -class SegmentationModelHandler +class OptimizationGuideSegmentationModelHandler : public optimization_guide::ModelHandler<float, const std::vector<float>&> { public: @@ -34,18 +34,20 @@ proto::SegmentationModelMetadata, int64_t)>; - explicit SegmentationModelHandler( + explicit OptimizationGuideSegmentationModelHandler( optimization_guide::OptimizationGuideModelProvider* model_provider, scoped_refptr<base::SequencedTaskRunner> background_task_runner, optimization_guide::proto::OptimizationTarget optimization_target, const ModelUpdatedCallback& model_updated_callback, absl::optional<optimization_guide::proto::Any>&& model_metadata); - ~SegmentationModelHandler() override; + ~OptimizationGuideSegmentationModelHandler() override; // Disallow copy/assign. - SegmentationModelHandler(const SegmentationModelHandler&) = delete; - SegmentationModelHandler& operator=(const SegmentationModelHandler&) = delete; + OptimizationGuideSegmentationModelHandler( + const OptimizationGuideSegmentationModelHandler&) = delete; + OptimizationGuideSegmentationModelHandler& operator=( + const OptimizationGuideSegmentationModelHandler&) = delete; // optimization_guide::ModelHandler overrides. void OnModelUpdated( @@ -60,4 +62,4 @@ } // namespace segmentation_platform -#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_HANDLER_H_ +#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_HANDLER_H_
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc new file mode 100644 index 0000000..be5e59da --- /dev/null +++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc
@@ -0,0 +1,80 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h" + +#include <memory> +#include <vector> + +#include "base/threading/sequenced_task_runner_handle.h" +#include "components/optimization_guide/core/model_executor.h" +#include "components/optimization_guide/proto/common_types.pb.h" +#include "components/optimization_guide/proto/models.pb.h" +#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h" +#include "components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h" +#include "components/segmentation_platform/internal/proto/model_metadata.pb.h" +#include "components/segmentation_platform/internal/stats.h" + +namespace segmentation_platform { + +namespace { + +const char kSegmentationModelMetadataTypeUrl[] = + "type.googleapis.com/" + "google.internal.chrome.optimizationguide.v1.SegmentationModelMetadata"; + +absl::optional<optimization_guide::proto::Any> GetModelFetchConfig() { + // Preparing the version data to be sent to server along with the request to + // download the model. + optimization_guide::proto::Any any_metadata; + any_metadata.set_type_url(kSegmentationModelMetadataTypeUrl); + proto::SegmentationModelMetadata model_metadata; + proto::VersionInfo* version_info = model_metadata.mutable_version_info(); + version_info->set_metadata_cur_version( + proto::CurrentVersion::METADATA_VERSION); + model_metadata.SerializeToString(any_metadata.mutable_value()); + return any_metadata; +} + +} // namespace + +OptimizationGuideSegmentationModelProvider:: + OptimizationGuideSegmentationModelProvider( + optimization_guide::OptimizationGuideModelProvider* model_provider, + scoped_refptr<base::SequencedTaskRunner> background_task_runner, + optimization_guide::proto::OptimizationTarget optimization_target) + : ModelProvider(optimization_target), + model_provider_(model_provider), + background_task_runner_(background_task_runner) {} + +OptimizationGuideSegmentationModelProvider:: + ~OptimizationGuideSegmentationModelProvider() = default; + +void OptimizationGuideSegmentationModelProvider::InitAndFetchModel( + const ModelUpdatedCallback& model_updated_callback) { + DCHECK(!model_handler_); + model_handler_ = std::make_unique<OptimizationGuideSegmentationModelHandler>( + model_provider_, background_task_runner_, optimization_target_, + model_updated_callback, GetModelFetchConfig()); +} + +void OptimizationGuideSegmentationModelProvider::ExecuteModelWithInput( + const std::vector<float>& inputs, + ExecutionCallback callback) { + if (!model_handler_) { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt)); + return; + } + model_handler_->ExecuteModelWithInput(std::move(callback), inputs); +} + +bool OptimizationGuideSegmentationModelProvider::ModelAvailable() { + if (!model_handler_) { + return false; + } + return model_handler_->ModelAvailable(); +} + +} // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h new file mode 100644 index 0000000..2e7bdee --- /dev/null +++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h
@@ -0,0 +1,60 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_PROVIDER_H_ +#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_PROVIDER_H_ + +#include <memory> +#include <vector> + +#include "components/optimization_guide/core/model_handler.h" +#include "components/optimization_guide/proto/models.pb.h" +#include "components/segmentation_platform/public/model_provider.h" + +namespace optimization_guide { +class OptimizationGuideSegmentationModelProvider; +} // namespace optimization_guide + +namespace segmentation_platform { + +class OptimizationGuideSegmentationModelHandler; + +// Model provider implementation that uses optimization guide to fetch and +// execute models. +class OptimizationGuideSegmentationModelProvider : public ModelProvider { + public: + OptimizationGuideSegmentationModelProvider( + optimization_guide::OptimizationGuideModelProvider* model_provider, + scoped_refptr<base::SequencedTaskRunner> background_task_runner, + optimization_guide::proto::OptimizationTarget optimization_target); + + ~OptimizationGuideSegmentationModelProvider() override; + + // Disallow copy/assign. + OptimizationGuideSegmentationModelProvider( + const OptimizationGuideSegmentationModelProvider&) = delete; + OptimizationGuideSegmentationModelProvider& operator=( + const OptimizationGuideSegmentationModelProvider&) = delete; + + // ModelProvider impl: + void InitAndFetchModel( + const ModelUpdatedCallback& model_updated_callback) override; + void ExecuteModelWithInput(const std::vector<float>& inputs, + ExecutionCallback callback) override; + bool ModelAvailable() override; + + OptimizationGuideSegmentationModelHandler& model_handler_for_testing() { + return *model_handler_; + } + + private: + raw_ptr<optimization_guide::OptimizationGuideModelProvider> model_provider_; + scoped_refptr<base::SequencedTaskRunner> background_task_runner_; + + std::unique_ptr<OptimizationGuideSegmentationModelHandler> model_handler_; +}; + +} // namespace segmentation_platform + +#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_PROVIDER_H_
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc new file mode 100644 index 0000000..5586081 --- /dev/null +++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc
@@ -0,0 +1,164 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h" + +#include "base/test/task_environment.h" +#include "base/test/test_simple_task_runner.h" +#include "components/optimization_guide/core/optimization_guide_util.h" +#include "components/optimization_guide/core/test_optimization_guide_model_provider.h" +#include "components/segmentation_platform/internal/proto/model_metadata.pb.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace segmentation_platform { + +namespace { + +class ModelObserverTracker + : public optimization_guide::TestOptimizationGuideModelProvider { + public: + void AddObserverForOptimizationTargetModel( + optimization_guide::proto::OptimizationTarget target, + const absl::optional<optimization_guide::proto::Any>& model_metadata, + optimization_guide::OptimizationTargetModelObserver* observer) override { + registered_model_metadata_.insert_or_assign(target, model_metadata); + } + + bool DidRegisterForTarget( + optimization_guide::proto::OptimizationTarget target) const { + auto it = registered_model_metadata_.find(target); + if (it == registered_model_metadata_.end()) + return false; + const auto& model_metadata = registered_model_metadata_.at(target); + + EXPECT_TRUE(model_metadata); + absl::optional<proto::SegmentationModelMetadata> metadata = + optimization_guide::ParsedAnyMetadata<proto::SegmentationModelMetadata>( + model_metadata.value()); + EXPECT_TRUE(metadata); + EXPECT_EQ(metadata->version_info().metadata_cur_version(), + proto::CurrentVersion::METADATA_VERSION); + + return true; + } + + private: + base::flat_map<optimization_guide::proto::OptimizationTarget, + absl::optional<optimization_guide::proto::Any>> + registered_model_metadata_; +}; + +} // namespace + +class OptimizationGuideSegmentationModelProviderTest : public testing::Test { + public: + OptimizationGuideSegmentationModelProviderTest() = default; + ~OptimizationGuideSegmentationModelProviderTest() override = default; + + void SetUp() override { + task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>(); + model_observer_tracker_ = std::make_unique<ModelObserverTracker>(); + } + + void TearDown() override { + model_observer_tracker_.reset(); + task_runner_->RunPendingTasks(); + } + + std::unique_ptr<OptimizationGuideSegmentationModelProvider> + CreateModelProvider(optimization_guide::proto::OptimizationTarget target) { + return std::make_unique<OptimizationGuideSegmentationModelProvider>( + model_observer_tracker_.get(), task_runner_, target); + } + + protected: + base::test::TaskEnvironment task_environment_; + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + + std::unique_ptr<ModelObserverTracker> model_observer_tracker_; +}; + +TEST_F(OptimizationGuideSegmentationModelProviderTest, InitAndFetchModel) { + std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider = + CreateModelProvider(optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_SHARE); + + // Not initialized yet. + EXPECT_FALSE(model_observer_tracker_->DidRegisterForTarget( + optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_SHARE)); + + // Init should register observer. + provider->InitAndFetchModel(base::DoNothing()); + EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget( + optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_SHARE)); + + // Different target does not register yet. + EXPECT_FALSE(model_observer_tracker_->DidRegisterForTarget( + optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_VOICE)); + + // Initialize voice provider. + std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider2 = + CreateModelProvider(optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_VOICE); + provider2->InitAndFetchModel(base::DoNothing()); + + // 2 observers should be available: + EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget( + optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_SHARE)); + + EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget( + optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_VOICE)); +} + +TEST_F(OptimizationGuideSegmentationModelProviderTest, + ExecuteModelWithoutFetch) { + std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider = + CreateModelProvider(optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_SHARE); + + base::RunLoop run_loop; + std::vector<float> input = {4, 5}; + provider->ExecuteModelWithInput( + input, + base::BindOnce( + [](base::RunLoop* run_loop, const absl::optional<float>& output) { + EXPECT_FALSE(output.has_value()); + run_loop->Quit(); + }, + &run_loop)); + run_loop.Run(); +} + +TEST_F(OptimizationGuideSegmentationModelProviderTest, ExecuteModelWithFetch) { + std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider = + CreateModelProvider(optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_SHARE); + provider->InitAndFetchModel(base::DoNothing()); + EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget( + optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_SEGMENTATION_SHARE)); + + base::RunLoop run_loop; + std::vector<float> input = {4, 5}; + provider->ExecuteModelWithInput( + input, + base::BindOnce( + [](base::RunLoop* run_loop, const absl::optional<float>& output) { + // TODO(ssid): Consider using a mock executor to return results. + // This failure is caused by not no TFLite model being loaded in the + // opt-guide executor. + EXPECT_FALSE(output.has_value()); + run_loop->Quit(); + }, + &run_loop)); + task_runner_->RunPendingTasks(); + run_loop.Run(); +} + +} // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_executor.cc b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.cc similarity index 94% rename from components/segmentation_platform/internal/execution/segmentation_model_executor.cc rename to components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.cc index 3b7396d..faaaf0f 100644 --- a/components/segmentation_platform/internal/execution/segmentation_model_executor.cc +++ b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/segmentation_platform/internal/execution/segmentation_model_executor.h" +#include "components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h" #include <vector>
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_executor.h b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h similarity index 83% rename from components/segmentation_platform/internal/execution/segmentation_model_executor.h rename to components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h index 33e885e..02cbdcd 100644 --- a/components/segmentation_platform/internal/execution/segmentation_model_executor.h +++ b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_EXECUTOR_H_ -#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_EXECUTOR_H_ +#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_EXECUTOR_H_ +#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_EXECUTOR_H_ #include <memory> #include <vector> @@ -45,4 +45,4 @@ } // namespace segmentation_platform -#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_EXECUTOR_H_ +#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_EXECUTOR_H_
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_executor_unittest.cc b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc similarity index 76% rename from components/segmentation_platform/internal/execution/segmentation_model_executor_unittest.cc rename to components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc index 5944bbdd..87b8409 100644 --- a/components/segmentation_platform/internal/execution/segmentation_model_executor_unittest.cc +++ b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/segmentation_platform/internal/execution/segmentation_model_executor.h" +#include "components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h" #include <memory> @@ -20,9 +20,11 @@ #include "components/optimization_guide/core/test_optimization_guide_model_provider.h" #include "components/optimization_guide/proto/common_types.pb.h" #include "components/optimization_guide/proto/models.pb.h" -#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h" +#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h" +#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h" #include "components/segmentation_platform/internal/proto/model_metadata.pb.h" #include "components/segmentation_platform/internal/proto/model_prediction.pb.h" +#include "components/segmentation_platform/public/model_provider.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -60,26 +62,26 @@ .AppendASCII("segmentation_platform") .AppendASCII("adder.tflite"); - optimization_guide_model_provider_ = std::make_unique< + optimization_guide_segmentation_model_provider_ = std::make_unique< optimization_guide::TestOptimizationGuideModelProvider>(); } void TearDown() override { ResetModelExecutor(); } - void CreateModelExecutor( - SegmentationModelHandler::ModelUpdatedCallback callback) { - if (model_executor_handle_) - model_executor_handle_.reset(); + void CreateModelExecutor(ModelProvider::ModelUpdatedCallback callback) { + if (opt_guide_model_provider_) + opt_guide_model_provider_.reset(); - model_executor_handle_ = std::make_unique<SegmentationModelHandler>( - optimization_guide_model_provider_.get(), - task_environment_.GetMainThreadTaskRunner(), kOptimizationTarget, - callback, absl::nullopt); + opt_guide_model_provider_ = + std::make_unique<OptimizationGuideSegmentationModelProvider>( + optimization_guide_segmentation_model_provider_.get(), + task_environment_.GetMainThreadTaskRunner(), kOptimizationTarget); + opt_guide_model_provider_->InitAndFetchModel(callback); } void ResetModelExecutor() { - model_executor_handle_.reset(); - // Allow for the SegmentationModelExecutor owned by SegmentationModelHandler + opt_guide_model_provider_.reset(); + // Allow for the SegmentationModelExecutor owned by ModelProvider // to be destroyed. RunUntilIdle(); } @@ -101,32 +103,33 @@ "type.googleapis.com/" "segmentation_platform.proto.SegmentationModelMetadata"); } - DCHECK(model_executor_handle_); + DCHECK(opt_guide_model_provider_); auto model_metadata = optimization_guide::TestModelInfoBuilder() .SetModelMetadata(any) .SetModelFilePath(model_file_path_) .SetVersion(kModelVersion) .Build(); - model_executor_handle_->OnModelUpdated(kOptimizationTarget, - *model_metadata); + opt_guide_model_handler().OnModelUpdated(kOptimizationTarget, + *model_metadata); RunUntilIdle(); } - SegmentationModelHandler* model_executor_handle() { - return model_executor_handle_.get(); + OptimizationGuideSegmentationModelHandler& opt_guide_model_handler() { + return opt_guide_model_provider_->model_handler_for_testing(); } void RunUntilIdle() { task_environment_.RunUntilIdle(); } - private: + protected: base::test::TaskEnvironment task_environment_; base::FilePath model_file_path_; std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider> - optimization_guide_model_provider_; + optimization_guide_segmentation_model_provider_; - std::unique_ptr<SegmentationModelHandler> model_executor_handle_; + std::unique_ptr<OptimizationGuideSegmentationModelProvider> + opt_guide_model_provider_; }; TEST_F(SegmentationModelExecutorTest, ExecuteWithLoadedModel) { @@ -154,12 +157,13 @@ PushModelFileToModelExecutor(metadata); model_update_runloop->Run(); - EXPECT_TRUE(model_executor_handle()->ModelAvailable()); + EXPECT_TRUE(opt_guide_model_handler().ModelAvailable()); std::vector<float> input = {4, 5}; std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>(); - model_executor_handle()->ExecuteModelWithInput( + opt_guide_model_provider_->ExecuteModelWithInput( + input, base::BindOnce( [](base::RunLoop* run_loop, const absl::optional<float>& output) { EXPECT_TRUE(output.has_value()); @@ -168,8 +172,7 @@ run_loop->Quit(); }, - run_loop.get()), - input); + run_loop.get())); run_loop->Run(); ResetModelExecutor(); @@ -178,7 +181,7 @@ TEST_F(SegmentationModelExecutorTest, FailToProvideMetadata) { std::unique_ptr<base::RunLoop> model_update_runloop = std::make_unique<base::RunLoop>(); - base::MockCallback<SegmentationModelHandler::ModelUpdatedCallback> callback; + base::MockCallback<ModelProvider::ModelUpdatedCallback> callback; CreateModelExecutor(callback.Get()); EXPECT_CALL(callback, Run(_, _, _)).Times(0); @@ -187,7 +190,7 @@ PushModelFileToModelExecutor(absl::nullopt); model_update_runloop->RunUntilIdle(); - EXPECT_TRUE(model_executor_handle()->ModelAvailable()); + EXPECT_TRUE(opt_guide_model_handler().ModelAvailable()); } } // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/sql_feature_processor.cc b/components/segmentation_platform/internal/execution/sql_feature_processor.cc index 1f9881f..14c50f5b 100644 --- a/components/segmentation_platform/internal/execution/sql_feature_processor.cc +++ b/components/segmentation_platform/internal/execution/sql_feature_processor.cc
@@ -4,6 +4,7 @@ #include "components/segmentation_platform/internal/execution/sql_feature_processor.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "components/segmentation_platform/internal/execution/feature_processor_state.h" namespace segmentation_platform {
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc index b91ef47..475ff5d 100644 --- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc +++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -43,6 +43,7 @@ #include "components/segmentation_platform/internal/stats.h" #include "components/segmentation_platform/internal/ukm_data_manager.h" #include "components/segmentation_platform/public/config.h" +#include "components/segmentation_platform/public/model_provider.h" using optimization_guide::proto::OptimizationTarget; @@ -57,7 +58,7 @@ } // namespace SegmentationPlatformServiceImpl::SegmentationPlatformServiceImpl( - optimization_guide::OptimizationGuideModelProvider* model_provider, + std::unique_ptr<ModelProviderFactory> model_provider, leveldb_proto::ProtoDatabaseProvider* db_provider, const base::FilePath& storage_dir, UkmDataManager* ukm_data_manager, @@ -80,7 +81,7 @@ storage_dir.Append(kSignalStorageConfigDBName), task_runner), ukm_data_manager, - model_provider, + std::move(model_provider), pref_service, history_service, task_runner, @@ -94,13 +95,13 @@ std::unique_ptr<leveldb_proto::ProtoDatabase<proto::SignalStorageConfigs>> signal_storage_config_db, UkmDataManager* ukm_data_manager, - optimization_guide::OptimizationGuideModelProvider* model_provider, + std::unique_ptr<ModelProviderFactory> model_provider, PrefService* pref_service, history::HistoryService* history_service, const scoped_refptr<base::SequencedTaskRunner>& task_runner, base::Clock* clock, std::vector<std::unique_ptr<Config>> configs) - : model_provider_(model_provider), + : model_provider_factory_(std::move(model_provider)), task_runner_(task_runner), clock_(clock), platform_options_(PlatformOptions::CreateDefault()), @@ -138,7 +139,7 @@ std::make_unique<SegmentSelectorImpl>( segment_info_database_.get(), signal_storage_config_.get(), segmentation_result_prefs_.get(), config.get(), clock, - platform_options_); + platform_options_, model_provider_factory_.get()); } proxy_ = std::make_unique<ServiceProxyImpl>(segment_info_database_.get(), @@ -250,7 +251,7 @@ training_data_collector_->OnServiceInitialized(); model_execution_manager_ = CreateModelExecutionManager( - model_provider_, task_runner_, all_segment_ids_, clock_, + model_provider_factory_.get(), task_runner_, all_segment_ids_, clock_, segment_info_database_.get(), signal_database_.get(), feature_list_query_processor_.get(), base::BindRepeating(
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.h b/components/segmentation_platform/internal/segmentation_platform_service_impl.h index 46fd76d8..8bec06f 100644 --- a/components/segmentation_platform/internal/segmentation_platform_service_impl.h +++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
@@ -35,10 +35,6 @@ class ProtoDatabaseProvider; } // namespace leveldb_proto -namespace optimization_guide { -class OptimizationGuideModelProvider; -} // namespace optimization_guide - class PrefService; namespace segmentation_platform { @@ -56,6 +52,7 @@ class HistoryServiceObserver; class ModelExecutionManager; class ModelExecutionSchedulerImpl; +class ModelProviderFactory; class SegmentationResultPrefs; class SegmentInfoDatabase; class SegmentSelectorImpl; @@ -87,7 +84,7 @@ class SegmentationPlatformServiceImpl : public SegmentationPlatformService { public: SegmentationPlatformServiceImpl( - optimization_guide::OptimizationGuideModelProvider* model_provider, + std::unique_ptr<ModelProviderFactory> model_provider, leveldb_proto::ProtoDatabaseProvider* db_provider, const base::FilePath& storage_dir, UkmDataManager* ukm_data_manager, @@ -106,7 +103,7 @@ std::unique_ptr<leveldb_proto::ProtoDatabase<proto::SignalStorageConfigs>> signal_storage_config_db, UkmDataManager* ukm_data_manager, - optimization_guide::OptimizationGuideModelProvider* model_provider, + std::unique_ptr<ModelProviderFactory> model_provider, PrefService* pref_service, history::HistoryService* history_service, const scoped_refptr<base::SequencedTaskRunner>& task_runner, @@ -147,7 +144,8 @@ // Called when service status changes. void OnServiceStatusChanged(); - raw_ptr<optimization_guide::OptimizationGuideModelProvider> model_provider_; + std::unique_ptr<ModelProviderFactory> model_provider_factory_; + scoped_refptr<base::SequencedTaskRunner> task_runner_; raw_ptr<base::Clock> clock_; const PlatformOptions platform_options_;
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc index 3320ad0..65e0889 100644 --- a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc +++ b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
@@ -20,7 +20,6 @@ #include "components/leveldb_proto/public/proto_database_provider.h" #include "components/leveldb_proto/public/shared_proto_database_client_list.h" #include "components/leveldb_proto/testing/fake_db.h" -#include "components/optimization_guide/core/test_optimization_guide_model_provider.h" #include "components/optimization_guide/machine_learning_tflite_buildflags.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/scoped_user_pref_update.h" @@ -31,8 +30,7 @@ #include "components/segmentation_platform/internal/database/signal_storage_config.h" #include "components/segmentation_platform/internal/dummy_ukm_data_manager.h" #include "components/segmentation_platform/internal/execution/feature_aggregator_impl.h" -#include "components/segmentation_platform/internal/execution/model_execution_manager.h" -#include "components/segmentation_platform/internal/execution/model_execution_manager_factory.h" +#include "components/segmentation_platform/internal/execution/mock_model_provider.h" #include "components/segmentation_platform/internal/proto/model_metadata.pb.h" #include "components/segmentation_platform/internal/proto/model_prediction.pb.h" #include "components/segmentation_platform/internal/proto/signal.pb.h" @@ -48,11 +46,8 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) -#include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h" -#endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB - using ::testing::_; +using ::testing::Invoke; namespace segmentation_platform { namespace { @@ -103,8 +98,6 @@ return configs; } -} // namespace - // A mock of the ServiceProxy::Observer. class MockServiceProxyObserver : public ServiceProxy::Observer { public: @@ -118,6 +111,8 @@ (override)); }; +} // namespace + class SegmentationPlatformServiceImplTest : public testing::Test { public: explicit SegmentationPlatformServiceImplTest( @@ -158,8 +153,9 @@ std::make_unique<SegmentationPlatformServiceImpl>( std::move(segment_db), std::move(signal_db), std::move(segment_storage_config_db), ukm_data_manager_.get(), - &model_provider_, &pref_service_, /*history_service=*/nullptr, - task_runner_, &test_clock_, std::move(configs)); + std::make_unique<TestModelProviderFactory>(&model_provider_data_), + &pref_service_, /*history_service=*/nullptr, task_runner_, + &test_clock_, std::move(configs)); segmentation_platform_service_impl_->GetServiceProxy()->AddObserver( &observer_); } @@ -243,11 +239,6 @@ // ModelExecutionManagerImpl is publishing the correct data and whether that // leads to the SegmentationPlatformServiceImpl doing the right thing. #if BUILDFLAG(BUILD_WITH_TFLITE_LIB) - ModelExecutionManagerImpl* mem_impl = - static_cast<ModelExecutionManagerImpl*>( - segmentation_platform_service_impl_->model_execution_manager_ - .get()); - base::HistogramTester histogram_tester; proto::SegmentationModelMetadata metadata; metadata.set_time_unit(proto::TimeUnit::DAY); @@ -266,9 +257,13 @@ // been updated and every time at startup. This will first read the old info // from the database, and then write the merged result of the old and new to // the database. - mem_impl->OnSegmentationModelUpdated( - OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, metadata, - kModelVersion); + ASSERT_TRUE(model_provider_data_.model_providers_callbacks.count( + OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE)); + model_provider_data_ + .model_providers_callbacks + [OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE] + .Run(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, + metadata, kModelVersion); segment_db_->GetCallback(true); segment_db_->UpdateCallback(true); @@ -301,9 +296,13 @@ task_environment_.RunUntilIdle(); segment_db_->LoadCallback(true); - mem_impl->OnSegmentationModelUpdated( - OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE, metadata, - kModelVersion); + ASSERT_TRUE(model_provider_data_.model_providers_callbacks.count( + OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE)); + model_provider_data_ + .model_providers_callbacks + [OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE] + .Run(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE, + metadata, kModelVersion); segment_db_->GetCallback(true); segment_db_->UpdateCallback(true); @@ -351,7 +350,7 @@ raw_ptr<leveldb_proto::test::FakeDB<proto::SignalData>> signal_db_; raw_ptr<leveldb_proto::test::FakeDB<proto::SignalStorageConfigs>> segment_storage_config_db_; - optimization_guide::TestOptimizationGuideModelProvider model_provider_; + TestModelProviderFactory::Data model_provider_data_; TestingPrefServiceSimple pref_service_; base::SimpleTestClock test_clock_; std::unique_ptr<UkmDataManager> ukm_data_manager_;
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.cc b/components/segmentation_platform/internal/selection/segment_result_provider.cc new file mode 100644 index 0000000..f8cec69 --- /dev/null +++ b/components/segmentation_platform/internal/selection/segment_result_provider.cc
@@ -0,0 +1,148 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/segmentation_platform/internal/selection/segment_result_provider.h" + +#include <map> + +#include "base/logging.h" +#include "base/memory/raw_ptr.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "components/segmentation_platform/internal/database/metadata_utils.h" +#include "components/segmentation_platform/internal/database/segment_info_database.h" +#include "components/segmentation_platform/internal/database/signal_storage_config.h" +#include "components/segmentation_platform/internal/proto/model_metadata.pb.h" +#include "components/segmentation_platform/internal/proto/model_prediction.pb.h" + +namespace segmentation_platform { +namespace { + +class SegmentResultProviderImpl : public SegmentResultProvider { + public: + SegmentResultProviderImpl(SegmentInfoDatabase* segment_database, + SignalStorageConfig* signal_storage_config, + ModelProviderFactory* model_provider_factory, + base::Clock* clock, + bool force_refresh_results) + : segment_database_(segment_database), + signal_storage_config_(signal_storage_config), + model_provider_factory_(model_provider_factory), + clock_(clock), + force_refresh_results_(force_refresh_results), + task_runner_(base::SequencedTaskRunnerHandle::Get()) {} + + void GetSegmentResult(OptimizationTarget segment_id, + const std::string& segmentation_key, + SegmentResultCallback callback) override; + + SegmentResultProviderImpl(SegmentResultProviderImpl&) = delete; + SegmentResultProviderImpl& operator=(SegmentResultProviderImpl&) = delete; + + private: + void OnGetSegmentInfo(OptimizationTarget segment_id, + const std::string& segmentation_key, + SegmentResultCallback callback, + absl::optional<proto::SegmentInfo> available_segment); + + const raw_ptr<SegmentInfoDatabase> segment_database_; + const raw_ptr<SignalStorageConfig> signal_storage_config_; + const raw_ptr<ModelProviderFactory> model_provider_factory_; + const raw_ptr<base::Clock> clock_; + const bool force_refresh_results_; + scoped_refptr<base::SequencedTaskRunner> task_runner_; + + base::WeakPtrFactory<SegmentResultProviderImpl> weak_ptr_factory_{this}; +}; + +void SegmentResultProviderImpl::GetSegmentResult( + OptimizationTarget segment_id, + const std::string& segmentation_key, + SegmentResultCallback callback) { + segment_database_->GetSegmentInfo( + segment_id, base::BindOnce(&SegmentResultProviderImpl::OnGetSegmentInfo, + weak_ptr_factory_.GetWeakPtr(), segment_id, + segmentation_key, std::move(callback))); +} + +void SegmentResultProviderImpl::OnGetSegmentInfo( + OptimizationTarget segment_id, + const std::string& segmentation_key, + SegmentResultCallback callback, + absl::optional<proto::SegmentInfo> available_segment) { + // Don't compute results if we don't have enough signals, or don't have + // valid unexpired results for any of the segments. + proto::SegmentInfo* segment_info = nullptr; + if (available_segment) { + segment_info = &available_segment.value(); + } else { + VLOG(1) << __func__ << ": segment=" << OptimizationTarget_Name(segment_id) + << " does not have segment info."; + task_runner_->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), + std::make_unique<SegmentResult>( + ResultState::kSegmentNotAvailable))); + return; + } + + // TODO(ssid): Remove this check since scheduler does this before executing + // the model. + if (!force_refresh_results_ && + !signal_storage_config_->MeetsSignalCollectionRequirement( + segment_info->model_metadata())) { + VLOG(1) << __func__ << ": segment=" + << OptimizationTarget_Name(segment_info->segment_id()) + << " does not meet signal collection requirements."; + task_runner_->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), + std::make_unique<SegmentResult>( + ResultState::kSignalsNotCollected))); + return; + } + + if (metadata_utils::HasExpiredOrUnavailableResult(*segment_info, + clock_->Now())) { + VLOG(1) << __func__ << ": segment=" + << OptimizationTarget_Name(segment_info->segment_id()) + << " has expired or unavailable result."; + task_runner_->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), + std::make_unique<SegmentResult>( + ResultState::kDatabaseScoreNotReady))); + return; + } + + int rank = metadata_utils::ConvertToDiscreteScore( + segmentation_key, segment_info->prediction_result().result(), + segment_info->model_metadata()); + VLOG(1) << __func__ << ": segment=" << OptimizationTarget_Name(segment_id) + << ": result=" << segment_info->prediction_result().result() + << ", rank=" << rank; + + auto result = + std::make_unique<SegmentResult>(ResultState::kSuccessFromDatabase, rank); + task_runner_->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), std::move(result))); +} + +} // namespace + +SegmentResultProvider::SegmentResult::SegmentResult(ResultState state) + : state(state) {} +SegmentResultProvider::SegmentResult::SegmentResult(ResultState state, int rank) + : state(state), rank(rank) {} +SegmentResultProvider::SegmentResult::~SegmentResult() = default; + +// static +std::unique_ptr<SegmentResultProvider> SegmentResultProvider::Create( + SegmentInfoDatabase* segment_database, + SignalStorageConfig* signal_storage_config, + ModelProviderFactory* model_provider_factory, + base::Clock* clock, + bool force_refresh_results) { + return std::make_unique<SegmentResultProviderImpl>( + segment_database, signal_storage_config, model_provider_factory, clock, + force_refresh_results); +} + +} // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.h b/components/segmentation_platform/internal/selection/segment_result_provider.h new file mode 100644 index 0000000..a730ea49 --- /dev/null +++ b/components/segmentation_platform/internal/selection/segment_result_provider.h
@@ -0,0 +1,63 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_RESULT_PROVIDER_H_ +#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_RESULT_PROVIDER_H_ + +#include "base/callback.h" +#include "components/optimization_guide/proto/models.pb.h" +#include "components/segmentation_platform/internal/database/segment_info_database.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace base { +class Clock; +} +namespace segmentation_platform { + +class ModelProviderFactory; +class SignalStorageConfig; + +// Used for retrieving the result of a particular model. +class SegmentResultProvider { + public: + SegmentResultProvider() = default; + virtual ~SegmentResultProvider() = default; + + enum class ResultState { + kUnknown = 0, + kSuccessFromDatabase = 1, + kSegmentNotAvailable = 2, + kSignalsNotCollected = 3, + kDatabaseScoreNotReady = 4, + }; + struct SegmentResult { + explicit SegmentResult(ResultState state); + SegmentResult(ResultState state, int rank); + ~SegmentResult(); + SegmentResult(SegmentResult&) = delete; + SegmentResult& operator=(SegmentResult&) = delete; + + ResultState state = ResultState::kUnknown; + absl::optional<int> rank; + }; + using SegmentResultCallback = + base::OnceCallback<void(std::unique_ptr<SegmentResult>)>; + + // Creates the instance. + static std::unique_ptr<SegmentResultProvider> Create( + SegmentInfoDatabase* segment_info_database, + SignalStorageConfig* signal_storage_config, + ModelProviderFactory* model_provider_factory, + base::Clock* clock, + bool force_refresh_results); + + // Returns latest available score for the segment. + virtual void GetSegmentResult(OptimizationTarget segment_id, + const std::string& segmentation_key, + SegmentResultCallback callback) = 0; +}; + +} // namespace segmentation_platform + +#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_RESULT_PROVIDER_H_
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/components/segmentation_platform/internal/selection/segment_selector_impl.cc index fa78630c..d7034c7e7 100644 --- a/components/segmentation_platform/internal/selection/segment_selector_impl.cc +++ b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
@@ -5,6 +5,8 @@ #include "components/segmentation_platform/internal/selection/segment_selector_impl.h" #include "base/containers/contains.h" +#include "base/containers/flat_map.h" +#include "base/containers/flat_set.h" #include "base/logging.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/clock.h" @@ -15,11 +17,35 @@ #include "components/segmentation_platform/internal/platform_options.h" #include "components/segmentation_platform/internal/proto/model_metadata.pb.h" #include "components/segmentation_platform/internal/proto/model_prediction.pb.h" +#include "components/segmentation_platform/internal/selection/segment_result_provider.h" #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h" #include "components/segmentation_platform/internal/stats.h" #include "components/segmentation_platform/public/config.h" +#include "components/segmentation_platform/public/model_provider.h" namespace segmentation_platform { +namespace { + +stats::SegmentationSelectionFailureReason GetFailureReason( + SegmentResultProvider::ResultState result_state) { + switch (result_state) { + case SegmentResultProvider::ResultState::kUnknown: + case SegmentResultProvider::ResultState::kSuccessFromDatabase: + NOTREACHED(); + return stats::SegmentationSelectionFailureReason::kMaxValue; + case SegmentResultProvider::ResultState::kDatabaseScoreNotReady: + return stats::SegmentationSelectionFailureReason:: + kAtLeastOneSegmentNotReady; + case SegmentResultProvider::ResultState::kSegmentNotAvailable: + return stats::SegmentationSelectionFailureReason:: + kAtLeastOneSegmentNotAvailable; + case SegmentResultProvider::ResultState::kSignalsNotCollected: + return stats::SegmentationSelectionFailureReason:: + kAtLeastOneSegmentSignalsNotCollected; + } +} + +} // namespace using optimization_guide::proto::OptimizationTarget_Name; @@ -29,13 +55,21 @@ SegmentationResultPrefs* result_prefs, const Config* config, base::Clock* clock, - const PlatformOptions& platform_options) - : segment_database_(segment_database), + const PlatformOptions& platform_options, + ModelProviderFactory* model_provider_factory) + : segment_result_provider_(SegmentResultProvider::Create( + segment_database, + signal_storage_config, + model_provider_factory, + clock, + platform_options.force_refresh_results)), + segment_database_(segment_database), signal_storage_config_(signal_storage_config), result_prefs_(result_prefs), config_(config), clock_(clock), - platform_options_(platform_options) { + platform_options_(platform_options), + model_provider_factory_(model_provider_factory) { // Read selected segment from prefs. const auto& selected_segment = result_prefs_->ReadSegmentationResultFromPref(config_->segmentation_key); @@ -71,52 +105,13 @@ if (!base::Contains(config_->segment_ids, segment_id)) return; - segment_database_->GetSegmentInfoForSegments( - config_->segment_ids, - base::BindOnce(&SegmentSelectorImpl::RunSegmentSelection, - weak_ptr_factory_.GetWeakPtr())); -} - -void SegmentSelectorImpl::RunSegmentSelection( - std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> all_segments) { - if (!CanComputeSegmentSelection(*all_segments)) + if (!CanComputeSegmentSelection()) return; - OptimizationTarget selected_segment = FindBestSegment(*all_segments); - UpdateSelectedSegment(selected_segment); + GetRankForNextSegment(std::make_unique<SegmentRanks>()); } -bool SegmentSelectorImpl::CanComputeSegmentSelection( - const SegmentInfoDatabase::SegmentInfoList& all_segments) { - VLOG(1) << __func__ << ": all_segments.size()=" << all_segments.size(); - // Don't compute results if we don't have enough signals, or don't have - // valid unexpired results for any of the segments. - for (const auto& pair : all_segments) { - const proto::SegmentInfo& segment_info = pair.second; - if (!platform_options_.force_refresh_results && - !signal_storage_config_->MeetsSignalCollectionRequirement( - segment_info.model_metadata())) { - VLOG(1) << __func__ << ": segment=" - << OptimizationTarget_Name(segment_info.segment_id()) - << " does not meet signal collection requirements."; - stats::RecordSegmentSelectionFailure( - config_->segmentation_key, stats::SegmentationSelectionFailureReason:: - kAtLeastOneSegmentSignalsNotCollected); - return false; - } - - if (metadata_utils::HasExpiredOrUnavailableResult(segment_info, - clock_->Now())) { - VLOG(1) << __func__ << ": segment=" - << OptimizationTarget_Name(segment_info.segment_id()) - << " has expired or unavailable result."; - stats::RecordSegmentSelectionFailure( - config_->segmentation_key, stats::SegmentationSelectionFailureReason:: - kAtLeastOneSegmentNotReady); - return false; - } - } - +bool SegmentSelectorImpl::CanComputeSegmentSelection() { // Don't compute results if segment selection TTL hasn't expired. const auto& previous_selection = result_prefs_->ReadSegmentationResultFromPref(config_->segmentation_key); @@ -141,31 +136,55 @@ return true; } +void SegmentSelectorImpl::GetRankForNextSegment( + std::unique_ptr<SegmentRanks> ranks) { + for (OptimizationTarget needed_segment : config_->segment_ids) { + if (ranks->count(needed_segment) == 0) { + segment_result_provider_->GetSegmentResult( + needed_segment, config_->segmentation_key, + base::BindOnce(&SegmentSelectorImpl::OnGetResultForSegmentSelection, + weak_ptr_factory_.GetWeakPtr(), std::move(ranks), + needed_segment)); + return; + } + } + OptimizationTarget selected_segment = FindBestSegment(*ranks); + UpdateSelectedSegment(selected_segment); +} + +void SegmentSelectorImpl::OnGetResultForSegmentSelection( + std::unique_ptr<SegmentRanks> ranks, + OptimizationTarget current_segment_id, + std::unique_ptr<SegmentResultProvider::SegmentResult> result) { + if (!result->rank) { + stats::RecordSegmentSelectionFailure(config_->segmentation_key, + GetFailureReason(result->state)); + return; + } + ranks->insert(std::make_pair(current_segment_id, *result->rank)); + + GetRankForNextSegment(std::move(ranks)); +} + OptimizationTarget SegmentSelectorImpl::FindBestSegment( - const SegmentInfoDatabase::SegmentInfoList& all_segments) { - int max_score = 0; - OptimizationTarget max_score_id = + const SegmentRanks& segment_results) { + int max_rank = 0; + OptimizationTarget max_rank_id = OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN; - // Loop through all the results. Convert them to discrete scores. Select the - // one with highest discrete score. - for (const auto& pair : all_segments) { + // Loop through all the results. Convert them to discrete ranks. Select the + // one with highest discrete rank. + for (const auto& pair : segment_results) { OptimizationTarget id = pair.first; - const proto::SegmentInfo& info = pair.second; - int score = metadata_utils::ConvertToDiscreteScore( - config_->segmentation_key, info.prediction_result().result(), - info.model_metadata()); - VLOG(1) << __func__ << ": segment=" << OptimizationTarget_Name(id) - << ": result=" << info.prediction_result().result() - << ", score=" << score; - if (score > max_score) { - max_score = score; - max_score_id = id; - } else if (score == max_score && score > 0) { + int rank = pair.second; + if (rank > max_rank) { + max_rank = rank; + max_rank_id = id; + } else if (rank == max_rank && rank > 0) { // TODO(shaktisahu): Use fallback priority. } } - return max_score_id; + return max_rank_id; } void SegmentSelectorImpl::UpdateSelectedSegment(
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.h b/components/segmentation_platform/internal/selection/segment_selector_impl.h index 4a2c9845..03823b53 100644 --- a/components/segmentation_platform/internal/selection/segment_selector_impl.h +++ b/components/segmentation_platform/internal/selection/segment_selector_impl.h
@@ -5,12 +5,13 @@ #ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_SELECTOR_IMPL_H_ #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_SELECTOR_IMPL_H_ -#include "base/memory/raw_ptr.h" -#include "components/segmentation_platform/internal/selection/segment_selector.h" - #include "base/callback_helpers.h" +#include "base/containers/flat_map.h" +#include "base/memory/raw_ptr.h" #include "components/segmentation_platform/internal/database/segment_info_database.h" #include "components/segmentation_platform/internal/platform_options.h" +#include "components/segmentation_platform/internal/selection/segment_result_provider.h" +#include "components/segmentation_platform/internal/selection/segment_selector.h" #include "components/segmentation_platform/public/segment_selection_result.h" namespace base { @@ -21,6 +22,7 @@ struct Config; class ModelExecutionScheduler; +class ModelProviderFactory; class SegmentationResultPrefs; class SignalStorageConfig; @@ -31,7 +33,8 @@ SegmentationResultPrefs* result_prefs, const Config* config, base::Clock* clock, - const PlatformOptions& platform_options); + const PlatformOptions& platform_options, + ModelProviderFactory* model_provider_factory); ~SegmentSelectorImpl() override; @@ -53,21 +56,29 @@ // For testing. friend class SegmentSelectorTest; - // Helper method to run segment selection if possible. - void RunSegmentSelection( - std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> all_segments); + using SegmentRanks = base::flat_map<OptimizationTarget, int>; // Determines whether segment selection can be run based on whether all // segments have met signal collection requirement, have valid results, and // segment selection TTL has expired. - bool CanComputeSegmentSelection( - const SegmentInfoDatabase::SegmentInfoList& all_segments); + bool CanComputeSegmentSelection(); + + // Gets ranks for each segment from SegmentResultProvider, and then computes + // segment selection. + void GetRankForNextSegment(std::unique_ptr<SegmentRanks> ranks); + + // Callback used to get result from SegmentResultProvider for each segment. + void OnGetResultForSegmentSelection( + std::unique_ptr<SegmentRanks> ranks, + OptimizationTarget current_segment_id, + std::unique_ptr<SegmentResultProvider::SegmentResult> result); // Loops through all segments, performs discrete mapping, honors finch - // supplied tie-breakers, TTL, inertia etc, and finds the highest score. + // supplied tie-breakers, TTL, inertia etc, and finds the highest rank. // Ignores the segments that have no results. - OptimizationTarget FindBestSegment( - const SegmentInfoDatabase::SegmentInfoList& all_segments); + OptimizationTarget FindBestSegment(const SegmentRanks& segment_scores); + + std::unique_ptr<SegmentResultProvider> segment_result_provider_; // The database storing metadata and results. raw_ptr<SegmentInfoDatabase> segment_database_; @@ -86,6 +97,8 @@ const PlatformOptions platform_options_; + const raw_ptr<ModelProviderFactory> model_provider_factory_; + // Segment selection result is read from prefs on init and used for serving // the clients in the current session. SegmentSelectionResult selected_segment_last_session_;
diff --git a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc index e02af0ef..1f271c2 100644 --- a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc +++ b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
@@ -71,7 +71,7 @@ prefs_ = std::make_unique<TestSegmentationResultPrefs>(); segment_selector_ = std::make_unique<SegmentSelectorImpl>( segment_database_.get(), &signal_storage_config_, prefs_.get(), - &config_, &clock_, PlatformOptions::CreateDefault()); + &config_, &clock_, PlatformOptions::CreateDefault(), nullptr); } void GetSelectedSegment(const SegmentSelectionResult& expected) { @@ -106,6 +106,7 @@ void CompleteModelExecution(OptimizationTarget segment_id, float score) { segment_database_->AddPredictionResult(segment_id, score, clock_.Now()); segment_selector_->OnModelExecutionCompleted(segment_id); + task_environment_.RunUntilIdle(); } base::test::TaskEnvironment task_environment_; @@ -137,6 +138,7 @@ clock_.Advance(base::Days(1)); segment_selector_->OnModelExecutionCompleted(segment_id); + task_environment_.RunUntilIdle(); ASSERT_TRUE(prefs_->selection.has_value()); ASSERT_EQ(segment_id2, prefs_->selection->segment_id); } @@ -285,7 +287,9 @@ OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE; OptimizationTarget segment_id1 = OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB; + float mapping0[][2] = {{1.0, 0}}; float mapping1[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}}; + InitializeMetadataForSegment(segment_id0, mapping0, 1); InitializeMetadataForSegment(segment_id1, mapping1, 3); // Set up a selected segment in prefs. @@ -295,7 +299,7 @@ // Construct a segment selector. It should read result from last session. segment_selector_ = std::make_unique<SegmentSelectorImpl>( segment_database_.get(), &signal_storage_config_, prefs_.get(), &config_, - &clock_, PlatformOptions::CreateDefault()); + &clock_, PlatformOptions::CreateDefault(), nullptr); SegmentSelectionResult result; result.segment = segment_id0; @@ -306,8 +310,10 @@ // Add results for a new segment. base::Time result_timestamp = base::Time::Now(); segment_database_->AddPredictionResult(segment_id1, 0.6, result_timestamp); + segment_database_->AddPredictionResult(segment_id0, 0.5, result_timestamp); segment_selector_->OnModelExecutionCompleted(segment_id1); + task_environment_.RunUntilIdle(); ASSERT_TRUE(prefs_->selection.has_value()); ASSERT_EQ(segment_id1, prefs_->selection->segment_id); @@ -337,6 +343,7 @@ clock_.Advance(base::Days(1)); segment_selector_->OnModelExecutionCompleted(segment_id); + task_environment_.RunUntilIdle(); ASSERT_TRUE(prefs_->selection.has_value()); ASSERT_EQ(segment_id2, prefs_->selection->segment_id);
diff --git a/components/segmentation_platform/internal/service_proxy_impl.cc b/components/segmentation_platform/internal/service_proxy_impl.cc index 05cbc4a9..5864dcd7 100644 --- a/components/segmentation_platform/internal/service_proxy_impl.cc +++ b/components/segmentation_platform/internal/service_proxy_impl.cc
@@ -11,8 +11,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" -#include "components/optimization_guide/core/model_util.h" -#include "components/optimization_guide/core/optimization_guide_util.h" #include "components/segmentation_platform/internal/database/metadata_utils.h" #include "components/segmentation_platform/internal/database/signal_storage_config.h" #include "components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h"
diff --git a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc index a35b13e..e538165 100644 --- a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc +++ b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
@@ -67,7 +67,8 @@ result_prefs, config, nullptr, - PlatformOptions::CreateDefault()) {} + PlatformOptions::CreateDefault(), + nullptr) {} ~FakeSegmentSelectorImpl() override = default; void UpdateSelectedSegment(OptimizationTarget new_selection) override {
diff --git a/components/segmentation_platform/internal/stats.h b/components/segmentation_platform/internal/stats.h index 5e1e707..a5d9088 100644 --- a/components/segmentation_platform/internal/stats.h +++ b/components/segmentation_platform/internal/stats.h
@@ -159,7 +159,8 @@ kFailedToSaveModelResult = 8, kInvalidSelectionResultInPrefs = 9, kDBInitFailure = 10, - kMaxValue = kDBInitFailure + kAtLeastOneSegmentNotAvailable = 11, + kMaxValue = kAtLeastOneSegmentNotAvailable }; // Records the reason for failure or success to compute a segment selection.
diff --git a/components/segmentation_platform/public/BUILD.gn b/components/segmentation_platform/public/BUILD.gn index 4f325522..0dd677e 100644 --- a/components/segmentation_platform/public/BUILD.gn +++ b/components/segmentation_platform/public/BUILD.gn
@@ -13,6 +13,8 @@ "config.h", "features.cc", "features.h", + "model_provider.cc", + "model_provider.h", "segment_selection_result.cc", "segment_selection_result.h", "segmentation_platform_service.cc",
diff --git a/components/segmentation_platform/public/model_provider.cc b/components/segmentation_platform/public/model_provider.cc new file mode 100644 index 0000000..1eede716 --- /dev/null +++ b/components/segmentation_platform/public/model_provider.cc
@@ -0,0 +1,17 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/segmentation_platform/public/model_provider.h" + +namespace segmentation_platform { + +ModelProvider::ModelProvider( + optimization_guide::proto::OptimizationTarget optimization_target) + : optimization_target_(optimization_target) {} + +ModelProvider::~ModelProvider() = default; + +ModelProviderFactory::~ModelProviderFactory() = default; + +} // namespace segmentation_platform
diff --git a/components/segmentation_platform/public/model_provider.h b/components/segmentation_platform/public/model_provider.h new file mode 100644 index 0000000..fe41711 --- /dev/null +++ b/components/segmentation_platform/public/model_provider.h
@@ -0,0 +1,81 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_MODEL_PROVIDER_H_ +#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_MODEL_PROVIDER_H_ + +#include "base/callback.h" +#include "base/task/sequenced_task_runner.h" +#include "components/optimization_guide/proto/models.pb.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace segmentation_platform { +namespace proto { +class SegmentationModelMetadata; +} // namespace proto + +// Interface used by the segmentation platform to get model and metadata for a +// single optimization target. +class ModelProvider { + public: + using ModelUpdatedCallback = base::RepeatingCallback<void( + optimization_guide::proto::OptimizationTarget, + proto::SegmentationModelMetadata, + int64_t)>; + using ExecutionCallback = + base::OnceCallback<void(const absl::optional<float>&)>; + + explicit ModelProvider( + optimization_guide::proto::OptimizationTarget optimization_target); + virtual ~ModelProvider(); + + ModelProvider(ModelProvider&) = delete; + ModelProvider& operator=(ModelProvider&) = delete; + + // Implementation should return metadata that will be used to execute model. + // The metadata provided should define the number of features needed by the + // ExecuteModelWithInput() method. Starts a fetch request for the model for + // optimization target. The `model_updated_callback` can be called multiple + // times when new models are available for the optimization target. + virtual void InitAndFetchModel( + const ModelUpdatedCallback& model_updated_callback) = 0; + + // Executes the latest model available, with the given inputs and returns + // result via `callback`. Should be called only after InitAndFetchModel() + // otherwise returns absl::nullopt. Implementation could be a heuristic or + // model execution to return a result. The inputs to this method are the + // computed tensors based on the features provided in the latest call to + // `model_updated_callback`. The result is a float score with the probability + // of positive result. Also see `discrete_mapping` field in the + // `SegmentationModelMetadata` for how the score will be used to determine the + // segment. + virtual void ExecuteModelWithInput(const std::vector<float>& inputs, + ExecutionCallback callback) = 0; + + // Returns true if a model is available. + virtual bool ModelAvailable() = 0; + + protected: + const optimization_guide::proto::OptimizationTarget optimization_target_; +}; + +// Interface used by segmentation platform to create ModelProvider(s). +class ModelProviderFactory { + public: + virtual ~ModelProviderFactory(); + + // Creates a model provider for the given `optimization_target`. + virtual std::unique_ptr<ModelProvider> CreateProvider( + optimization_guide::proto::OptimizationTarget) = 0; + + // Creates a default model provider to be used when the original provider did + // not provide a model. Returns `nullptr` when a default provider is not + // available. + virtual std::unique_ptr<ModelProvider> CreateDefaultProvider( + optimization_guide::proto::OptimizationTarget) = 0; +}; + +} // namespace segmentation_platform + +#endif // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_MODEL_PROVIDER_H_
diff --git a/components/services/app_service/public/cpp/BUILD.gn b/components/services/app_service/public/cpp/BUILD.gn index ecf0d7d..0467960 100644 --- a/components/services/app_service/public/cpp/BUILD.gn +++ b/components/services/app_service/public/cpp/BUILD.gn
@@ -202,8 +202,6 @@ sources = [ "intent.cc", "intent.h", - "intent_constants.cc", - "intent_constants.h", "intent_filter_util.cc", "intent_filter_util.h", "intent_util.cc", @@ -239,6 +237,7 @@ ] deps = [ + ":app_types", ":intents", "//base", "//components/services/app_service/public/mojom",
diff --git a/components/services/app_service/public/cpp/app_update.cc b/components/services/app_service/public/cpp/app_update.cc index 225c22e..723d349 100644 --- a/components/services/app_service/public/cpp/app_update.cc +++ b/components/services/app_service/public/cpp/app_update.cc
@@ -39,7 +39,17 @@ } } -void CloneIntentFilters( +std::vector<apps::IntentFilterPtr> ConvertMojomIntentFiltersToIntentFilters( + const std::vector<apps::mojom::IntentFilterPtr>& mojom_intent_filters) { + std::vector<apps::IntentFilterPtr> intent_filters; + for (const auto& mojom_intent_filter : mojom_intent_filters) { + intent_filters.push_back( + apps::ConvertMojomIntentFilterToIntentFilter(mojom_intent_filter)); + } + return intent_filters; +} + +void CloneMojomIntentFilters( const std::vector<apps::mojom::IntentFilterPtr>& clone_from, std::vector<apps::mojom::IntentFilterPtr>* clone_to) { for (const auto& intent_filter : clone_from) { @@ -156,7 +166,7 @@ } if (!delta->intent_filters.empty()) { state->intent_filters.clear(); - ::CloneIntentFilters(delta->intent_filters, &state->intent_filters); + ::CloneMojomIntentFilters(delta->intent_filters, &state->intent_filters); } if (delta->resize_locked != apps::mojom::OptionalBool::kUnknown) { state->resize_locked = delta->resize_locked; @@ -855,28 +865,25 @@ (!mojom_state_ || (mojom_delta_->paused != mojom_state_->paused)); } -std::vector<apps::mojom::IntentFilterPtr> AppUpdate::IntentFilters() const { - std::vector<apps::mojom::IntentFilterPtr> intent_filters; +apps::IntentFilters AppUpdate::IntentFilters() const { + if (ShouldUseNonMojom()) { + if (delta_ && !delta_->intent_filters.empty()) { + return CloneIntentFilters(delta_->intent_filters); + } + if (state_ && !state_->intent_filters.empty()) { + return CloneIntentFilters(state_->intent_filters); + } + return std::vector<IntentFilterPtr>{}; + } if (mojom_delta_ && !mojom_delta_->intent_filters.empty()) { - ::CloneIntentFilters(mojom_delta_->intent_filters, &intent_filters); + return ::ConvertMojomIntentFiltersToIntentFilters( + mojom_delta_->intent_filters); } else if (mojom_state_ && !mojom_state_->intent_filters.empty()) { - ::CloneIntentFilters(mojom_state_->intent_filters, &intent_filters); + return ::ConvertMojomIntentFiltersToIntentFilters( + mojom_state_->intent_filters); } - - return intent_filters; -} - -apps::IntentFilters AppUpdate::GetIntentFilters() const { - apps::IntentFilters intent_filters; - - if (delta_ && !delta_->intent_filters.empty()) { - intent_filters = CloneIntentFilters(delta_->intent_filters); - } else if (state_ && !state_->intent_filters.empty()) { - intent_filters = CloneIntentFilters(state_->intent_filters); - } - - return intent_filters; + return std::vector<IntentFilterPtr>{}; } bool AppUpdate::IntentFiltersChanged() const {
diff --git a/components/services/app_service/public/cpp/app_update.h b/components/services/app_service/public/cpp/app_update.h index fdab66f..09b6b51a 100644 --- a/components/services/app_service/public/cpp/app_update.h +++ b/components/services/app_service/public/cpp/app_update.h
@@ -163,8 +163,7 @@ absl::optional<bool> Paused() const; bool PausedChanged() const; - std::vector<apps::mojom::IntentFilterPtr> IntentFilters() const; - apps::IntentFilters GetIntentFilters() const; + apps::IntentFilters IntentFilters() const; bool IntentFiltersChanged() const; absl::optional<bool> ResizeLocked() const;
diff --git a/components/services/app_service/public/cpp/app_update_mojom_unittest.cc b/components/services/app_service/public/cpp/app_update_mojom_unittest.cc index 4a8fdd1..6a35122 100644 --- a/components/services/app_service/public/cpp/app_update_mojom_unittest.cc +++ b/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
@@ -102,7 +102,7 @@ absl::optional<bool> expect_paused_; bool expect_paused_changed_; - std::vector<apps::mojom::IntentFilterPtr> expect_intent_filters_; + std::vector<apps::IntentFilterPtr> expect_intent_filters_; bool expect_intent_filters_changed_; absl::optional<bool> expect_resize_locked_; @@ -236,7 +236,7 @@ EXPECT_EQ(expect_paused_, u.Paused()); EXPECT_EQ(expect_paused_changed_, u.PausedChanged()); - EXPECT_EQ(expect_intent_filters_, u.IntentFilters()); + EXPECT_TRUE(IsEqual(expect_intent_filters_, u.IntentFilters())); EXPECT_EQ(expect_intent_filters_changed_, u.IntentFiltersChanged()); EXPECT_EQ(expect_resize_locked_, u.ResizeLocked()); @@ -881,7 +881,8 @@ intent_filter->conditions.push_back(std::move(host_condition)); state->intent_filters.push_back(intent_filter.Clone()); - expect_intent_filters_.push_back(intent_filter.Clone()); + expect_intent_filters_.push_back( + apps::ConvertMojomIntentFilterToIntentFilter(intent_filter)); expect_intent_filters_changed_ = false; CheckExpects(u); } @@ -910,7 +911,8 @@ intent_filter->conditions.push_back(std::move(host_condition)); delta->intent_filters.push_back(intent_filter.Clone()); - expect_intent_filters_.push_back(intent_filter.Clone()); + expect_intent_filters_.push_back( + apps::ConvertMojomIntentFilterToIntentFilter(intent_filter)); expect_intent_filters_changed_ = true; CheckExpects(u); }
diff --git a/components/services/app_service/public/cpp/app_update_unittest.cc b/components/services/app_service/public/cpp/app_update_unittest.cc index 261f7eb..2781f4b 100644 --- a/components/services/app_service/public/cpp/app_update_unittest.cc +++ b/components/services/app_service/public/cpp/app_update_unittest.cc
@@ -245,7 +245,7 @@ EXPECT_EQ(expect_paused_, u.Paused()); EXPECT_EQ(expect_paused_changed_, u.PausedChanged()); - EXPECT_TRUE(IsEqual(expect_intent_filters_, u.GetIntentFilters())); + EXPECT_TRUE(IsEqual(expect_intent_filters_, u.IntentFilters())); EXPECT_EQ(expect_intent_filters_changed_, u.IntentFiltersChanged()); EXPECT_EQ(expect_resize_locked_, u.ResizeLocked());
diff --git a/components/services/app_service/public/cpp/intent_constants.cc b/components/services/app_service/public/cpp/intent_constants.cc deleted file mode 100644 index 14e3812e..0000000 --- a/components/services/app_service/public/cpp/intent_constants.cc +++ /dev/null
@@ -1,11 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/services/app_service/public/cpp/intent_constants.h" - -namespace apps { - -const char kUseBrowserForLink[] = "use_browser"; - -} // namespace apps
diff --git a/components/services/app_service/public/cpp/intent_constants.h b/components/services/app_service/public/cpp/intent_constants.h deleted file mode 100644 index 762a46f..0000000 --- a/components/services/app_service/public/cpp/intent_constants.h +++ /dev/null
@@ -1,16 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_CONSTANTS_H_ -#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_CONSTANTS_H_ - -namespace apps { - -// App ID value which can be used as a Preferred App to denote that the browser -// will open the link, and that we should not prompt the user about it. -extern const char kUseBrowserForLink[]; - -} // namespace apps - -#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_CONSTANTS_H_
diff --git a/components/services/app_service/public/cpp/intent_filter.cc b/components/services/app_service/public/cpp/intent_filter.cc index 98174e21..7cc8315 100644 --- a/components/services/app_service/public/cpp/intent_filter.cc +++ b/components/services/app_service/public/cpp/intent_filter.cc
@@ -93,6 +93,99 @@ return intent_filter; } +void IntentFilter::AddSingleValueCondition( + ConditionType condition_type, + const std::string& value, + PatternMatchType pattern_match_type) { + ConditionValues condition_values; + condition_values.push_back( + std::make_unique<ConditionValue>(value, pattern_match_type)); + conditions.push_back( + std::make_unique<Condition>(condition_type, std::move(condition_values))); +} + +int IntentFilter::GetFilterMatchLevel() { + int match_level = static_cast<int>(IntentFilterMatchLevel::kNone); + for (const auto& condition : conditions) { + switch (condition->condition_type) { + case ConditionType::kAction: + // Action always need to be matched, so there is no need for + // match level. + break; + case ConditionType::kScheme: + match_level += static_cast<int>(IntentFilterMatchLevel::kScheme); + break; + case ConditionType::kHost: + match_level += static_cast<int>(IntentFilterMatchLevel::kHost); + break; + case ConditionType::kPattern: + match_level += static_cast<int>(IntentFilterMatchLevel::kPattern); + break; + case ConditionType::kMimeType: + case ConditionType::kFile: + match_level += static_cast<int>(IntentFilterMatchLevel::kMimeType); + break; + } + } + return match_level; +} + +void IntentFilter::GetMimeTypesAndExtensions( + std::set<std::string>& mime_types, + std::set<std::string>& file_extensions) { + for (const auto& condition : conditions) { + if (condition->condition_type != ConditionType::kFile) { + continue; + } + for (const auto& condition_value : condition->condition_values) { + if (condition_value->match_type == PatternMatchType::kFileExtension) { + file_extensions.insert(condition_value->value); + } + if (condition_value->match_type == PatternMatchType::kMimeType) { + mime_types.insert(condition_value->value); + } + } + } +} + +bool IntentFilter::IsBrowserFilter() { + if (GetFilterMatchLevel() != + static_cast<int>(IntentFilterMatchLevel::kScheme)) { + return false; + } + for (const auto& condition : conditions) { + if (condition->condition_type != ConditionType::kScheme) { + continue; + } + for (const auto& condition_value : condition->condition_values) { + if (condition_value->value == url::kHttpScheme || + condition_value->value == url::kHttpsScheme) { + return true; + } + } + } + + return false; +} + +bool IntentFilter::IsFileExtensionsFilter() { + for (const auto& condition : conditions) { + // We expect action conditions to be paired with file conditions. + if (condition->condition_type == ConditionType::kAction) { + continue; + } + if (condition->condition_type != ConditionType::kFile) { + return false; + } + for (const auto& condition_value : condition->condition_values) { + if (condition_value->match_type != PatternMatchType::kFileExtension) { + return false; + } + } + } + return true; +} + IntentFilters CloneIntentFilters(const IntentFilters& intent_filters) { IntentFilters ret; for (const auto& intent_filter : intent_filters) {
diff --git a/components/services/app_service/public/cpp/intent_filter.h b/components/services/app_service/public/cpp/intent_filter.h index 4fd72b8..042f43b 100644 --- a/components/services/app_service/public/cpp/intent_filter.h +++ b/components/services/app_service/public/cpp/intent_filter.h
@@ -5,6 +5,7 @@ #ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_H_ #define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_H_ +#include <set> #include <string> #include <utility> #include <vector> @@ -16,6 +17,19 @@ namespace apps { +// The concept of match level is taken from Android. The values are not +// necessary the same. +// See +// https://developer.android.com/reference/android/content/IntentFilter.html#constants_2 +// for more details. +enum class IntentFilterMatchLevel { + kNone = 0, + kScheme = 1, + kHost = 2, + kPattern = 4, + kMimeType = 8, +}; + // The intent filter matching condition types. enum class ConditionType { kScheme, // Matches the URL scheme (e.g. https, tel). @@ -92,6 +106,29 @@ std::unique_ptr<IntentFilter> Clone() const; + // Creates condition that only contain one value and adds the condition to + // the intent filter. + void AddSingleValueCondition(apps::ConditionType condition_type, + const std::string& value, + apps::PatternMatchType pattern_match_type); + + // Gets the intent_filter match level. The higher the return value, the better + // the match is. For example, a filter with scheme, host and path is better + // match compare with filter with only scheme. Each condition type has a + // matching level value, and this function will return the sum of the matching + // level values of all existing condition types. + int GetFilterMatchLevel(); + + void GetMimeTypesAndExtensions(std::set<std::string>& mime_types, + std::set<std::string>& file_extensions); + + // Returns true if the filter is a browser filter, i.e. can handle all https + // or http scheme. + bool IsBrowserFilter(); + + // Returns true if the filter only contains file extension pattern matches. + bool IsFileExtensionsFilter(); + Conditions conditions; // Activity which registered this filter. We only fill this field for ARC
diff --git a/components/services/app_service/public/cpp/intent_filter_util.cc b/components/services/app_service/public/cpp/intent_filter_util.cc index dfcc3689..bed5aabe 100644 --- a/components/services/app_service/public/cpp/intent_filter_util.cc +++ b/components/services/app_service/public/cpp/intent_filter_util.cc
@@ -6,7 +6,6 @@ #include "base/strings/string_piece.h" #include "base/strings/string_util.h" -#include "components/services/app_service/public/cpp/intent_constants.h" #include "components/services/app_service/public/cpp/intent_util.h" #include "components/services/app_service/public/mojom/types.mojom-shared.h" #include "url/url_constants.h" @@ -92,17 +91,6 @@ return condition; } -void AddSingleValueCondition(apps::ConditionType condition_type, - const std::string& value, - apps::PatternMatchType pattern_match_type, - apps::IntentFilterPtr& intent_filter) { - apps::ConditionValues condition_values; - condition_values.push_back( - std::make_unique<apps::ConditionValue>(value, pattern_match_type)); - intent_filter->conditions.push_back(std::make_unique<apps::Condition>( - condition_type, std::move(condition_values))); -} - void AddSingleValueCondition(apps::mojom::ConditionType condition_type, const std::string& value, apps::mojom::PatternMatchType pattern_match_type, @@ -118,18 +106,20 @@ apps::IntentFilterPtr MakeIntentFilterForUrlScope(const GURL& url) { auto intent_filter = std::make_unique<apps::IntentFilter>(); - AddSingleValueCondition(apps::ConditionType::kAction, - apps_util::kIntentActionView, - apps::PatternMatchType::kNone, intent_filter); + intent_filter->AddSingleValueCondition(apps::ConditionType::kAction, + apps_util::kIntentActionView, + apps::PatternMatchType::kNone); - AddSingleValueCondition(apps::ConditionType::kScheme, url.scheme(), - apps::PatternMatchType::kNone, intent_filter); + intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme, + url.scheme(), + apps::PatternMatchType::kNone); - AddSingleValueCondition(apps::ConditionType::kHost, url.host(), - apps::PatternMatchType::kNone, intent_filter); + intent_filter->AddSingleValueCondition(apps::ConditionType::kHost, url.host(), + apps::PatternMatchType::kNone); - AddSingleValueCondition(apps::ConditionType::kPattern, url.path(), - apps::PatternMatchType::kPrefix, intent_filter); + intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern, + url.path(), + apps::PatternMatchType::kPrefix); return intent_filter; } @@ -155,7 +145,7 @@ } int GetFilterMatchLevel(const apps::mojom::IntentFilterPtr& intent_filter) { - int match_level = IntentFilterMatchLevel::kNone; + int match_level = static_cast<int>(apps::IntentFilterMatchLevel::kNone); for (const auto& condition : intent_filter->conditions) { switch (condition->condition_type) { case apps::mojom::ConditionType::kAction: @@ -163,17 +153,18 @@ // match level. break; case apps::mojom::ConditionType::kScheme: - match_level += IntentFilterMatchLevel::kScheme; + match_level += static_cast<int>(apps::IntentFilterMatchLevel::kScheme); break; case apps::mojom::ConditionType::kHost: - match_level += IntentFilterMatchLevel::kHost; + match_level += static_cast<int>(apps::IntentFilterMatchLevel::kHost); break; case apps::mojom::ConditionType::kPattern: - match_level += IntentFilterMatchLevel::kPattern; + match_level += static_cast<int>(apps::IntentFilterMatchLevel::kPattern); break; case apps::mojom::ConditionType::kMimeType: case apps::mojom::ConditionType::kFile: - match_level += IntentFilterMatchLevel::kMimeType; + match_level += + static_cast<int>(apps::IntentFilterMatchLevel::kMimeType); break; } } @@ -217,7 +208,8 @@ } bool IsBrowserFilter(const apps::mojom::IntentFilterPtr& filter) { - if (GetFilterMatchLevel(filter) != IntentFilterMatchLevel::kScheme) { + if (GetFilterMatchLevel(filter) != + static_cast<int>(apps::IntentFilterMatchLevel::kScheme)) { return false; } for (const auto& condition : filter->conditions) { @@ -314,11 +306,61 @@ } bool IsSupportedLinkForApp(const std::string& app_id, + const apps::IntentFilterPtr& intent_filter) { + // Filters associated with kUseBrowserForLink are a special case. These + // filters do not "belong" to the app and should not be treated as supported + // links. + if (app_id == apps_util::kUseBrowserForLink) { + return false; + } + + bool action = false; + bool scheme = false; + bool host = false; + bool pattern = false; + for (auto& condition : intent_filter->conditions) { + switch (condition->condition_type) { + case apps::ConditionType::kAction: + for (auto& condition_value : condition->condition_values) { + if (condition_value->value == apps_util::kIntentActionView) { + action = true; + break; + } + } + break; + case apps::ConditionType::kScheme: + for (auto& condition_value : condition->condition_values) { + if (condition_value->value == "http" || + condition_value->value == "https") { + scheme = true; + break; + } + } + break; + case apps::ConditionType::kHost: + host = true; + break; + case apps::ConditionType::kPattern: + pattern = true; + break; + default: + break; + } + + if (action && scheme && host && pattern) { + return true; + } + } + + return false; +} + +bool IsSupportedLinkForApp(const std::string& app_id, const apps::mojom::IntentFilterPtr& intent_filter) { // Filters associated with kUseBrowserForLink are a special case. These // filters do not "belong" to the app and should not be treated as supported // links. - if (app_id == apps::kUseBrowserForLink) { + if (app_id == apps_util::kUseBrowserForLink) { return false; }
diff --git a/components/services/app_service/public/cpp/intent_filter_util.h b/components/services/app_service/public/cpp/intent_filter_util.h index e7ee2ad..97eb7d1 100644 --- a/components/services/app_service/public/cpp/intent_filter_util.h +++ b/components/services/app_service/public/cpp/intent_filter_util.h
@@ -15,19 +15,6 @@ namespace apps_util { -// The concept of match level is taken from Android. The values are not -// necessary the same. -// See -// https://developer.android.com/reference/android/content/IntentFilter.html#constants_2 -// for more details. -enum IntentFilterMatchLevel { - kNone = 0, - kScheme = 1, - kHost = 2, - kPattern = 4, - kMimeType = 8, -}; - // Creates condition value that makes up App Service intent filter // condition. Each condition contains a list of condition values. // For pattern type of condition, the value match will be based on the @@ -46,13 +33,6 @@ apps::mojom::ConditionType condition_type, std::vector<apps::mojom::ConditionValuePtr> condition_values); -// Creates condition that only contain one value and add the condition to -// the intent filter. -void AddSingleValueCondition(apps::ConditionType condition_type, - const std::string& value, - apps::PatternMatchType pattern_match_type, - apps::IntentFilterPtr& intent_filter); - // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService. void AddSingleValueCondition(apps::mojom::ConditionType condition_type, const std::string& value, @@ -98,8 +78,14 @@ std::set<std::string> AppManagementGetSupportedLinks( const apps::mojom::IntentFilterPtr& intent_filter); +// Checks if the `intent_filter` is a supported link for `app_id`, i.e. it has +// the "view" action, a http or https scheme, and at least one host and pattern. +bool IsSupportedLinkForApp(const std::string& app_id, + const apps::IntentFilterPtr& intent_filter); + // Check if the |intent_filter| is a supported link for |app_id|, i.e. it has // the "view" action, a http or https scheme, and at least one host and pattern. +// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService. bool IsSupportedLinkForApp(const std::string& app_id, const apps::mojom::IntentFilterPtr& intent_filter);
diff --git a/components/services/app_service/public/cpp/intent_filter_util_unittest.cc b/components/services/app_service/public/cpp/intent_filter_util_unittest.cc index 5da79c2e3..b48c3b9 100644 --- a/components/services/app_service/public/cpp/intent_filter_util_unittest.cc +++ b/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
@@ -27,6 +27,29 @@ class IntentFilterUtilTest : public testing::Test { protected: + apps::IntentFilterPtr MakeFilter(std::string scheme, + std::string host, + std::string path, + apps::PatternMatchType pattern) { + auto intent_filter = std::make_unique<apps::IntentFilter>(); + + intent_filter->AddSingleValueCondition(apps::ConditionType::kAction, + apps_util::kIntentActionView, + apps::PatternMatchType::kNone); + + intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme, scheme, + apps::PatternMatchType::kNone); + + intent_filter->AddSingleValueCondition(apps::ConditionType::kHost, host, + apps::PatternMatchType::kNone); + + intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern, path, + pattern); + + return intent_filter; + } + + // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService. apps::mojom::IntentFilterPtr MakeFilter( std::string scheme, std::string host, @@ -290,7 +313,8 @@ EXPECT_EQ(links.count("m.youtube.com/.*/foo"), 1u); } -TEST_F(IntentFilterUtilTest, IsSupportedLink) { +// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService. +TEST_F(IntentFilterUtilTest, IsSupportedLinkMojom) { auto filter = MakeFilter("https", "www.google.com", "/maps", apps::mojom::PatternMatchType::kLiteral); ASSERT_TRUE(apps_util::IsSupportedLinkForApp(kAppId, filter)); @@ -300,7 +324,18 @@ ASSERT_TRUE(apps_util::IsSupportedLinkForApp(kAppId, filter)); } -TEST_F(IntentFilterUtilTest, NotSupportedLink) { +TEST_F(IntentFilterUtilTest, IsSupportedLink) { + auto filter = MakeFilter("https", "www.google.com", "/maps", + apps::PatternMatchType::kLiteral); + ASSERT_TRUE(apps_util::IsSupportedLinkForApp(kAppId, filter)); + + filter = MakeFilter("https", "www.google.com", ".*", + apps::PatternMatchType::kGlob); + ASSERT_TRUE(apps_util::IsSupportedLinkForApp(kAppId, filter)); +} + +// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService. +TEST_F(IntentFilterUtilTest, NotSupportedLinkMojom) { ASSERT_FALSE(apps_util::IsSupportedLinkForApp( kAppId, apps_util::CreateIntentFilterForMimeType("image/png"))); @@ -326,6 +361,30 @@ ASSERT_FALSE(apps_util::IsSupportedLinkForApp(kAppId, browser_filter)); } +TEST_F(IntentFilterUtilTest, NotSupportedLink) { + ASSERT_FALSE(apps_util::IsSupportedLinkForApp( + kAppId, apps_util::MakeIntentFilterForMimeType("image/png"))); + + auto browser_filter = std::make_unique<apps::IntentFilter>(); + browser_filter->AddSingleValueCondition(apps::ConditionType::kAction, + apps_util::kIntentActionView, + apps::PatternMatchType::kNone); + browser_filter->AddSingleValueCondition(apps::ConditionType::kScheme, "https", + apps::PatternMatchType::kNone); + ASSERT_FALSE(apps_util::IsSupportedLinkForApp(kAppId, browser_filter)); + + auto host_filter = std::make_unique<apps::IntentFilter>(); + host_filter->AddSingleValueCondition(apps::ConditionType::kAction, + apps_util::kIntentActionView, + apps::PatternMatchType::kNone); + host_filter->AddSingleValueCondition(apps::ConditionType::kScheme, "https", + apps::PatternMatchType::kNone); + host_filter->AddSingleValueCondition(apps::ConditionType::kHost, + "www.example.com", + apps::PatternMatchType::kNone); + ASSERT_FALSE(apps_util::IsSupportedLinkForApp(kAppId, browser_filter)); +} + TEST_F(IntentFilterUtilTest, HostMatchOverlapLiteralAndNone) { auto google_domain_filter = MakeFilter( "https", "www.google.com", "/", apps::mojom::PatternMatchType::kLiteral); @@ -448,37 +507,30 @@ base::flat_map<std::string, std::vector<apps::IntentFilterPtr>> filters; auto intent_filter1 = std::make_unique<apps::IntentFilter>(); - apps_util::AddSingleValueCondition(apps::ConditionType::kScheme, "1", - apps::PatternMatchType::kNone, - intent_filter1); + intent_filter1->AddSingleValueCondition(apps::ConditionType::kScheme, "1", + apps::PatternMatchType::kNone); filters["1"].push_back(std::move(intent_filter1)); auto intent_filter2 = std::make_unique<apps::IntentFilter>(); - apps_util::AddSingleValueCondition(apps::ConditionType::kHost, "2", - apps::PatternMatchType::kLiteral, - intent_filter2); - apps_util::AddSingleValueCondition(apps::ConditionType::kPattern, "3", - apps::PatternMatchType::kPrefix, - intent_filter2); + intent_filter2->AddSingleValueCondition(apps::ConditionType::kHost, "2", + apps::PatternMatchType::kLiteral); + intent_filter2->AddSingleValueCondition(apps::ConditionType::kPattern, "3", + apps::PatternMatchType::kPrefix); filters["1"].push_back(std::move(intent_filter2)); apps::IntentFilters intent_filters2; auto intent_filter3 = std::make_unique<apps::IntentFilter>(); - apps_util::AddSingleValueCondition(apps::ConditionType::kAction, "4", - apps::PatternMatchType::kGlob, - intent_filter3); - apps_util::AddSingleValueCondition(apps::ConditionType::kMimeType, "5", - apps::PatternMatchType::kMimeType, - intent_filter3); + intent_filter3->AddSingleValueCondition(apps::ConditionType::kAction, "4", + apps::PatternMatchType::kGlob); + intent_filter3->AddSingleValueCondition(apps::ConditionType::kMimeType, "5", + apps::PatternMatchType::kMimeType); filters["2"].push_back(std::move(intent_filter3)); auto intent_filter4 = std::make_unique<apps::IntentFilter>(); - apps_util::AddSingleValueCondition(apps::ConditionType::kFile, "6", - apps::PatternMatchType::kMimeType, - intent_filter4); - apps_util::AddSingleValueCondition(apps::ConditionType::kFile, "7", - apps::PatternMatchType::kFileExtension, - intent_filter4); + intent_filter4->AddSingleValueCondition(apps::ConditionType::kFile, "6", + apps::PatternMatchType::kMimeType); + intent_filter4->AddSingleValueCondition( + apps::ConditionType::kFile, "7", apps::PatternMatchType::kFileExtension); filters["2"].push_back(std::move(intent_filter4)); auto output = apps::ConvertMojomIntentFiltersToIntentFilters(
diff --git a/components/services/app_service/public/cpp/intent_test_util.cc b/components/services/app_service/public/cpp/intent_test_util.cc index 50b8c6b..30b14767 100644 --- a/components/services/app_service/public/cpp/intent_test_util.cc +++ b/components/services/app_service/public/cpp/intent_test_util.cc
@@ -20,9 +20,8 @@ DCHECK(!mime_type.empty() || !file_extension.empty() || !url_pattern.empty()); auto intent_filter = std::make_unique<apps::IntentFilter>(); - apps_util::AddSingleValueCondition(apps::ConditionType::kAction, action, - apps::PatternMatchType::kNone, - intent_filter); + intent_filter->AddSingleValueCondition(apps::ConditionType::kAction, action, + apps::PatternMatchType::kNone); apps::ConditionValues condition_values; if (!mime_type.empty()) { @@ -85,9 +84,9 @@ const std::string& mime_type) { auto intent_filter = std::make_unique<apps::IntentFilter>(); - apps_util::AddSingleValueCondition( - apps::ConditionType::kAction, kIntentActionSend, - apps::PatternMatchType::kNone, intent_filter); + intent_filter->AddSingleValueCondition(apps::ConditionType::kAction, + kIntentActionSend, + apps::PatternMatchType::kNone); std::vector<apps::ConditionValuePtr> condition_values; condition_values.push_back(std::make_unique<apps::ConditionValue>( @@ -124,6 +123,55 @@ activity_label); } +apps::IntentFilterPtr MakeSchemeOnlyFilter(const std::string& scheme) { + apps::ConditionValues condition_values; + condition_values.push_back(std::make_unique<apps::ConditionValue>( + scheme, apps::PatternMatchType::kNone)); + auto condition = std::make_unique<apps::Condition>( + apps::ConditionType::kScheme, std::move(condition_values)); + + auto intent_filter = std::make_unique<apps::IntentFilter>(); + intent_filter->conditions.push_back(std::move(condition)); + + return intent_filter; +} + +apps::IntentFilterPtr MakeSchemeAndHostOnlyFilter(const std::string& scheme, + const std::string& host) { + apps::ConditionValues scheme_condition_values; + scheme_condition_values.push_back(std::make_unique<apps::ConditionValue>( + scheme, apps::PatternMatchType::kNone)); + auto scheme_condition = std::make_unique<apps::Condition>( + apps::ConditionType::kScheme, std::move(scheme_condition_values)); + + apps::ConditionValues host_condition_values; + host_condition_values.push_back(std::make_unique<apps::ConditionValue>( + host, apps::PatternMatchType::kNone)); + auto host_condition = std::make_unique<apps::Condition>( + apps::ConditionType::kHost, std::move(host_condition_values)); + + auto intent_filter = std::make_unique<apps::IntentFilter>(); + intent_filter->conditions.push_back(std::move(scheme_condition)); + intent_filter->conditions.push_back(std::move(host_condition)); + + return intent_filter; +} + +void AddConditionValue(apps::ConditionType condition_type, + const std::string& value, + apps::PatternMatchType pattern_match_type, + apps::IntentFilterPtr& intent_filter) { + for (auto& condition : intent_filter->conditions) { + if (condition->condition_type == condition_type) { + condition->condition_values.push_back( + std::make_unique<apps::ConditionValue>(value, pattern_match_type)); + return; + } + } + intent_filter->AddSingleValueCondition(condition_type, value, + pattern_match_type); +} + apps::mojom::IntentFilterPtr CreateSchemeOnlyFilter(const std::string& scheme) { std::vector<apps::mojom::ConditionValuePtr> condition_values; condition_values.push_back(apps_util::MakeConditionValue(
diff --git a/components/services/app_service/public/cpp/intent_test_util.h b/components/services/app_service/public/cpp/intent_test_util.h index a149c3dc..651d64d 100644 --- a/components/services/app_service/public/cpp/intent_test_util.h +++ b/components/services/app_service/public/cpp/intent_test_util.h
@@ -34,6 +34,21 @@ apps::IntentFilterPtr MakeURLFilterForView(const std::string& url_pattern, const std::string& activity_label); +// Creates intent filter that contains only the `scheme`. +apps::IntentFilterPtr MakeSchemeOnlyFilter(const std::string& scheme); + +// Creates intent filter that contains only the `scheme` and `host`. +apps::IntentFilterPtr MakeSchemeAndHostOnlyFilter(const std::string& scheme, + const std::string& host); + +// Add a condition value to the |intent_filter|. If the |condition_type| +// exists, add the condition value to the existing condition, otherwise +// create new condition. +void AddConditionValue(apps::ConditionType condition_type, + const std::string& value, + apps::PatternMatchType pattern_match_type, + apps::IntentFilterPtr& intent_filter); + // TODO(crbug.com/1253250): Remove below functions after migrating to non-mojo // AppService.
diff --git a/components/services/app_service/public/cpp/intent_util.cc b/components/services/app_service/public/cpp/intent_util.cc index e82df903e4..bfdd42d 100644 --- a/components/services/app_service/public/cpp/intent_util.cc +++ b/components/services/app_service/public/cpp/intent_util.cc
@@ -91,6 +91,7 @@ const char kIntentActionSend[] = "send"; const char kIntentActionSendMultiple[] = "send_multiple"; const char kIntentActionCreateNote[] = "create_note"; +const char kUseBrowserForLink[] = "use_browser"; apps::mojom::IntentPtr CreateIntentFromUrl(const GURL& url) { auto intent = apps::mojom::Intent::New(); @@ -366,6 +367,43 @@ } // namespace +bool IsGenericFileHandler(const apps::IntentPtr& intent, + const apps::IntentFilterPtr& filter) { + if (!intent || !filter || intent->files.empty()) { + return false; + } + + std::set<std::string> mime_types; + std::set<std::string> file_extensions; + filter->GetMimeTypesAndExtensions(mime_types, file_extensions); + if (file_extensions.count("*") > 0 || mime_types.count("*") > 0 || + mime_types.count("*/*") > 0) { + return true; + } + + // If a text/* file handler matches with an unsupported text mime type, we + // regard it as a generic match. + if (mime_types.count("text/*")) { + for (const auto& file : intent->files) { + DCHECK(file); + if (file->mime_type.has_value() && + blink::IsUnsupportedTextMimeType(file->mime_type.value())) { + return true; + } + } + } + + // If directory is selected, it is generic unless mime_types included + // 'inode/directory'. + for (const auto& file : intent->files) { + DCHECK(file); + if (file->is_directory.value_or(false)) { + return mime_types.count(kMimeTypeInodeDirectory) == 0; + } + } + return false; +} + bool IsGenericFileHandler(const apps::mojom::IntentPtr& intent, const apps::mojom::IntentFilterPtr& filter) { if (!intent->files.has_value())
diff --git a/components/services/app_service/public/cpp/intent_util.h b/components/services/app_service/public/cpp/intent_util.h index aff734b4..9f7b855 100644 --- a/components/services/app_service/public/cpp/intent_util.h +++ b/components/services/app_service/public/cpp/intent_util.h
@@ -9,6 +9,7 @@ #include <string> +#include "components/services/app_service/public/cpp/intent.h" #include "components/services/app_service/public/cpp/intent_filter.h" #include "components/services/app_service/public/mojom/types.mojom.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -27,6 +28,10 @@ extern const char kIntentActionSendMultiple[]; extern const char kIntentActionCreateNote[]; +// App ID value which can be used as a Preferred App to denote that the browser +// will open the link, and that we should not prompt the user about it. +extern const char kUseBrowserForLink[]; + struct SharedText { std::string text; GURL url; @@ -101,6 +106,11 @@ // Return true if |filter| only contains file extension pattern matches. bool FilterIsForFileExtensions(const apps::mojom::IntentFilterPtr& filter); +bool IsGenericFileHandler(const apps::IntentPtr& intent, + const apps::IntentFilterPtr& filter); + +// TODO(crbug.com/1253250): Remove this function after migrating to non-mojo +// AppService. bool IsGenericFileHandler(const apps::mojom::IntentPtr& intent, const apps::mojom::IntentFilterPtr& filter);
diff --git a/components/services/app_service/public/cpp/intent_util_unittest.cc b/components/services/app_service/public/cpp/intent_util_unittest.cc index eecc22b..4eaf6dd 100644 --- a/components/services/app_service/public/cpp/intent_util_unittest.cc +++ b/components/services/app_service/public/cpp/intent_util_unittest.cc
@@ -6,6 +6,7 @@ #include "base/values.h" #include "components/services/app_service/public/cpp/intent.h" +#include "components/services/app_service/public/cpp/intent_filter.h" #include "components/services/app_service/public/cpp/intent_filter_util.h" #include "components/services/app_service/public/cpp/intent_test_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -380,7 +381,8 @@ apps_util::ConditionValueMatches("/acb", condition_value_escape_star)); } -TEST_F(IntentUtilTest, FilterMatchLevel) { +// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService. +TEST_F(IntentUtilTest, FilterMatchLevelMojom) { auto filter_scheme_only = apps_util::CreateSchemeOnlyFilter("http"); auto filter_scheme_and_host_only = apps_util::CreateSchemeAndHostOnlyFilter("https", "www.abc.com"); @@ -389,16 +391,16 @@ auto filter_empty = apps::mojom::IntentFilter::New(); EXPECT_EQ(apps_util::GetFilterMatchLevel(filter_url), - apps_util::IntentFilterMatchLevel::kScheme + - apps_util::IntentFilterMatchLevel::kHost + - apps_util::IntentFilterMatchLevel::kPattern); + static_cast<int>(apps::IntentFilterMatchLevel::kScheme) + + static_cast<int>(apps::IntentFilterMatchLevel::kHost) + + static_cast<int>(apps::IntentFilterMatchLevel::kPattern)); EXPECT_EQ(apps_util::GetFilterMatchLevel(filter_scheme_and_host_only), - apps_util::IntentFilterMatchLevel::kScheme + - apps_util::IntentFilterMatchLevel::kHost); + static_cast<int>(apps::IntentFilterMatchLevel::kScheme) + + static_cast<int>(apps::IntentFilterMatchLevel::kHost)); EXPECT_EQ(apps_util::GetFilterMatchLevel(filter_scheme_only), - apps_util::IntentFilterMatchLevel::kScheme); + static_cast<int>(apps::IntentFilterMatchLevel::kScheme)); EXPECT_EQ(apps_util::GetFilterMatchLevel(filter_empty), - apps_util::IntentFilterMatchLevel::kNone); + static_cast<int>(apps::IntentFilterMatchLevel::kNone)); EXPECT_TRUE(apps_util::GetFilterMatchLevel(filter_url) > apps_util::GetFilterMatchLevel(filter_scheme_and_host_only)); @@ -408,6 +410,39 @@ apps_util::GetFilterMatchLevel(filter_empty)); } +TEST_F(IntentUtilTest, FilterMatchLevel) { + auto filter_scheme_only = apps_util::MakeSchemeOnlyFilter("http"); + auto filter_scheme_and_host_only = + apps_util::MakeSchemeAndHostOnlyFilter("https", "www.abc.com"); + auto filter_url = + apps_util::MakeIntentFilterForUrlScope(GURL("https:://www.google.com/")); + auto filter_empty = std::make_unique<apps::IntentFilter>(); + + EXPECT_TRUE(filter_scheme_only->IsBrowserFilter()); + EXPECT_FALSE(filter_scheme_and_host_only->IsBrowserFilter()); + EXPECT_FALSE(filter_url->IsBrowserFilter()); + EXPECT_FALSE(filter_empty->IsBrowserFilter()); + + EXPECT_EQ(filter_url->GetFilterMatchLevel(), + static_cast<int>(apps::IntentFilterMatchLevel::kScheme) + + static_cast<int>(apps::IntentFilterMatchLevel::kHost) + + static_cast<int>(apps::IntentFilterMatchLevel::kPattern)); + EXPECT_EQ(filter_scheme_and_host_only->GetFilterMatchLevel(), + static_cast<int>(apps::IntentFilterMatchLevel::kScheme) + + static_cast<int>(apps::IntentFilterMatchLevel::kHost)); + EXPECT_EQ(filter_scheme_only->GetFilterMatchLevel(), + static_cast<int>(apps::IntentFilterMatchLevel::kScheme)); + EXPECT_EQ(filter_empty->GetFilterMatchLevel(), + static_cast<int>(apps::IntentFilterMatchLevel::kNone)); + + EXPECT_TRUE(filter_url->GetFilterMatchLevel() > + filter_scheme_and_host_only->GetFilterMatchLevel()); + EXPECT_TRUE(filter_scheme_and_host_only->GetFilterMatchLevel() > + filter_scheme_only->GetFilterMatchLevel()); + EXPECT_TRUE(filter_scheme_only->GetFilterMatchLevel() > + filter_empty->GetFilterMatchLevel()); +} + // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService. TEST_F(IntentUtilTest, ActionMatchMojom) { GURL test_url("https://www.google.com/"); @@ -1336,7 +1371,8 @@ {"image/png", "image/jpeg", "text/plain"})); } -TEST_F(IntentUtilTest, IsGenericFileHandler) { +// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService. +TEST_F(IntentUtilTest, IsGenericFileHandlerMojom) { using apps::mojom::IntentFile; using apps::mojom::IntentFilePtr; using apps::mojom::IntentFilterPtr; @@ -1445,6 +1481,116 @@ EXPECT_FALSE(apps_util::IsGenericFileHandler(intent3, filter12)); } +TEST_F(IntentUtilTest, IsGenericFileHandler) { + using apps::Intent; + using apps::IntentFile; + using apps::IntentFilePtr; + using apps::IntentFilterPtr; + using apps::IntentPtr; + + std::vector<IntentFilePtr> intent_files; + IntentFilePtr foo = std::make_unique<IntentFile>(test_url("foo.jpg")); + foo->mime_type = "image/jpeg"; + foo->is_directory = false; + intent_files.push_back(std::move(foo)); + + IntentFilePtr bar = std::make_unique<IntentFile>(test_url("bar.txt")); + bar->mime_type = "text/plain"; + bar->is_directory = false; + intent_files.push_back(std::move(bar)); + + std::vector<IntentFilePtr> intent_files2; + IntentFilePtr foo2 = std::make_unique<IntentFile>(test_url("foo.ics")); + foo2->mime_type = "text/calendar"; + foo2->is_directory = false; + intent_files2.push_back(std::move(foo2)); + + std::vector<IntentFilePtr> intent_files3; + IntentFilePtr foo_dir = std::make_unique<IntentFile>(test_url("foo/")); + foo_dir->mime_type = ""; + foo_dir->is_directory = true; + intent_files3.push_back(std::move(foo_dir)); + + IntentPtr intent = std::make_unique<Intent>(std::move(intent_files)); + IntentPtr intent2 = std::make_unique<Intent>(std::move(intent_files2)); + IntentPtr intent3 = std::make_unique<Intent>(std::move(intent_files3)); + + const std::string kLabel = ""; + + // extensions: ["*"] + IntentFilterPtr filter1 = apps_util::MakeFileFilterForView("", "*", kLabel); + EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter1)); + EXPECT_TRUE(filter1->IsFileExtensionsFilter()); + + // extensions: ["*", "jpg"] + IntentFilterPtr filter2 = apps_util::MakeFileFilterForView("", "*", kLabel); + apps_util::AddConditionValue(apps::ConditionType::kFile, "jpg", + apps::PatternMatchType::kFileExtension, filter2); + EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter2)); + EXPECT_TRUE(filter2->IsFileExtensionsFilter()); + + // extensions: ["jpg"] + IntentFilterPtr filter3 = apps_util::MakeFileFilterForView("", "jpg", kLabel); + EXPECT_FALSE(apps_util::IsGenericFileHandler(intent, filter3)); + EXPECT_TRUE(filter3->IsFileExtensionsFilter()); + + // types: ["*"] + IntentFilterPtr filter4 = apps_util::MakeFileFilterForView("*", "", kLabel); + EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter4)); + EXPECT_FALSE(filter4->IsFileExtensionsFilter()); + + // types: ["*/*"] + IntentFilterPtr filter5 = apps_util::MakeFileFilterForView("*/*", "", kLabel); + EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter5)); + EXPECT_FALSE(filter5->IsFileExtensionsFilter()); + + // types: ["image/*"] + IntentFilterPtr filter6 = + apps_util::MakeFileFilterForView("image/*", "", kLabel); + // Partial wild card is not generic. + EXPECT_FALSE(apps_util::IsGenericFileHandler(intent, filter6)); + EXPECT_FALSE(filter6->IsFileExtensionsFilter()); + + // types: ["*", "image/*"] + IntentFilterPtr filter7 = apps_util::MakeFileFilterForView("*", "", kLabel); + apps_util::AddConditionValue(apps::ConditionType::kFile, "image/*", + apps::PatternMatchType::kMimeType, filter7); + EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter7)); + EXPECT_FALSE(filter7->IsFileExtensionsFilter()); + + // extensions: ["*"], types: ["image/*"] + IntentFilterPtr filter8 = + apps_util::MakeFileFilterForView("image/*", "*", kLabel); + EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter8)); + EXPECT_FALSE(filter8->IsFileExtensionsFilter()); + + // types: ["text/*"] and target files contain unsupported text mime type, e.g. + // text/calendar. + IntentFilterPtr filter9 = + apps_util::MakeFileFilterForView("text/*", "", kLabel); + EXPECT_TRUE(apps_util::IsGenericFileHandler(intent2, filter9)); + EXPECT_FALSE(filter9->IsFileExtensionsFilter()); + + // types: ["text/*"] and target files don't contain unsupported text mime + // type. + IntentFilterPtr filter10 = + apps_util::MakeFileFilterForView("text/*", "", kLabel); + EXPECT_FALSE(apps_util::IsGenericFileHandler(intent, filter10)); + EXPECT_FALSE(filter10->IsFileExtensionsFilter()); + + // File is a directory. + IntentFilterPtr filter11 = + apps_util::MakeFileFilterForView("text/*", "", kLabel); + EXPECT_TRUE(apps_util::IsGenericFileHandler(intent3, filter11)); + EXPECT_FALSE(filter11->IsFileExtensionsFilter()); + + // File is a directory, but filter is inode/directory. + IntentFilterPtr filter12 = + apps_util::MakeFileFilterForView("inode/directory", "", kLabel); + EXPECT_FALSE(apps_util::IsGenericFileHandler(intent3, filter12)); + EXPECT_FALSE(filter12->IsFileExtensionsFilter()); +} + // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService. TEST_F(IntentUtilTest, MojomConvert) { const std::string action = apps_util::kIntentActionSend;
diff --git a/components/services/app_service/public/cpp/preferred_apps_list.cc b/components/services/app_service/public/cpp/preferred_apps_list.cc index 1fb545c6..82400e3 100644 --- a/components/services/app_service/public/cpp/preferred_apps_list.cc +++ b/components/services/app_service/public/cpp/preferred_apps_list.cc
@@ -9,6 +9,7 @@ #include "base/containers/contains.h" #include "base/ranges/algorithm.h" #include "base/strings/string_util.h" +#include "components/services/app_service/public/cpp/intent_filter.h" #include "components/services/app_service/public/cpp/intent_filter_util.h" #include "components/services/app_service/public/cpp/intent_util.h" #include "url/gurl.h" @@ -264,7 +265,7 @@ absl::optional<std::string> PreferredAppsList::FindPreferredAppForIntent( const apps::mojom::IntentPtr& intent) const { absl::optional<std::string> best_match_app_id = absl::nullopt; - int best_match_level = apps_util::IntentFilterMatchLevel::kNone; + int best_match_level = static_cast<int>(IntentFilterMatchLevel::kNone); for (auto& preferred_app : preferred_apps_) { if (apps_util::IntentMatchesFilter(intent, preferred_app->intent_filter)) { int match_level =
diff --git a/components/signin/public/identity_manager/account_capabilities.cc b/components/signin/public/identity_manager/account_capabilities.cc index 58e5187..eb521b9f 100644 --- a/components/signin/public/identity_manager/account_capabilities.cc +++ b/components/signin/public/identity_manager/account_capabilities.cc
@@ -34,8 +34,7 @@ AccountCapabilities::GetSupportedAccountCapabilityNames() { static base::NoDestructor<std::vector<std::string>> kCapabilityNames{ {kCanOfferExtendedChromeSyncPromosCapabilityName, - kCanRunChromePrivacySandboxTrialsCapabilityName, - kIsSubjectToParentalControlsCapabilityName}}; + kCanRunChromePrivacySandboxTrialsCapabilityName}}; return *kCapabilityNames; } @@ -68,10 +67,6 @@ return GetCapabilityByName(kCanRunChromePrivacySandboxTrialsCapabilityName); } -signin::Tribool AccountCapabilities::is_subject_to_parental_controls() const { - return GetCapabilityByName(kIsSubjectToParentalControlsCapabilityName); -} - bool AccountCapabilities::UpdateWith(const AccountCapabilities& other) { bool modified = false;
diff --git a/components/signin/public/identity_manager/account_capabilities.h b/components/signin/public/identity_manager/account_capabilities.h index 6006382..9f85a273 100644 --- a/components/signin/public/identity_manager/account_capabilities.h +++ b/components/signin/public/identity_manager/account_capabilities.h
@@ -48,9 +48,6 @@ // Chrome can run privacy sandbox trials for accounts with this capability. signin::Tribool can_run_chrome_privacy_sandbox_trials() const; - // Chrome applies parental controls to accounts with this capability. - signin::Tribool is_subject_to_parental_controls() const; - // Whether none of the capabilities has `signin::Tribool::kUnknown`. bool AreAllCapabilitiesKnown() const;
diff --git a/components/signin/public/identity_manager/account_capabilities_test_mutator.cc b/components/signin/public/identity_manager/account_capabilities_test_mutator.cc index 3536410..c7db63b 100644 --- a/components/signin/public/identity_manager/account_capabilities_test_mutator.cc +++ b/components/signin/public/identity_manager/account_capabilities_test_mutator.cc
@@ -30,12 +30,6 @@ value; } -void AccountCapabilitiesTestMutator::set_is_subject_to_parental_controls( - bool value) { - capabilities_->capabilities_map_[kIsSubjectToParentalControlsCapabilityName] = - value; -} - void AccountCapabilitiesTestMutator::SetAllSupportedCapabilities(bool value) { for (const std::string& name : AccountCapabilities::GetSupportedAccountCapabilityNames()) {
diff --git a/components/signin/public/identity_manager/account_capabilities_test_mutator.h b/components/signin/public/identity_manager/account_capabilities_test_mutator.h index 182a197..28198b4 100644 --- a/components/signin/public/identity_manager/account_capabilities_test_mutator.h +++ b/components/signin/public/identity_manager/account_capabilities_test_mutator.h
@@ -19,7 +19,6 @@ // Exposes setters for the supported capabilities. void set_can_offer_extended_chrome_sync_promos(bool value); void set_can_run_chrome_privacy_sandbox_trials(bool value); - void set_is_subject_to_parental_controls(bool value); // Modifies all supported capabilities at once. void SetAllSupportedCapabilities(bool value);
diff --git a/components/signin/public/identity_manager/account_capabilities_unittest.cc b/components/signin/public/identity_manager/account_capabilities_unittest.cc index 01fc029e..81980687 100644 --- a/components/signin/public/identity_manager/account_capabilities_unittest.cc +++ b/components/signin/public/identity_manager/account_capabilities_unittest.cc
@@ -43,21 +43,6 @@ signin::Tribool::kFalse); } -TEST_F(AccountCapabilitiesTest, IsSubjectToParentalControls) { - AccountCapabilities capabilities; - EXPECT_EQ(capabilities.is_subject_to_parental_controls(), - signin::Tribool::kUnknown); - - AccountCapabilitiesTestMutator mutator(&capabilities); - mutator.set_is_subject_to_parental_controls(true); - EXPECT_EQ(capabilities.is_subject_to_parental_controls(), - signin::Tribool::kTrue); - - mutator.set_is_subject_to_parental_controls(false); - EXPECT_EQ(capabilities.is_subject_to_parental_controls(), - signin::Tribool::kFalse); -} - TEST_F(AccountCapabilitiesTest, AreAllCapabilitiesKnown_Empty) { AccountCapabilities capabilities; EXPECT_FALSE(capabilities.AreAllCapabilitiesKnown());
diff --git a/components/sync/driver/sync_service_crypto.cc b/components/sync/driver/sync_service_crypto.cc index 01ad736..e61dbd6 100644 --- a/components/sync/driver/sync_service_crypto.cc +++ b/components/sync/driver/sync_service_crypto.cc
@@ -368,16 +368,12 @@ state_.passphrase_key_derivation_params, passphrase); DCHECK(nigori); - std::string bootstrap_token = SerializeNigoriAsBootstrapToken(*nigori); - if (SetDecryptionKeyWithoutUpdatingBootstrapToken(std::move(nigori))) { - // Update the bootstrap token immediately, even if engine has new pending - // keys, which aren't decryptable with |nigori|, this is harmless as - // bootstrap token is ignored if it doesn't contain the right key. - delegate_->SetEncryptionBootstrapToken(bootstrap_token); - return true; - } + // Update the bootstrap token immediately, this is harmless as bootstrap token + // is ignored if it doesn't contain the right key. + delegate_->SetEncryptionBootstrapToken( + SerializeNigoriAsBootstrapToken(*nigori)); - return false; + return SetDecryptionKeyWithoutUpdatingBootstrapToken(std::move(nigori)); } void SyncServiceCrypto::SetDecryptionNigoriKey(std::unique_ptr<Nigori> nigori) { @@ -389,13 +385,12 @@ return; } - std::string bootstrap_token = SerializeNigoriAsBootstrapToken(*nigori); - if (SetDecryptionKeyWithoutUpdatingBootstrapToken(std::move(nigori))) { - // Update the bootstrap token immediately, even if engine has new pending - // keys, which aren't decryptable with |nigori|, this is harmless as - // bootstrap token is ignored if it doesn't contain the right key. - delegate_->SetEncryptionBootstrapToken(bootstrap_token); - } + // Update the bootstrap token immediately, this is harmless as bootstrap token + // is ignored if it doesn't contain the right key. + delegate_->SetEncryptionBootstrapToken( + SerializeNigoriAsBootstrapToken(*nigori)); + + SetDecryptionKeyWithoutUpdatingBootstrapToken(std::move(nigori)); } std::unique_ptr<Nigori> SyncServiceCrypto::GetDecryptionNigoriKey() const {
diff --git a/components/sync/driver/sync_service_crypto_unittest.cc b/components/sync/driver/sync_service_crypto_unittest.cc index d74978bf..a08238e8 100644 --- a/components/sync/driver/sync_service_crypto_unittest.cc +++ b/components/sync/driver/sync_service_crypto_unittest.cc
@@ -463,6 +463,40 @@ EXPECT_FALSE(crypto_.IsPassphraseRequired()); } +// Regression test for crbug.com/1306831. +TEST_F(SyncServiceCryptoTest, + ShouldStoreBootstrapTokenBeforeReconfiguringDataTypes) { + const std::string kTestPassphrase = "somepassphrase"; + + crypto_.SetSyncEngine(CoreAccountInfo(), &engine_); + ASSERT_FALSE(crypto_.IsPassphraseRequired()); + + crypto_.OnPassphraseRequired( + KeyDerivationParams::CreateForPbkdf2(), + MakeEncryptedData(kTestPassphrase, + KeyDerivationParams::CreateForPbkdf2())); + ASSERT_TRUE(crypto_.IsPassphraseRequired()); + + // Entering the correct passphrase should be accepted. + EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey(NotNull())) + .WillOnce( + [&](std::unique_ptr<Nigori>) { crypto_.OnPassphraseAccepted(); }); + + // Order of SetEncryptionBootstrapToken() and + // ReconfigureDataTypesDueToCrypto() (assuming passphrase is not required upon + // reconfiguration) is important as clients rely on this to detect whether + // GetDecryptionNigoriKey() can be called. + testing::InSequence seq; + EXPECT_CALL(delegate_, + SetEncryptionBootstrapToken(BootstrapTokenDerivedFrom( + kTestPassphrase, KeyDerivationParams::CreateForPbkdf2()))); + // The current implementation issues two reconfigurations: one immediately + // after checking the passphrase in the UI thread and a second time later when + // the engine confirms with OnPassphraseAccepted(). + EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(2); + ASSERT_TRUE(crypto_.SetDecryptionPassphrase(kTestPassphrase)); +} + TEST_F(SyncServiceCryptoTest, ShouldSetupDecryptionWithBootstrapToken) { const std::string kTestPassphrase = "somepassphrase"; @@ -607,7 +641,6 @@ // Passing wrong decryption key should be ignored. EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(0); EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey).Times(0); - EXPECT_CALL(delegate_, SetEncryptionBootstrapToken).Times(0); crypto_.SetDecryptionNigoriKey(Nigori::CreateByDerivation( KeyDerivationParams::CreateForPbkdf2(), "wrongpassphrase")); EXPECT_TRUE(crypto_.IsPassphraseRequired());
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc index 2e3a5954..5d53c26 100644 --- a/components/viz/service/display/skia_renderer.cc +++ b/components/viz/service/display/skia_renderer.cc
@@ -1429,34 +1429,20 @@ // device space, it will be contained in in the original scissor. // Applying the scissor explicitly means avoiding a clipRect() call and // allows more quads to be batched together in a DrawEdgeAAImageSet call - float left_inset = local_scissor.x() - visible_rect.x(); - float top_inset = local_scissor.y() - visible_rect.y(); - float right_inset = visible_rect.right() - local_scissor.right(); - float bottom_inset = visible_rect.bottom() - local_scissor.bottom(); + float x_epsilon = kAAEpsilon / content_device_transform.matrix().rc(0, 0); + float y_epsilon = kAAEpsilon / content_device_transform.matrix().rc(1, 1); - // The scissor is a non-AA clip, so we unset the bit flag for clipped edges. - if (left_inset >= kAAEpsilon) { + // The scissor is a non-AA clip, so unset the bit flag for clipped edges. + if (local_scissor.x() - visible_rect.x() >= x_epsilon) aa_flags &= ~SkCanvas::kLeft_QuadAAFlag; - } else { - left_inset = 0; - } - if (top_inset >= kAAEpsilon) { + if (local_scissor.y() - visible_rect.y() >= y_epsilon) aa_flags &= ~SkCanvas::kTop_QuadAAFlag; - } else { - top_inset = 0; - } - if (right_inset >= kAAEpsilon) { + if (visible_rect.right() - local_scissor.right() >= x_epsilon) aa_flags &= ~SkCanvas::kRight_QuadAAFlag; - } else { - right_inset = 0; - } - if (bottom_inset >= kAAEpsilon) { + if (visible_rect.bottom() - local_scissor.bottom() >= y_epsilon) aa_flags &= ~SkCanvas::kBottom_QuadAAFlag; - } else { - bottom_inset = 0; - } - visible_rect.Inset(left_inset, top_inset, right_inset, bottom_inset); + visible_rect.Intersect(local_scissor); vis_tex_coords = visible_rect; scissor_rect.reset(); }
diff --git a/content/browser/android/drop_data_android.cc b/content/browser/android/drop_data_android.cc index c9a8e95..1fd7bc11 100644 --- a/content/browser/android/drop_data_android.cc +++ b/content/browser/android/drop_data_android.cc
@@ -37,6 +37,7 @@ ScopedJavaLocalRef<jobject> jgurl; if (!drop_data.url.is_empty()) { jgurl = url::GURLAndroid::FromNativeGURL(env, drop_data.url); + jtext = ConvertUTF16ToJavaString(env, drop_data.url_title); } // If file_contents is not empty, user is trying to drag image out of the
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc index 349c873..580347c 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/check.h" +#include "base/metrics/histogram_functions.h" #include "base/time/time.h" #include "content/browser/attribution_reporting/attribution_aggregatable_source.h" #include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h" @@ -46,6 +47,21 @@ } // namespace +// static +void AttributionDataHostManagerImpl::RecordRegisteredDataPerDataHost( + const ReceiverData& receiver_data) { + int count = receiver_data.num_data_registered; + DCHECK_GE(count, 0); + + if (receiver_data.source_declared_destination_origin.opaque()) { + base::UmaHistogramExactLinear("Conversions.RegisteredTriggersPerDataHost", + count, 101); + } else { + base::UmaHistogramExactLinear("Conversions.RegisteredSourcesPerDataHost", + count, 101); + } +} + AttributionDataHostManagerImpl::AttributionDataHostManagerImpl( AttributionManager* attribution_manager) : attribution_manager_(attribution_manager) { @@ -58,7 +74,11 @@ base::Unretained(this))); } -AttributionDataHostManagerImpl::~AttributionDataHostManagerImpl() = default; +AttributionDataHostManagerImpl::~AttributionDataHostManagerImpl() { + for (const auto& [id, data] : receiver_data_) { + RecordRegisteredDataPerDataHost(data); + } +} void AttributionDataHostManagerImpl::RegisterDataHost( mojo::PendingReceiver<blink::mojom::AttributionDataHost> data_host, @@ -109,9 +129,12 @@ if (data->destination.opaque()) return; - auto [it, inserted] = - receiver_data_.emplace(receivers_.current_receiver(), data->destination); - if (!inserted && data->destination != it->second) + auto [it, inserted] = receiver_data_.emplace( + receivers_.current_receiver(), + ReceiverData{.source_declared_destination_origin = data->destination, + .num_data_registered = 0}); + if (!inserted && + data->destination != it->second.source_declared_destination_origin) return; const FrozenContext& context = receivers_.current_context(); @@ -128,9 +151,13 @@ break; case AttributionSourceType::kEvent: // For event source verify that all sources are consistent. - auto result = receiver_data_.emplace(receivers_.current_receiver(), - data->destination); - if (!result.second && data->destination != result.first->second) + auto result = receiver_data_.emplace( + receivers_.current_receiver(), + ReceiverData{.source_declared_destination_origin = data->destination, + .num_data_registered = 0}); + if (!result.second && + data->destination != + result.first->second.source_declared_destination_origin) return; break; } @@ -156,6 +183,8 @@ if (!aggregatable_source.has_value()) return; + it->second.num_data_registered++; + StorableSource storable_source(CommonSourceInfo( data->source_event_id, context.context_origin, data->destination, reporting_origin, source_time, @@ -173,9 +202,11 @@ blink::mojom::AttributionTriggerDataPtr data) { // TODO(linnan): Log metrics for early returns. - auto [it, inserted] = - receiver_data_.emplace(receivers_.current_receiver(), url::Origin()); - if (!it->second.opaque()) + auto [it, inserted] = receiver_data_.emplace( + receivers_.current_receiver(), + ReceiverData{.source_declared_destination_origin = url::Origin(), + .num_data_registered = 0}); + if (!it->second.source_declared_destination_origin.opaque()) return; const FrozenContext& context = receivers_.current_context(); @@ -231,6 +262,8 @@ if (!aggregatable_trigger.has_value()) return; + it->second.num_data_registered++; + AttributionTrigger trigger( /*destination_origin=*/context.context_origin, reporting_origin, std::move(*filters), @@ -242,7 +275,12 @@ } void AttributionDataHostManagerImpl::OnDataHostDisconnected() { - receiver_data_.erase(receivers_.current_receiver()); + auto iter = receiver_data_.find(receivers_.current_receiver()); + DCHECK(iter != receiver_data_.end()); + + RecordRegisteredDataPerDataHost(iter->second); + + receiver_data_.erase(iter); } } // namespace content
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h index 4949e90..3423e254 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
@@ -74,6 +74,14 @@ url::Origin destination; }; + struct ReceiverData { + url::Origin source_declared_destination_origin; + int num_data_registered; + }; + + static void RecordRegisteredDataPerDataHost( + const ReceiverData& receiver_data); + // blink::mojom::AttributionDataHost: void SourceDataAvailable( blink::mojom::AttributionSourceDataPtr data) override; @@ -94,7 +102,7 @@ // `AttributionDataHost`. // // If the receiver is processing triggers, stores an opaque origin. - base::flat_map<mojo::ReceiverId, url::Origin> receiver_data_; + base::flat_map<mojo::ReceiverId, ReceiverData> receiver_data_; // Map which stores pending receivers for data hosts which are going to // register sources associated with a navigation. These are not added to
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc index a321bb0..25d2655 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -13,6 +13,7 @@ #include "base/containers/flat_map.h" #include "base/strings/string_number_conversions.h" +#include "base/test/metrics/histogram_tester.h" #include "content/browser/attribution_reporting/attribution_aggregatable_source.h" #include "content/browser/attribution_reporting/attribution_manager.h" #include "content/browser/attribution_reporting/attribution_source_type.h" @@ -47,15 +48,18 @@ public: AttributionDataHostManagerImplTest() : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), - data_host_manager_(&mock_manager_) {} + data_host_manager_( + std::make_unique<AttributionDataHostManagerImpl>(&mock_manager_)) {} protected: BrowserTaskEnvironment task_environment_; MockAttributionManager mock_manager_; - AttributionDataHostManagerImpl data_host_manager_; + std::unique_ptr<AttributionDataHostManagerImpl> data_host_manager_; }; TEST_F(AttributionDataHostManagerImplTest, SourceDataHost_SourceRegistered) { + base::HistogramTester histograms; + auto page_origin = url::Origin::Create(GURL("https://page.example")); auto destination_origin = url::Origin::Create(GURL("https://trigger.example")); @@ -75,7 +79,7 @@ .Build()))))); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), page_origin); auto source_data = blink::mojom::AttributionSourceData::New(); @@ -93,10 +97,17 @@ .Build(); data_host_remote->SourceDataAvailable(std::move(source_data)); data_host_remote.FlushForTesting(); + + data_host_manager_.reset(); + + histograms.ExpectBucketCount("Conversions.RegisteredSourcesPerDataHost", 1, + 1); } TEST_F(AttributionDataHostManagerImplTest, SourceDataHost_OriginTrustworthyChecksPerformed) { + base::HistogramTester histograms; + const char kLocalHost[] = "http://localhost"; struct { @@ -135,7 +146,7 @@ EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.source_expected); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), url::Origin::Create(GURL(test_case.source_origin))); @@ -152,6 +163,14 @@ Mock::VerifyAndClear(&mock_manager_); } + + data_host_manager_.reset(); + + histograms.ExpectBucketCount("Conversions.RegisteredSourcesPerDataHost", 1, + 3); + // Untrustworthy source origin doesn't register data host. + histograms.ExpectBucketCount("Conversions.RegisteredSourcesPerDataHost", 0, + 2); } TEST_F(AttributionDataHostManagerImplTest, @@ -161,7 +180,7 @@ EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.valid); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), url::Origin::Create(GURL("https://page.example"))); @@ -205,7 +224,7 @@ EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.valid); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), url::Origin::Create(GURL("https://page.example"))); @@ -227,6 +246,8 @@ TEST_F(AttributionDataHostManagerImplTest, SourceDataHost_ReceiverDestinationCheckPerformed) { + base::HistogramTester histograms; + Checkpoint checkpoint; { InSequence seq; @@ -246,7 +267,7 @@ auto reporting_origin = url::Origin::Create(GURL("https://reporter.example")); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), page_origin); auto source_data = blink::mojom::AttributionSourceData::New(); @@ -273,6 +294,11 @@ checkpoint.Call(3); data_host_remote->SourceDataAvailable(std::move(source_data)); data_host_remote.FlushForTesting(); + + data_host_manager_.reset(); + + histograms.ExpectBucketCount("Conversions.RegisteredSourcesPerDataHost", 2, + 1); } TEST_F(AttributionDataHostManagerImplTest, @@ -314,7 +340,7 @@ EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.valid); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), url::Origin::Create(GURL("https://page.example"))); @@ -333,6 +359,8 @@ } TEST_F(AttributionDataHostManagerImplTest, TriggerDataHost_TriggerRegistered) { + base::HistogramTester histograms; + auto destination_origin = url::Origin::Create(GURL("https://trigger.example")); auto reporting_origin = url::Origin::Create(GURL("https://reporter.example")); @@ -368,7 +396,7 @@ }))); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), destination_origin); auto trigger_data = blink::mojom::AttributionTriggerData::New(); @@ -400,10 +428,17 @@ data_host_remote->TriggerDataAvailable(std::move(trigger_data)); data_host_remote.FlushForTesting(); + + data_host_manager_.reset(); + + histograms.ExpectBucketCount("Conversions.RegisteredTriggersPerDataHost", 1, + 1); } TEST_F(AttributionDataHostManagerImplTest, TriggerDataHost_OriginTrustworthyChecksPerformed) { + base::HistogramTester histograms; + const char kLocalHost[] = "http://localhost"; struct { @@ -432,7 +467,7 @@ EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.trigger_expected); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), url::Origin::Create(GURL(test_case.destination_origin))); @@ -449,6 +484,14 @@ Mock::VerifyAndClear(&mock_manager_); } + + data_host_manager_.reset(); + + histograms.ExpectBucketCount("Conversions.RegisteredTriggersPerDataHost", 1, + 3); + // Untrustworthy destination origin doesn't register data host. + histograms.ExpectBucketCount("Conversions.RegisteredTriggersPerDataHost", 0, + 1); } TEST_F(AttributionDataHostManagerImplTest, @@ -458,7 +501,7 @@ EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.valid); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), url::Origin::Create(GURL("https://trigger.example"))); @@ -486,7 +529,7 @@ EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.valid); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), url::Origin::Create(GURL("https://trigger.example"))); @@ -520,7 +563,7 @@ EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.valid); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), url::Origin::Create(GURL("https://trigger.example"))); @@ -562,7 +605,7 @@ EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.expected); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), url::Origin::Create(GURL("https://trigger.example"))); @@ -593,6 +636,8 @@ TEST_F(AttributionDataHostManagerImplTest, TriggerDataHost_ReceiverModeCheckPerformed) { + base::HistogramTester histograms; + Checkpoint checkpoint; { InSequence seq; @@ -611,7 +656,7 @@ auto reporting_origin = url::Origin::Create(GURL("https://reporter.example")); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), destination_origin); auto trigger_data = blink::mojom::AttributionTriggerData::New(); @@ -644,10 +689,18 @@ data_host_remote->TriggerDataAvailable(std::move(trigger_data)); data_host_remote.FlushForTesting(); + + data_host_manager_.reset(); + + histograms.ExpectTotalCount("Conversions.RegisteredSourcesPerDataHost", 0); + histograms.ExpectBucketCount("Conversions.RegisteredTriggersPerDataHost", 3, + 1); } TEST_F(AttributionDataHostManagerImplTest, SourceDataHost_ReceiverModeCheckPerformed) { + base::HistogramTester histograms; + Checkpoint checkpoint; { InSequence seq; @@ -667,7 +720,7 @@ auto reporting_origin = url::Origin::Create(GURL("https://reporter.example")); mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote; - data_host_manager_.RegisterDataHost( + data_host_manager_->RegisterDataHost( data_host_remote.BindNewPipeAndPassReceiver(), page_origin); auto source_data = blink::mojom::AttributionSourceData::New(); @@ -700,6 +753,12 @@ data_host_remote->SourceDataAvailable(std::move(source_data)); data_host_remote.FlushForTesting(); + + data_host_manager_.reset(); + + histograms.ExpectBucketCount("Conversions.RegisteredSourcesPerDataHost", 3, + 1); + histograms.ExpectTotalCount("Conversions.RegisteredTriggersPerDataHost", 0); } } // namespace content
diff --git a/content/browser/attribution_reporting/attribution_report.cc b/content/browser/attribution_reporting/attribution_report.cc index b6023208..0d30bf7 100644 --- a/content/browser/attribution_reporting/attribution_report.cc +++ b/content/browser/attribution_reporting/attribution_report.cc
@@ -26,6 +26,16 @@ namespace content { +namespace { + +int64_t EncodeTimeRoundDownToWholeDayInSeconds(base::Time time) { + return (time - base::Time::UnixEpoch()) + .FloorToMultiple(base::Days(1)) + .InSeconds(); +} + +} // namespace + AttributionReport::EventLevelData::EventLevelData( uint64_t trigger_data, int64_t priority, @@ -203,13 +213,10 @@ dict.emplace("attribution_destination", common_info.ConversionDestination().Serialize()); - // source_registration_time is rounded to the nearest whole day and in - // seconds. + // source_registration_time is rounded down to whole day and in seconds. dict.emplace("source_registration_time", - base::NumberToString( - (common_info.impression_time() - base::Time::UnixEpoch()) - .RoundToMultiple(base::Days(1)) - .InSeconds())); + base::NumberToString(EncodeTimeRoundDownToWholeDayInSeconds( + common_info.impression_time()))); if (absl::optional<uint64_t> debug_key = common_info.debug_key()) dict.emplace("source_debug_key", base::NumberToString(*debug_key)); @@ -249,6 +256,10 @@ static constexpr char kVersion[] = ""; value.emplace("version", kVersion); + value.emplace("source_registration_time", + EncodeTimeRoundDownToWholeDayInSeconds( + common_source_info.impression_time())); + absl::optional<std::vector<uint8_t>> bytes = cbor::Writer::Write(cbor::Value(std::move(value))); DCHECK(bytes.has_value());
diff --git a/content/browser/attribution_reporting/attribution_report_unittest.cc b/content/browser/attribution_reporting/attribution_report_unittest.cc index 7e9a20d4..d687e2ff 100644 --- a/content/browser/attribution_reporting/attribution_report_unittest.cc +++ b/content/browser/attribution_reporting/attribution_report_unittest.cc
@@ -32,9 +32,10 @@ {"14288 * 86400000", 1234483200000, "1234483200"}, {"14288 * 86400000 + 1", 1234483200001, "1234483200"}, {"14288.5 * 86400000 - 1", 1234526399999, "1234483200"}, - {"14288.5 * 86400000", 1234526400000, "1234569600"}, - {"14288.5 * 86400000 + 1", 1234526400001, "1234569600"}, - {"14289 * 86400000 -1", 1234569599999, "1234569600"}, + {"14288.5 * 86400000", 1234526400000, "1234483200"}, + {"14288.5 * 86400000 + 1", 1234526400001, "1234483200"}, + {"14289 * 86400000 -1", 1234569599999, "1234483200"}, + {"14289 * 86400000", 1234569600000, "1234569600"}, }; for (const auto& test_case : kTestCases) { @@ -81,21 +82,14 @@ } TEST(AttributionReportTest, PrivacyBudgetKey) { - // Pre-hashed CBOR bytes - // { 0xA4, 0x67, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x60, 0x6B, 0x64, - // 0x65, 0x73, 0x74, 0x69, 0x6E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x77, 0x68, - // 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, 0x63, 0x6F, 0x6E, 0x76, 0x65, - // 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x2E, 0x74, 0x65, 0x73, 0x74, 0x6B, 0x73, - // 0x6F, 0x75, 0x72, 0x63, 0x65, 0x5F, 0x73, 0x69, 0x74, 0x65, 0x77, 0x68, - // 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, 0x69, 0x6D, 0x70, 0x72, 0x65, - // 0x73, 0x73, 0x69, 0x6F, 0x6E, 0x2E, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, - // 0x65, 0x70, 0x6F, 0x72, 0x74, 0x69, 0x6E, 0x67, 0x5F, 0x6F, 0x72, 0x69, - // 0x67, 0x69, 0x6E, 0x73, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, - // 0x72, 0x65, 0x70, 0x6F, 0x72, 0x74, 0x2E, 0x74, 0x65, 0x73, 0x74 } + // Pre-hashed CBOR bytes, base64-encoded for brevity: + // "pWd2ZXJzaW9uYGtkZXN0aW5hdGlvbndodHRwczovL2NvbnZlcnNpb24udGVzdGtzb3VyY2Vf" + // "c2l0ZXdodHRwczovL2ltcHJlc3Npb24udGVzdHByZXBvcnRpbmdfb3JpZ2luc2h0dHBzOi8v" + // "cmVwb3J0LnRlc3R4GHNvdXJjZV9yZWdpc3RyYXRpb25fdGltZRpJlLgA" - // base64 encoded SHA256 hash string of the bytes above. + // base64-encoded SHA256 hash string of the bytes above. const std::string kExpectedPrivacyBudgetKey( - "NOM7HGJIb2ReR2jRlz1E0WIywBdUB/qLC6nCFyDqmRQ="); + "aOEbtVxG8dYzAR2K/xuW/OppNaQikp5RdjAXshOQ9w8="); AttributionReport report = ReportBuilder(AttributionInfoBuilder(
diff --git a/content/browser/attribution_reporting/attribution_src_browsertest.cc b/content/browser/attribution_reporting/attribution_src_browsertest.cc index 65a223b..7ceadbef 100644 --- a/content/browser/attribution_reporting/attribution_src_browsertest.cc +++ b/content/browser/attribution_reporting/attribution_src_browsertest.cc
@@ -79,11 +79,6 @@ void SetUpOnMainThread() override { host_resolver()->AddRule("*", "127.0.0.1"); - embedded_test_server()->ServeFilesFromSourceDirectory( - "content/test/data/attribution_reporting"); - embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data"); - content::SetupCrossSiteRedirector(embedded_test_server()); - ASSERT_TRUE(embedded_test_server()->Start()); https_server_ = std::make_unique<net::EmbeddedTestServer>( net::EmbeddedTestServer::TYPE_HTTPS); @@ -91,7 +86,6 @@ net::test_server::RegisterDefaultHandlers(https_server_.get()); https_server_->ServeFilesFromSourceDirectory( "content/test/data/attribution_reporting"); - https_server_->ServeFilesFromSourceDirectory("content/test/data"); SetupCrossSiteRedirector(https_server_.get()); ASSERT_TRUE(https_server_->Start()); }
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc index acaa8d3..e80c191 100644 --- a/content/browser/back_forward_cache_features_browsertest.cc +++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -159,6 +159,30 @@ ExpectRestored(FROM_HERE); } +// Confirms that an active page using a dedicated worker that calls +// importScripts won't trigger an eviction IPC, causing the page to reload. +// Regression test for https://crbug.com/1305041. +IN_PROC_BROWSER_TEST_P( + BackForwardCacheWithDedicatedWorkerBrowserTest, + PageWithDedicatedWorkerAndImportScriptsWontTriggerReload) { + CreateHttpsServer(); + ASSERT_TRUE(https_server()->Start()); + + EXPECT_TRUE(NavigateToURL( + shell(), https_server()->GetURL( + "a.test", + "/back_forward_cache/" + "page_with_dedicated_worker_and_importscripts.html"))); + // Wait until the importScripts() call finished running. + EXPECT_EQ(42, EvalJs(current_frame_host(), "window.receivedMessagePromise")); + + // If the importScripts() call triggered an eviction, a reload will be + // triggered due to the "evict after docment is restored" will be hit, as the + // page is not in back/forward cache. + EXPECT_FALSE( + web_contents()->GetPrimaryFrameTree().root()->navigation_request()); +} + // Confirms that a page using a dedicated worker with WebTransport is not // cached. // TODO(crbug.com/1299018): Flakes on Linux.
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.cc b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.cc index 63319aa..157ca86 100644 --- a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.cc +++ b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.cc
@@ -53,11 +53,11 @@ } void BrowsingTopicsSiteDataManagerImpl::OnBrowsingTopicsApiUsed( - const browsing_topics::HashedHost& hashed_top_host, + const browsing_topics::HashedHost& hashed_main_frame_host, const base::flat_set<browsing_topics::HashedDomain>& hashed_context_domains) { storage_.AsyncCall(&BrowsingTopicsSiteDataStorage::OnBrowsingTopicsApiUsed) - .WithArgs(hashed_top_host, hashed_context_domains); + .WithArgs(hashed_main_frame_host, hashed_context_domains); } } // namespace content
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h index d81c2bd7..10dc0a5 100644 --- a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h +++ b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h
@@ -39,7 +39,7 @@ GetBrowsingTopicsApiUsageCallback callback) override; void OnBrowsingTopicsApiUsed( - const browsing_topics::HashedHost& hashed_top_host, + const browsing_topics::HashedHost& hashed_main_frame_host, const base::flat_set<browsing_topics::HashedDomain>& hashed_context_domains) override;
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl_unittest.cc b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl_unittest.cc index 11f0161..43990e01 100644 --- a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl_unittest.cc +++ b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl_unittest.cc
@@ -36,7 +36,7 @@ base::Time initial_time = base::Time::Now(); topics_manager_->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)}); size_t query_result_count = 0; @@ -72,7 +72,7 @@ EXPECT_TRUE(result.success); EXPECT_EQ(result.api_usage_contexts.size(), 1u); - EXPECT_EQ(result.api_usage_contexts[0].hashed_top_host, + EXPECT_EQ(result.api_usage_contexts[0].hashed_main_frame_host, browsing_topics::HashedHost(123)); EXPECT_EQ(result.api_usage_contexts[0].hashed_context_domain, browsing_topics::HashedDomain(456));
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_storage.cc b/content/browser/browsing_topics/browsing_topics_site_data_storage.cc index c6ef8ea..2454ae7 100644 --- a/content/browser/browsing_topics/browsing_topics_site_data_storage.cc +++ b/content/browser/browsing_topics/browsing_topics_site_data_storage.cc
@@ -68,7 +68,7 @@ static constexpr char kGetApiUsageSql[] = // clang-format off - "SELECT hashed_context_domain,hashed_top_host,last_usage_time " + "SELECT hashed_context_domain,hashed_main_frame_host,last_usage_time " "FROM browsing_topics_api_usages " "WHERE last_usage_time>=? AND last_usage_time<? " "ORDER BY last_usage_time DESC " @@ -90,7 +90,7 @@ browsing_topics::ApiUsageContext usage_context; usage_context.hashed_context_domain = browsing_topics::HashedDomain(statement.ColumnInt64(0)); - usage_context.hashed_top_host = + usage_context.hashed_main_frame_host = browsing_topics::HashedHost(statement.ColumnInt64(1)); usage_context.time = statement.ColumnTime(2); @@ -104,7 +104,7 @@ } void BrowsingTopicsSiteDataStorage::OnBrowsingTopicsApiUsed( - const browsing_topics::HashedHost& hashed_top_host, + const browsing_topics::HashedHost& hashed_main_frame_host, const base::flat_set<browsing_topics::HashedDomain>& hashed_context_domains) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -123,14 +123,14 @@ static constexpr char kInsertApiUsageSql[] = // clang-format off "INSERT OR REPLACE INTO browsing_topics_api_usages " - "(hashed_context_domain,hashed_top_host,last_usage_time) " + "(hashed_context_domain,hashed_main_frame_host,last_usage_time) " "VALUES (?,?,?)"; // clang-format on sql::Statement insert_api_usage_statement( db_->GetCachedStatement(SQL_FROM_HERE, kInsertApiUsageSql)); insert_api_usage_statement.BindInt64(0, hashed_context_domain.value()); - insert_api_usage_statement.BindInt64(1, hashed_top_host.value()); + insert_api_usage_statement.BindInt64(1, hashed_main_frame_host.value()); insert_api_usage_statement.BindTime(2, current_time); if (!insert_api_usage_statement.Run()) @@ -211,9 +211,9 @@ // clang-format off "CREATE TABLE IF NOT EXISTS browsing_topics_api_usages(" "hashed_context_domain INTEGER NOT NULL," - "hashed_top_host INTEGER NOT NULL," + "hashed_main_frame_host INTEGER NOT NULL," "last_usage_time INTEGER NOT NULL," - "PRIMARY KEY (hashed_context_domain,hashed_top_host))"; + "PRIMARY KEY (hashed_context_domain,hashed_main_frame_host))"; // clang-format on if (!db_->Execute(kBrowsingTopicsApiUsagesTableSql)) return false;
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_storage.h b/content/browser/browsing_topics/browsing_topics_site_data_storage.h index 5e0815c3..590d3b58 100644 --- a/content/browser/browsing_topics/browsing_topics_site_data_storage.h +++ b/content/browser/browsing_topics/browsing_topics_site_data_storage.h
@@ -56,7 +56,7 @@ // Persist the browsing topics api usage context to storage. Called when the // usage is detected in a context on a page. void OnBrowsingTopicsApiUsed( - const browsing_topics::HashedHost& hashed_top_host, + const browsing_topics::HashedHost& hashed_main_frame_host, const base::flat_set<browsing_topics::HashedDomain>& hashed_context_domains);
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_storage_unittest.cc b/content/browser/browsing_topics/browsing_topics_site_data_storage_unittest.cc index 76e8d204..1e3bec1 100644 --- a/content/browser/browsing_topics/browsing_topics_site_data_storage_unittest.cc +++ b/content/browser/browsing_topics/browsing_topics_site_data_storage_unittest.cc
@@ -123,7 +123,7 @@ // and [sqlite_autoindex_meta_1]. EXPECT_EQ(3u, sql::test::CountSQLIndices(&db)); - // `hashed_context_domain`, `hashed_top_host`, and `last_usage_time`. + // `hashed_context_domain`, `hashed_main_frame_host`, and `last_usage_time`. EXPECT_EQ(3u, sql::test::CountTableColumns(&db, "browsing_topics_api_usages")); @@ -206,7 +206,7 @@ TEST_F(BrowsingTopicsSiteDataStorageTest, OnBrowsingTopicsApiUsed_SingleEntry) { OpenDatabase(); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)}); CloseDatabase(); @@ -215,17 +215,18 @@ EXPECT_EQ(1u, CountApiUsagesEntries(db)); const char kGetAllEntriesSql[] = - "SELECT hashed_context_domain, hashed_top_host, last_usage_time FROM " + "SELECT hashed_context_domain, hashed_main_frame_host, last_usage_time " + "FROM " "browsing_topics_api_usages"; sql::Statement s(db.GetUniqueStatement(kGetAllEntriesSql)); EXPECT_TRUE(s.Step()); int64_t hashed_context_domain = s.ColumnInt64(0); - int64_t hashed_top_host = s.ColumnInt64(1); + int64_t hashed_main_frame_host = s.ColumnInt64(1); base::Time time = s.ColumnTime(2); EXPECT_EQ(hashed_context_domain, 456); - EXPECT_EQ(hashed_top_host, 123); + EXPECT_EQ(hashed_main_frame_host, 123); EXPECT_EQ(time, base::Time::Now()); EXPECT_FALSE(s.Step()); @@ -235,17 +236,17 @@ OnBrowsingTopicsApiUsed_MultipleEntries) { OpenDatabase(); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(123)}); task_environment_.FastForwardBy(base::Seconds(1)); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(456), browsing_topics::HashedDomain(789)}); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(456), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(456), /*hashed_context_domains=*/{browsing_topics::HashedDomain(789)}); CloseDatabase(); @@ -254,9 +255,10 @@ EXPECT_EQ(4u, CountApiUsagesEntries(db)); const char kGetAllEntriesSql[] = - "SELECT hashed_context_domain, hashed_top_host, last_usage_time FROM " + "SELECT hashed_context_domain, hashed_main_frame_host, last_usage_time " + "FROM " "browsing_topics_api_usages " - "ORDER BY last_usage_time, hashed_top_host, hashed_context_domain"; + "ORDER BY last_usage_time, hashed_main_frame_host, hashed_context_domain"; sql::Statement s(db.GetUniqueStatement(kGetAllEntriesSql)); @@ -264,11 +266,11 @@ EXPECT_TRUE(s.Step()); int64_t hashed_context_domain = s.ColumnInt64(0); - int64_t hashed_top_host = s.ColumnInt64(1); + int64_t hashed_main_frame_host = s.ColumnInt64(1); base::Time time = s.ColumnTime(2); EXPECT_EQ(hashed_context_domain, 123); - EXPECT_EQ(hashed_top_host, 123); + EXPECT_EQ(hashed_main_frame_host, 123); EXPECT_EQ(time, base::Time::Now() - base::Seconds(1)); } @@ -276,11 +278,11 @@ EXPECT_TRUE(s.Step()); int64_t hashed_context_domain = s.ColumnInt64(0); - int64_t hashed_top_host = s.ColumnInt64(1); + int64_t hashed_main_frame_host = s.ColumnInt64(1); base::Time time = s.ColumnTime(2); EXPECT_EQ(hashed_context_domain, 456); - EXPECT_EQ(hashed_top_host, 123); + EXPECT_EQ(hashed_main_frame_host, 123); EXPECT_EQ(time, base::Time::Now()); } @@ -288,11 +290,11 @@ EXPECT_TRUE(s.Step()); int64_t hashed_context_domain = s.ColumnInt64(0); - int64_t hashed_top_host = s.ColumnInt64(1); + int64_t hashed_main_frame_host = s.ColumnInt64(1); base::Time time = s.ColumnTime(2); EXPECT_EQ(hashed_context_domain, 789u); - EXPECT_EQ(hashed_top_host, 123); + EXPECT_EQ(hashed_main_frame_host, 123); EXPECT_EQ(time, base::Time::Now()); } @@ -300,11 +302,11 @@ EXPECT_TRUE(s.Step()); int64_t hashed_context_domain = s.ColumnInt64(0); - int64_t hashed_top_host = s.ColumnInt64(1); + int64_t hashed_main_frame_host = s.ColumnInt64(1); base::Time time = s.ColumnTime(2); EXPECT_EQ(hashed_context_domain, 789u); - EXPECT_EQ(hashed_top_host, 456); + EXPECT_EQ(hashed_main_frame_host, 456); EXPECT_EQ(time, base::Time::Now()); } @@ -315,13 +317,13 @@ OpenDatabase(); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(123)}); task_environment_.FastForwardBy(base::Seconds(1)); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)}); task_environment_.FastForwardBy(base::Seconds(1)); @@ -335,14 +337,14 @@ EXPECT_TRUE(result.success); EXPECT_EQ(result.api_usage_contexts.size(), 2u); - EXPECT_EQ(result.api_usage_contexts[0].hashed_top_host, + EXPECT_EQ(result.api_usage_contexts[0].hashed_main_frame_host, browsing_topics::HashedHost(123)); EXPECT_EQ(result.api_usage_contexts[0].hashed_context_domain, browsing_topics::HashedDomain(456)); EXPECT_EQ(result.api_usage_contexts[0].time, base::Time::Now() - base::Seconds(1)); - EXPECT_EQ(result.api_usage_contexts[1].hashed_top_host, + EXPECT_EQ(result.api_usage_contexts[1].hashed_main_frame_host, browsing_topics::HashedHost(123)); EXPECT_EQ(result.api_usage_contexts[1].hashed_context_domain, browsing_topics::HashedDomain(123)); @@ -355,13 +357,13 @@ OpenDatabase(); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(123)}); task_environment_.FastForwardBy(base::Seconds(1)); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)}); task_environment_.FastForwardBy(base::Seconds(1)); @@ -374,7 +376,7 @@ EXPECT_TRUE(result.success); EXPECT_EQ(result.api_usage_contexts.size(), 1u); - EXPECT_EQ(result.api_usage_contexts[0].hashed_top_host, + EXPECT_EQ(result.api_usage_contexts[0].hashed_main_frame_host, browsing_topics::HashedHost(123)); EXPECT_EQ(result.api_usage_contexts[0].hashed_context_domain, browsing_topics::HashedDomain(456)); @@ -392,13 +394,13 @@ OpenDatabase(); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(123)}); task_environment_.FastForwardBy(base::Seconds(1)); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)}); task_environment_.FastForwardBy(base::Seconds(1)); @@ -412,17 +414,18 @@ // The `ExpireDataBefore()` should have deleted the first inserted entry. const char kGetAllEntriesSql[] = - "SELECT hashed_context_domain, hashed_top_host, last_usage_time FROM " + "SELECT hashed_context_domain, hashed_main_frame_host, last_usage_time " + "FROM " "browsing_topics_api_usages"; sql::Statement s(db.GetUniqueStatement(kGetAllEntriesSql)); EXPECT_TRUE(s.Step()); int64_t hashed_context_domain = s.ColumnInt64(0); - int64_t hashed_top_host = s.ColumnInt64(1); + int64_t hashed_main_frame_host = s.ColumnInt64(1); base::Time time = s.ColumnTime(2); EXPECT_EQ(hashed_context_domain, 456); - EXPECT_EQ(hashed_top_host, 123); + EXPECT_EQ(hashed_main_frame_host, 123); EXPECT_EQ(time, base::Time::Now() - base::Seconds(1)); EXPECT_FALSE(s.Step()); @@ -445,13 +448,13 @@ OpenDatabase(); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(123)}); task_environment_.FastForwardBy(base::Seconds(1)); topics_storage()->OnBrowsingTopicsApiUsed( - /*hashed_top_host=*/browsing_topics::HashedHost(123), + /*hashed_main_frame_host=*/browsing_topics::HashedHost(123), /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)}); task_environment_.FastForwardBy(base::Seconds(1)); @@ -466,7 +469,7 @@ EXPECT_TRUE(result.success); EXPECT_EQ(result.api_usage_contexts.size(), 1u); - EXPECT_EQ(result.api_usage_contexts[0].hashed_top_host, + EXPECT_EQ(result.api_usage_contexts[0].hashed_main_frame_host, browsing_topics::HashedHost(123)); EXPECT_EQ(result.api_usage_contexts[0].hashed_context_domain, browsing_topics::HashedDomain(456));
diff --git a/content/browser/client_hints/client_hints.cc b/content/browser/client_hints/client_hints.cc index 03c0d36b..0c5fe1b 100644 --- a/content/browser/client_hints/client_hints.cc +++ b/content/browser/client_hints/client_hints.cc
@@ -1063,6 +1063,8 @@ enabled_hints.GetEnabledHints(); PersistAcceptCH(origin, delegate, persisted_hints); if (base::FeatureList::IsEnabled(net::features::kPartitionedCookies) && + !base::FeatureList::IsEnabled( + net::features::kPartitionedCookiesBypassOriginTrial) && std::find(persisted_hints.begin(), persisted_hints.end(), WebClientHintsType::kPartitionedCookies) == persisted_hints.end()) {
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc index 0cc5d1f..00ded60 100644 --- a/content/browser/devtools/devtools_instrumentation.cc +++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -1339,7 +1339,8 @@ void OnServiceWorkerMainScriptRequestWillBeSent( const GlobalRenderFrameHostId& requesting_frame_id, - const base::UnguessableToken& token, + const ServiceWorkerContextWrapper* context_wrapper, + int64_t version_id, const network::ResourceRequest& request) { // Currently, `requesting_frame_id` is invalid when payment apps and // extensions register a service worker. See the callers of @@ -1352,17 +1353,25 @@ if (!requesting_frame) return; - FrameTreeNode* ftn = requesting_frame->frame_tree_node(); - DCHECK(ftn); - auto timestamp = base::TimeTicks::Now(); network::mojom::URLRequestDevToolsInfoPtr request_info = network::ExtractDevToolsInfo(request); - DispatchToAgents( - ftn, &protocol::NetworkHandler::RequestSent, token.ToString(), - /*loader_id=*/"", request.headers, *request_info, - protocol::Network::Initiator::TypeEnum::Other, ftn->current_url(), - /*initiator_devtools_request_id=*/"", timestamp); + + ServiceWorkerDevToolsAgentHost* agent_host = + ServiceWorkerDevToolsManager::GetInstance() + ->GetDevToolsAgentHostForNewInstallingWorker(context_wrapper, + version_id); + DCHECK(agent_host); + DCHECK(request.devtools_request_id.has_value()); + for (auto* network_handler : + protocol::NetworkHandler::ForAgentHost(agent_host)) { + network_handler->RequestSent( + request.devtools_request_id.value(), + /*loader_id=*/"", request.headers, *request_info, + protocol::Network::Initiator::TypeEnum::Other, + requesting_frame->GetLastCommittedURL(), + /*initiator_devtools_request_id=*/"", timestamp); + } } void OnWorkerMainScriptLoadingFailed(
diff --git a/content/browser/devtools/devtools_instrumentation.h b/content/browser/devtools/devtools_instrumentation.h index bc709e1..e9ba449 100644 --- a/content/browser/devtools/devtools_instrumentation.h +++ b/content/browser/devtools/devtools_instrumentation.h
@@ -294,7 +294,8 @@ const std::string& error); void OnServiceWorkerMainScriptRequestWillBeSent( const GlobalRenderFrameHostId& requesting_frame_id, - const base::UnguessableToken& token, + const ServiceWorkerContextWrapper* context_wrapper, + int64_t version_id, const network::ResourceRequest& request); // Fires `Network.onLoadingFailed` event for a dedicated worker main script.
diff --git a/content/browser/devtools/protocol/devtools_download_manager_delegate.cc b/content/browser/devtools/protocol/devtools_download_manager_delegate.cc index b56d4ec..eee1fc8 100644 --- a/content/browser/devtools/protocol/devtools_download_manager_delegate.cc +++ b/content/browser/devtools/protocol/devtools_download_manager_delegate.cc
@@ -79,7 +79,7 @@ empty_path, download::DownloadItem::TARGET_DISPOSITION_OVERWRITE, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, download::DownloadItem::MixedContentStatus::UNKNOWN, empty_path, - absl::nullopt /*download_schedule*/, + empty_path, absl::nullopt /*download_schedule*/, download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); return true; } @@ -161,7 +161,7 @@ download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT, download::DownloadItem::MixedContentStatus::UNKNOWN, suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")), - absl::nullopt /*download_schedule*/, + suggested_path.BaseName(), absl::nullopt /*download_schedule*/, download::DOWNLOAD_INTERRUPT_REASON_NONE); }
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc index f74eab50..147bb55f 100644 --- a/content/browser/download/download_manager_impl.cc +++ b/content/browser/download/download_manager_impl.cc
@@ -499,7 +499,7 @@ target_path, download::DownloadItem::TARGET_DISPOSITION_OVERWRITE, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, download::DownloadItem::MixedContentStatus::UNKNOWN, target_path, - absl::nullopt /*download_schedule*/, + base::FilePath(), absl::nullopt /*download_schedule*/, download::DOWNLOAD_INTERRUPT_REASON_NONE); } }
diff --git a/content/browser/download/download_manager_impl_unittest.cc b/content/browser/download/download_manager_impl_unittest.cc index b433e8f..1c137b4 100644 --- a/content/browser/download/download_manager_impl_unittest.cc +++ b/content/browser/download/download_manager_impl_unittest.cc
@@ -539,6 +539,7 @@ download::DownloadDangerType danger_type, download::DownloadItem::MixedContentStatus mixed_content_status, const base::FilePath& intermediate_path, + const base::FilePath& display_name, absl::optional<download::DownloadSchedule> download_schedule, download::DownloadInterruptReason interrupt_reason) { callback_called_ = true;
diff --git a/content/browser/media/cdm_registry_impl.cc b/content/browser/media/cdm_registry_impl.cc index 147f19c0..186caf12 100644 --- a/content/browser/media/cdm_registry_impl.cc +++ b/content/browser/media/cdm_registry_impl.cc
@@ -218,7 +218,8 @@ } void CdmRegistryImpl::RegisterCdm(const CdmInfo& info) { - DVLOG(1) << __func__; + DVLOG(1) << __func__ << "key_system=" << info.key_system + << ", robustness=" << static_cast<int>(info.robustness); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Always register new CDMs at the end of the list, so that the behavior is
diff --git a/content/browser/media/key_system_support_impl.cc b/content/browser/media/key_system_support_impl.cc index 6cb7b9a..6b8824c 100644 --- a/content/browser/media/key_system_support_impl.cc +++ b/content/browser/media/key_system_support_impl.cc
@@ -9,6 +9,24 @@ namespace content { +namespace { + +// All key systems must have either software or hardware secure capability +// supported. +bool IsValidKeySystemCapabilities(KeySystemCapabilities capabilities) { + for (const auto& entry : capabilities) { + auto& capability = entry.second; + if (!capability.sw_secure_capability.has_value() && + !capability.hw_secure_capability.has_value()) { + return false; + } + } + + return true; +} + +} // namespace + // static KeySystemSupportImpl* KeySystemSupportImpl::GetInstance() { static base::NoDestructor<KeySystemSupportImpl> impl; @@ -21,14 +39,49 @@ KeySystemSupportImpl::GetInstance()->Bind(std::move(receiver)); } -KeySystemSupportImpl::KeySystemSupportImpl( +KeySystemSupportImpl::KeySystemSupportImpl() = default; +KeySystemSupportImpl::~KeySystemSupportImpl() = default; + +void KeySystemSupportImpl::SetGetKeySystemCapabilitiesUpdateCbForTesting( GetKeySystemCapabilitiesUpdateCB get_support_cb_for_testing) { + get_support_cb_for_testing_ = std::move(get_support_cb_for_testing); +} + +void KeySystemSupportImpl::Bind( + mojo::PendingReceiver<media::mojom::KeySystemSupport> receiver) { + key_system_support_receivers_.Add(this, std::move(receiver)); +} + +void KeySystemSupportImpl::AddObserver( + mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer) { + DVLOG(3) << __func__; + + auto id = observer_remotes_.Add(std::move(observer)); + + // If `key_system_support_` is already available, notify the new observer + // immediately. All observers will be notified if there are updates later. + if (key_system_capabilities_.has_value()) { + auto* observer = observer_remotes_.Get(id); + observer->OnKeySystemSupportUpdated(CloneKeySystemCapabilities()); + return; + } + + // Observe key system capabilities if not have done so. + if (!is_observing_) + ObserveKeySystemCapabilities(); +} + +void KeySystemSupportImpl::ObserveKeySystemCapabilities() { + DCHECK(!is_observing_); + + is_observing_ = true; + auto result_cb = base::BindRepeating(&KeySystemSupportImpl::OnKeySystemCapabilitiesUpdated, weak_ptr_factory_.GetWeakPtr()); - if (get_support_cb_for_testing) { - get_support_cb_for_testing.Run(std::move(result_cb)); + if (get_support_cb_for_testing_) { + get_support_cb_for_testing_.Run(std::move(result_cb)); return; } @@ -36,65 +89,29 @@ std::move(result_cb)); } -KeySystemSupportImpl::~KeySystemSupportImpl() = default; - -void KeySystemSupportImpl::Bind( - mojo::PendingReceiver<media::mojom::KeySystemSupport> receiver) { - key_system_support_receivers_.Add(this, std::move(receiver)); -} - -void KeySystemSupportImpl::IsKeySystemSupported( - const std::string& key_system, - IsKeySystemSupportedCallback callback) { - DVLOG(3) << __func__ << ": key_system=" << key_system; - - if (!key_system_capabilities_.has_value()) { - pending_callbacks_.emplace_back(key_system, std::move(callback)); - return; - } - - DCHECK(pending_callbacks_.empty()); - - NotifyIsKeySystemSupportedCallback(key_system, std::move(callback)); -} - void KeySystemSupportImpl::OnKeySystemCapabilitiesUpdated( KeySystemCapabilities key_system_capabilities) { DVLOG(3) << __func__; + DCHECK(IsValidKeySystemCapabilities(key_system_capabilities)); + DCHECK(!key_system_capabilities_.has_value() || + key_system_capabilities_.value() != key_system_capabilities) + << "Should not be updated with the same key system capabilities"; + key_system_capabilities_ = std::move(key_system_capabilities); - PendingCallbacks callbacks; - pending_callbacks_.swap(callbacks); - - for (auto& entry : callbacks) { - auto& key_system = entry.first; - auto& callback = entry.second; - NotifyIsKeySystemSupportedCallback(key_system, std::move(callback)); - } + for (auto& observer : observer_remotes_) + observer->OnKeySystemSupportUpdated(CloneKeySystemCapabilities()); } -void KeySystemSupportImpl::NotifyIsKeySystemSupportedCallback( - const std::string& key_system, - IsKeySystemSupportedCallback callback) { - DVLOG(3) << __func__ << ": key_system=" << key_system; +KeySystemCapabilityPtrMap KeySystemSupportImpl::CloneKeySystemCapabilities() { DCHECK(key_system_capabilities_.has_value()); - if (!key_system_capabilities_->count(key_system)) { - std::move(callback).Run(false, nullptr); - return; + base::flat_map<std::string, media::mojom::KeySystemCapabilityPtr> result; + for (const auto& [key_system, capability] : + key_system_capabilities_.value()) { + result[key_system] = capability.Clone(); } - - auto key_system_capabilities = - key_system_capabilities_.value()[key_system].Clone(); - DCHECK(key_system_capabilities); - - if (!key_system_capabilities->sw_secure_capability.has_value() && - !key_system_capabilities->hw_secure_capability.has_value()) { - std::move(callback).Run(false, nullptr); - return; - } - - std::move(callback).Run(true, std::move(key_system_capabilities)); + return result; } } // namespace content
diff --git a/content/browser/media/key_system_support_impl.h b/content/browser/media/key_system_support_impl.h index 8def233..1fc1965 100644 --- a/content/browser/media/key_system_support_impl.h +++ b/content/browser/media/key_system_support_impl.h
@@ -17,10 +17,16 @@ #include "media/mojo/mojom/key_system_support.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote_set.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace content { +// TODO(xhwang): Use StructTraits to convert to KeySystemCapabilities to avoid +// this type. +using KeySystemCapabilityPtrMap = + base::flat_map<std::string, media::mojom::KeySystemCapabilityPtr>; + // A singleton class living in the browser process handling all KeySystemSupport // requests. class CONTENT_EXPORT KeySystemSupportImpl final @@ -34,43 +40,43 @@ KeySystemSupportImpl(const KeySystemSupportImpl&) = delete; KeySystemSupportImpl& operator=(const KeySystemSupportImpl&) = delete; + // `get_support_cb_for_testing` is used to get support update for testing. + // If null, we'll use `CdmRegistryImpl` to get the update. + using GetKeySystemCapabilitiesUpdateCB = + base::RepeatingCallback<void(KeySystemCapabilitiesUpdateCB)>; + void SetGetKeySystemCapabilitiesUpdateCbForTesting( + GetKeySystemCapabilitiesUpdateCB get_support_cb_for_testing); + // Binds the `receiver` to `this`. void Bind(mojo::PendingReceiver<media::mojom::KeySystemSupport> receiver); // media::mojom::KeySystemSupport implementation. - void IsKeySystemSupported(const std::string& key_system, - IsKeySystemSupportedCallback callback) final; + void AddObserver(mojo::PendingRemote<media::mojom::KeySystemSupportObserver> + observer) final; private: friend class base::NoDestructor<KeySystemSupportImpl>; friend class KeySystemSupportImplTest; - using GetKeySystemCapabilitiesUpdateCB = - base::RepeatingCallback<void(KeySystemCapabilitiesUpdateCB)>; - - // `get_support_cb_for_testing` is used to get support update for testing. - // If null, we'll use `CdmRegistryImpl` to get the update. - explicit KeySystemSupportImpl( - GetKeySystemCapabilitiesUpdateCB get_support_cb_for_testing = - base::NullCallback()); + KeySystemSupportImpl(); ~KeySystemSupportImpl() final; + void ObserveKeySystemCapabilities(); + void OnKeySystemCapabilitiesUpdated( KeySystemCapabilities key_system_capabilities); - void NotifyIsKeySystemSupportedCallback( - const std::string& key_system, - IsKeySystemSupportedCallback callback); + // `KeySystemSupport` uses `media::mojom::KeySystemCapability` while the mojom + // interface uses `media::mojom::KeySystemCapabilityPtr`. This function does + // the conversion. + KeySystemCapabilityPtrMap CloneKeySystemCapabilities(); + + GetKeySystemCapabilitiesUpdateCB get_support_cb_for_testing_; + bool is_observing_ = false; + mojo::ReceiverSet<KeySystemSupport> key_system_support_receivers_; + mojo::RemoteSet<media::mojom::KeySystemSupportObserver> observer_remotes_; absl::optional<KeySystemCapabilities> key_system_capabilities_; - mojo::ReceiverSet<media::mojom::KeySystemSupport> - key_system_support_receivers_; - - // Key system to IsKeySystemSupportedCallback map. - using PendingCallbacks = - std::vector<std::pair<std::string, IsKeySystemSupportedCallback>>; - PendingCallbacks pending_callbacks_; - base::WeakPtrFactory<KeySystemSupportImpl> weak_ptr_factory_{this}; };
diff --git a/content/browser/media/key_system_support_impl_unittest.cc b/content/browser/media/key_system_support_impl_unittest.cc index 65bbe10..980e408 100644 --- a/content/browser/media/key_system_support_impl_unittest.cc +++ b/content/browser/media/key_system_support_impl_unittest.cc
@@ -6,6 +6,7 @@ #include <string> +#include "base/callback_helpers.h" #include "base/containers/contains.h" #include "base/logging.h" #include "base/test/gmock_callback_support.h" @@ -15,6 +16,7 @@ #include "media/base/video_codecs.h" #include "media/cdm/cdm_capability.h" #include "media/cdm/cdm_type.h" +#include "mojo/public/cpp/bindings/equals_traits.h" #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "testing/gtest/include/gtest/gtest.h" @@ -30,6 +32,13 @@ using media::CdmCapability; using media::mojom::KeySystemCapability; using testing::_; +using testing::SaveArg; + +const char kTestKeySystem[] = "com.example.somesystem"; + +// Ids to keep track of observers. +const int kObserver1 = 1; +const int kObserver2 = 2; ACTION_TEMPLATE(PostOnceCallback, HAS_1_TEMPLATE_PARAMS(int, k), @@ -38,114 +47,194 @@ FROM_HERE, base::BindOnce(std::move(std::get<k>(args)), p0)); } +namespace { + +using KeySystemSupportCB = + base::RepeatingCallback<void(KeySystemCapabilityPtrMap)>; + +class KeySystemSupportObserverImpl + : public media::mojom::KeySystemSupportObserver { + public: + explicit KeySystemSupportObserverImpl(KeySystemSupportCB cb) + : key_system_support_cb_(std::move(cb)) {} + KeySystemSupportObserverImpl(const KeySystemSupportObserverImpl&) = delete; + KeySystemSupportObserverImpl& operator=(const KeySystemSupportObserverImpl&) = + delete; + ~KeySystemSupportObserverImpl() override = default; + + // media::mojom::KeySystemSupportObserver + void OnKeySystemSupportUpdated(KeySystemCapabilityPtrMap capabilities) final { + key_system_support_cb_.Run(std::move(capabilities)); + } + + private: + KeySystemSupportCB key_system_support_cb_; +}; + +CdmCapability TestCdmCapability() { + return CdmCapability( + {AudioCodec::kVorbis}, {{VideoCodec::kVP8, {}}, {VideoCodec::kVP9, {}}}, + {EncryptionScheme::kCenc, EncryptionScheme::kCbcs}, + {CdmSessionType::kTemporary, CdmSessionType::kPersistentLicense}); +} + +KeySystemCapabilities TestKeySystemCapabilities( + absl::optional<CdmCapability> sw_secure_capability, + absl::optional<CdmCapability> hw_secure_capability) { + KeySystemCapabilities key_system_capabilities; + key_system_capabilities[kTestKeySystem] = KeySystemCapability( + std::move(sw_secure_capability), std::move(hw_secure_capability)); + return key_system_capabilities; +} + +} // namespace + class KeySystemSupportImplTest : public testing::Test { - protected: - CdmCapability TestCdmCapability() { - return CdmCapability( - {AudioCodec::kVorbis}, {{VideoCodec::kVP8, {}}, {VideoCodec::kVP9, {}}}, - {EncryptionScheme::kCenc, EncryptionScheme::kCbcs}, - {CdmSessionType::kTemporary, CdmSessionType::kPersistentLicense}); + public: + KeySystemSupportImplTest() { + LOG(ERROR) << __func__; + key_system_support_impl_.SetGetKeySystemCapabilitiesUpdateCbForTesting( + get_support_cb_.Get()); + key_system_support_impl_.Bind( + key_system_support_.BindNewPipeAndPassReceiver()); } - KeySystemCapabilities TestKeySystemCapabilities( - absl::optional<CdmCapability> sw_secure_capability, - absl::optional<CdmCapability> hw_secure_capability) { - KeySystemCapabilities key_system_capabilities; - key_system_capabilities["KeySystem"] = KeySystemCapability( - std::move(sw_secure_capability), std::move(hw_secure_capability)); - return key_system_capabilities; - } - - void OnIsKeySystemSupported(base::OnceClosure done_cb, - bool is_supported, - media::mojom::KeySystemCapabilityPtr capability) { - is_supported_ = is_supported; - capability_ = std::move(capability); + void OnKeySystemSupportUpdated(int observer_id, + base::OnceClosure done_cb, + KeySystemCapabilityPtrMap capabilities) { + results_[observer_id].push_back(std::move(capabilities)); std::move(done_cb).Run(); } - // Determines if |key_system| is registered. If it is, updates |codecs_| - // and |persistent_|. - bool IsSupported(const std::string& key_system) { + protected: + void GetKeySystemSupport() { DVLOG(1) << __func__; - mojo::Remote<media::mojom::KeySystemSupport> key_system_support; - KeySystemSupportImpl key_system_support_impl(get_support_cb_.Get()); - key_system_support_impl.Bind( - key_system_support.BindNewPipeAndPassReceiver()); - base::RunLoop run_loop; - key_system_support->IsKeySystemSupported( - key_system, - base::BindOnce(&KeySystemSupportImplTest::OnIsKeySystemSupported, - base::Unretained(this), run_loop.QuitClosure())); + mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_remote; + mojo::MakeSelfOwnedReceiver( + std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating( + &KeySystemSupportImplTest::OnKeySystemSupportUpdated, + base::Unretained(this), kObserver1, run_loop.QuitClosure())), + observer_remote.InitWithNewPipeAndPassReceiver()); + key_system_support_->AddObserver(std::move(observer_remote)); run_loop.Run(); - - return is_supported_; - } - - // Same as `IsSupported()`, but calling into KeySystemSupportImpl directly - // instead of using the mojo interface. This is to avoid the complication of - // posted callbacks in async tests. - bool IsSupportedWithoutMojo(const std::string& key_system) { - DVLOG(1) << __func__; - - KeySystemSupportImpl key_system_support_impl(get_support_cb_.Get()); - - base::RunLoop run_loop; - key_system_support_impl.IsKeySystemSupported( - key_system, - base::BindOnce(&KeySystemSupportImplTest::OnIsKeySystemSupported, - base::Unretained(this), run_loop.QuitClosure())); - run_loop.Run(); - - return is_supported_; } BrowserTaskEnvironment task_environment_; - + KeySystemSupportImpl key_system_support_impl_; + mojo::Remote<media::mojom::KeySystemSupport> key_system_support_; base::MockCallback<KeySystemSupportImpl::GetKeySystemCapabilitiesUpdateCB> get_support_cb_; - // Updated by IsSupported(). - bool is_supported_ = false; - media::mojom::KeySystemCapabilityPtr capability_; + // KeySystemSupport update results. It's a map from the "observer ID" to the + // list of updates received by that observer. + std::map<int, std::vector<KeySystemCapabilityPtrMap>> results_; }; TEST_F(KeySystemSupportImplTest, NoKeySystems) { EXPECT_CALL(get_support_cb_, Run(_)) .WillOnce(RunOnceCallback<0>(KeySystemCapabilities())); - EXPECT_FALSE(IsSupported("KeySystem")); - EXPECT_FALSE(capability_); + GetKeySystemSupport(); + + EXPECT_EQ(results_.size(), 1u); // One observer + EXPECT_TRUE(results_.count(kObserver1)); // Observer 1 + EXPECT_EQ(results_[kObserver1].size(), 1u); // One update for observer 1 + + const auto& capabilities = results_[kObserver1][0]; + EXPECT_TRUE(capabilities.empty()); // No capabilities } -TEST_F(KeySystemSupportImplTest, NoCapabilities) { - EXPECT_CALL(get_support_cb_, Run(_)) - .WillOnce(RunOnceCallback<0>( - TestKeySystemCapabilities(absl::nullopt, absl::nullopt))); - EXPECT_FALSE(IsSupported("KeySystem")); - EXPECT_FALSE(capability_); -} - -TEST_F(KeySystemSupportImplTest, SoftwareSecureCapability_Sync) { +TEST_F(KeySystemSupportImplTest, OneObserver) { EXPECT_CALL(get_support_cb_, Run(_)) .WillOnce(RunOnceCallback<0>( TestKeySystemCapabilities(TestCdmCapability(), absl::nullopt))); - ASSERT_TRUE(IsSupported("KeySystem")); - EXPECT_TRUE(capability_->sw_secure_capability); - EXPECT_FALSE(capability_->hw_secure_capability); + GetKeySystemSupport(); + + EXPECT_EQ(results_.size(), 1u); // One observer + EXPECT_TRUE(results_.count(kObserver1)); // Observer 1 + EXPECT_EQ(results_[kObserver1].size(), 1u); // One update for observer 1 + + auto& capabilities = results_[kObserver1][0]; + ASSERT_TRUE(capabilities.count(kTestKeySystem)); + const auto& capability = capabilities[kTestKeySystem]; + EXPECT_TRUE(capability->sw_secure_capability); + EXPECT_FALSE(capability->hw_secure_capability); } -// Same as above, but post the callback instead of running it directly, and uses -// `IsSupportedWithoutMojo`, to simulate the case where `CdmRegistryImpl` -// resolves the callback asynchronously. -TEST_F(KeySystemSupportImplTest, SoftwareSecureCapability_Async) { +TEST_F(KeySystemSupportImplTest, TwoObservers) { EXPECT_CALL(get_support_cb_, Run(_)) - .WillOnce(PostOnceCallback<0>( + .WillOnce(RunOnceCallback<0>( TestKeySystemCapabilities(TestCdmCapability(), absl::nullopt))); - ASSERT_TRUE(IsSupportedWithoutMojo("KeySystem")); - EXPECT_TRUE(capability_->sw_secure_capability); - EXPECT_FALSE(capability_->hw_secure_capability); + + base::RunLoop run_loop; + mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_1_remote; + mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_2_remote; + mojo::MakeSelfOwnedReceiver( + std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating( + &KeySystemSupportImplTest::OnKeySystemSupportUpdated, + base::Unretained(this), kObserver1, base::DoNothing())), + observer_1_remote.InitWithNewPipeAndPassReceiver()); + mojo::MakeSelfOwnedReceiver( + std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating( + &KeySystemSupportImplTest::OnKeySystemSupportUpdated, + base::Unretained(this), kObserver2, run_loop.QuitClosure())), + observer_2_remote.InitWithNewPipeAndPassReceiver()); + key_system_support_->AddObserver(std::move(observer_1_remote)); + key_system_support_->AddObserver(std::move(observer_2_remote)); + run_loop.Run(); + + EXPECT_EQ(results_.size(), 2u); // Two observers + + EXPECT_TRUE(results_.count(kObserver1)); // Observer 1 + EXPECT_EQ(results_[kObserver1].size(), 1u); // One update for observer 1 + auto& capabilities = results_[kObserver1][0]; + ASSERT_TRUE(capabilities.count(kTestKeySystem)); + const auto& capability = capabilities[kTestKeySystem]; + EXPECT_TRUE(capability->sw_secure_capability); + EXPECT_FALSE(capability->hw_secure_capability); + + EXPECT_TRUE(results_.count(kObserver2)); // Observer 2 + EXPECT_EQ(results_[kObserver2].size(), 1u); // One update for observer 1 + EXPECT_TRUE(mojo::Equals(results_[kObserver1][0], results_[kObserver2][0])); +} + +TEST_F(KeySystemSupportImplTest, TwoUpdates) { + KeySystemCapabilitiesUpdateCB callback; + EXPECT_CALL(get_support_cb_, Run(_)).WillOnce(SaveArg<0>(&callback)); + + base::RunLoop run_loop_1; + mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_remote; + mojo::MakeSelfOwnedReceiver( + std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating( + &KeySystemSupportImplTest::OnKeySystemSupportUpdated, + base::Unretained(this), kObserver1, base::DoNothing())), + observer_remote.InitWithNewPipeAndPassReceiver()); + key_system_support_->AddObserver(std::move(observer_remote)); + run_loop_1.RunUntilIdle(); + + // Update twice, one with hardware capability, one without. + base::RunLoop run_loop_2; + callback.Run(TestKeySystemCapabilities(TestCdmCapability(), absl::nullopt)); + callback.Run( + TestKeySystemCapabilities(TestCdmCapability(), TestCdmCapability())); + run_loop_2.RunUntilIdle(); + + EXPECT_EQ(results_.size(), 1u); // One observer + EXPECT_TRUE(results_.count(kObserver1)); // Observer 1 + EXPECT_EQ(results_[kObserver1].size(), 2u); // Two updates for observer 1 + + auto& capabilities_1 = results_[kObserver1][0]; + ASSERT_TRUE(capabilities_1.count(kTestKeySystem)); + const auto& capability_1 = capabilities_1[kTestKeySystem]; + EXPECT_TRUE(capability_1->sw_secure_capability); + EXPECT_FALSE(capability_1->hw_secure_capability); + + auto& capabilities_2 = results_[kObserver1][1]; + ASSERT_TRUE(capabilities_2.count(kTestKeySystem)); + const auto& capability_2 = capabilities_2[kTestKeySystem]; + EXPECT_TRUE(capability_2->sw_secure_capability); + EXPECT_TRUE(capability_2->hw_secure_capability); } } // namespace content
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc index 94be77f4..9b74786 100644 --- a/content/browser/network_service_instance_impl.cc +++ b/content/browser/network_service_instance_impl.cc
@@ -177,6 +177,7 @@ base::Thread& GetNetworkServiceDedicatedThread() { static base::NoDestructor<base::Thread> thread{"NetworkService"}; + DCHECK(base::FeatureList::IsEnabled(kNetworkServiceDedicatedThread)); return *thread; } @@ -546,11 +547,7 @@ void CreateInProcessNetworkService( mojo::PendingReceiver<network::mojom::NetworkService> receiver) { scoped_refptr<base::SingleThreadTaskRunner> task_runner; - // If it's specified to run a separate thread for the in-process network - // service, or if the IO thread isn't initialized because we're in Android's - // minimal browser mode, then use a dedicated thread. - if (base::FeatureList::IsEnabled(kNetworkServiceDedicatedThread) || - !BrowserThread::IsThreadInitialized(BrowserThread::IO)) { + if (base::FeatureList::IsEnabled(kNetworkServiceDedicatedThread)) { base::Thread::Options options(base::MessagePumpType::IO, 0); GetNetworkServiceDedicatedThread().StartWithOptions(std::move(options)); task_runner = GetNetworkServiceDedicatedThread().task_runner();
diff --git a/content/browser/renderer_host/frame_tree_node.h b/content/browser/renderer_host/frame_tree_node.h index 1ba97bd..b102dd0 100644 --- a/content/browser/renderer_host/frame_tree_node.h +++ b/content/browser/renderer_host/frame_tree_node.h
@@ -332,7 +332,7 @@ // A RenderFrameHost in this node started loading. // |should_show_loading_ui| indicates whether this navigation should be // visible in the UI. True for cross-document navigations and navigations - // intercepted by appHistory's transitionWhile(). + // intercepted by the navigation API's transitionWhile(). // |was_previously_loading| is false if the FrameTree was not loading before. // The caller is required to provide this boolean as the delegate should only // be notified if the FrameTree went from non-loading to loading state.
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc index 464d92cb..a9d83b4 100644 --- a/content/browser/renderer_host/navigation_controller_impl.cc +++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -3738,7 +3738,7 @@ absl::nullopt /* ad_auction_components */, // This timestamp will be populated when the commit IPC is sent. base::TimeTicks() /* commit_sent */, false /* anonymous */, - std::string() /* srcdoc_value */); + std::string() /* srcdoc_value */, false /* should_load_data_url */); #if BUILDFLAG(IS_ANDROID) if (ValidateDataURLAsString(params.data_url_as_string)) { commit_params->data_url_as_string = params.data_url_as_string->data();
diff --git a/content/browser/renderer_host/navigation_entry_impl.cc b/content/browser/renderer_host/navigation_entry_impl.cc index 2987e91..35319ea 100644 --- a/content/browser/renderer_host/navigation_entry_impl.cc +++ b/content/browser/renderer_host/navigation_entry_impl.cc
@@ -929,7 +929,7 @@ absl::nullopt /* ad_auction_components */, // This timestamp will be populated when the commit IPC is sent. base::TimeTicks() /* commit_sent */, false /* anonymous */, - std::string() /* srcdoc_value */); + std::string() /* srcdoc_value */, false /* should_load_data_url */); #if BUILDFLAG(IS_ANDROID) // `data_url_as_string` is saved in NavigationEntry but should only be used by // main frames, because loadData* navigations can only happen on the main
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index 45af0f5..15a834a 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -961,7 +961,9 @@ PersistAcceptCH(url::Origin::Create(url), delegate, client_hints); } - if (base::FeatureList::IsEnabled(net::features::kPartitionedCookies)) { + if (base::FeatureList::IsEnabled(net::features::kPartitionedCookies) && + !base::FeatureList::IsEnabled( + net::features::kPartitionedCookiesBypassOriginTrial)) { if (auto* cookie_manager = frame_tree_node->current_frame_host() ->GetStoragePartition() ->GetCookieManagerForBrowserProcess()) { @@ -1171,7 +1173,7 @@ std::vector<GURL>(), absl::nullopt /* ad_auction_components */, // This timestamp will be populated when the commit IPC is sent. base::TimeTicks() /* commit_sent */, false /* anonymous */, - std::string() /* srcdoc_value */); + std::string() /* srcdoc_value */, false /* should_load_data_url */); // CreateRendererInitiated() should only be triggered when the navigation is // initiated by a frame in the same process. @@ -1295,7 +1297,7 @@ absl::nullopt /* ad_auction_components */, // This timestamp will be populated when the commit IPC is sent. base::TimeTicks() /* commit_sent */, false /* anonymous */, - std::string() /* srcdoc_value */); + std::string() /* srcdoc_value */, false /* should_load_data_url */); blink::mojom::BeginNavigationParamsPtr begin_params = blink::mojom::BeginNavigationParams::New(); std::unique_ptr<NavigationRequest> navigation_request(new NavigationRequest( @@ -5990,6 +5992,8 @@ SetExpectedProcess(render_frame_host_->GetProcess()); + commit_params_->is_load_data_with_base_url = IsLoadDataWithBaseURL(); + if (!IsSameDocument()) { #if DCHECK_IS_ON() DCHECK(is_safe_to_delete_); @@ -6027,11 +6031,9 @@ } bool NavigationRequest::IsLoadDataWithBaseURL() const { - // A navigation is a loadDataWithBaseURL navigation if it's a successful main - // frame navigation, and its base URL is valid. This function should be kept - // in sync with the ShouldLoadDataWithBaseURL() function in - // render_frame_impl.cc. - return IsInMainFrame() && !DidEncounterError() && + // A navigation is a loadDataWithBaseURL navigation if it's a successful + // primary main frame navigation to a data: URL, and its base URL is valid. + return IsInPrimaryMainFrame() && !DidEncounterError() && common_params_->url.SchemeIs(url::kDataScheme) && common_params_->base_url_for_data_url.is_valid(); }
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index b35619c..bf7c547c 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -10867,13 +10867,14 @@ // In general, loading ui is only shown for cross-document navigations, // because same-document navigations are already complete by the time the // renderer notifies the browser process of the navigation. - // AppHistory transitionWhile(), however, is an asynchronous same-document - // navigation, and should therefore show loading UI until load completion. + // The navigation API's transitionWhile(), however, is an asynchronous + // same-document navigation, and should therefore show loading UI until load + // completion. bool should_show_loading_ui = !is_same_document_navigation || same_document_params->same_document_navigation_type == blink::mojom::SameDocumentNavigationType:: - kAppHistoryTransitionWhile; + kNavigationApiTransitionWhile; bool was_loading = frame_tree()->LoadingTree()->IsLoading(); is_loading_ = true;
diff --git a/content/browser/renderer_host/render_frame_host_impl_unittest.cc b/content/browser/renderer_host/render_frame_host_impl_unittest.cc index b51de60..e52a0f7 100644 --- a/content/browser/renderer_host/render_frame_host_impl_unittest.cc +++ b/content/browser/renderer_host/render_frame_host_impl_unittest.cc
@@ -510,7 +510,7 @@ ASSERT_FALSE(contents()->IsLoading()); ASSERT_FALSE(contents()->ShouldShowLoadingUI()); - // Emulate appHistory.transitionWhile(). + // Emulate navigateEvent.transitionWhile(). const GURL url2("http://foo#a"); auto params = mojom::DidCommitProvisionalLoadParams::New(); params->did_create_new_entry = false; @@ -525,10 +525,10 @@ params->post_id = -1; main_test_rfh()->SendDidCommitSameDocumentNavigation( std::move(params), - blink::mojom::SameDocumentNavigationType::kAppHistoryTransitionWhile); + blink::mojom::SameDocumentNavigationType::kNavigationApiTransitionWhile); - // appHistory.transitionWhile() should leave WebContents in the loading state - // and showing loading UI, unlike other same-document navigations. + // navigateEvent.transitionWhile() should leave WebContents in the loading + // state and showing loading UI, unlike other same-document navigations. EXPECT_TRUE(delegate->should_show_loading_ui()); EXPECT_TRUE(contents()->IsLoading()); EXPECT_TRUE(contents()->ShouldShowLoadingUI());
diff --git a/content/browser/resources/service_worker/serviceworker_internals.css b/content/browser/resources/service_worker/serviceworker_internals.css index 373f576..20a8e804 100644 --- a/content/browser/resources/service_worker/serviceworker_internals.css +++ b/content/browser/resources/service_worker/serviceworker_internals.css
@@ -3,44 +3,54 @@ * found in the LICENSE file. */ .serviceworker-summary { - background-color: rgb(235, 239, 249); - border-top: 1px solid rgb(156, 194, 239); - margin-bottom: 6px; - margin-top: 12px; - padding: 3px; - font-weight: bold; + background-color: rgb(235, 239, 249); + border-top: 1px solid rgb(156, 194, 239); + font-weight: bold; + margin-bottom: 6px; + margin-top: 12px; + padding: 3px; } .serviceworker-item { - margin-bottom: 15px; - margin-top: 6px; - position: relative; + margin-bottom: 15px; + margin-top: 6px; + position: relative; } .serviceworker-registration { - padding: 5px; + padding: 5px; } .serviceworker-scope { - color: rgb(85, 102, 221); - display: inline-block; - padding-bottom: 1px; - padding-top: 4px; - text-decoration: none; - white-space: nowrap; + color: rgb(85, 102, 221); + display: inline-block; + padding-bottom: 1px; + padding-top: 4px; + text-decoration: none; + white-space: nowrap; } .serviceworker-version { - padding-bottom: 3px; - padding-left: 10px; + padding-bottom: 3px; + padding-inline-start: 10px; } .serviceworker-client { - padding-bottom: 3px; - padding-left: 10px; + padding-bottom: 3px; + padding-inline-start: 10px; } .controls a { - margin-block-end: 16px; - color: #777; + color: #777; + margin-block-end: 16px; +} + +.serviceworker-storage-key { + padding-bottom: 3px; + padding-inline-start: 10px; +} + +.serviceworker-storage-key-wrapper { + color: rgb(85, 102, 221); + white-space: nowrap; }
diff --git a/content/browser/resources/service_worker/serviceworker_internals.html b/content/browser/resources/service_worker/serviceworker_internals.html index f486ea9..5829829 100644 --- a/content/browser/resources/service_worker/serviceworker_internals.html +++ b/content/browser/resources/service_worker/serviceworker_internals.html
@@ -69,6 +69,33 @@ <span>Scope:</span> <span jscontent="scope"></span> </div> + <!-- Storage Partitioning --> + <div class="serviceworker-storage-key-wrapper" + jsdisplay="$this.third_party_storage_partitioning_enabled"> + Storage key: + <div class="serviceworker-storage-key"> + <div class="serviceworker-origin" + jsdisplay="$this.ancestor_chain_bit == 'CrossSite'"> + <span>origin:</span> + <span jscontent="$this.origin"></span> + </div> + <div class="serviceworker-top-level-site" + jsdisplay="$this.ancestor_chain_bit == 'CrossSite'"> + <span>Top level site:</span> + <span jscontent="$this.top_level_site"></span> + </div> + <div class="serviceworker-ancestor-chain-bit"> + <span>Ancestor chain bit:</span> + <span jscontent="$this.ancestor_chain_bit"></span> + </div> + <div class="serviceworker-nonce" + jsdisplay="$this.ancestor_chain_bit == 'CrossSite'"> + <span>nonce:</span> + <span jscontent="$this.active"></span> + </div> + </div> + </div> + <!-- Storage Partitioning ends --> <div class="serviceworker-rid"> <span>Registration ID:</span> <span jscontent="registration_id"></span> @@ -147,7 +174,8 @@ <span>URL: </span><span jscontent="$this.url"></span> </div> </div> - </div> <!-- templates end --> + </div> + <!-- templates end --> <h1>ServiceWorker</h1> <div class="content"> <div id="serviceworker-options"></div>
diff --git a/content/browser/sandbox_mac_unittest.mm b/content/browser/sandbox_mac_unittest.mm index a69810d9..05e9b29 100644 --- a/content/browser/sandbox_mac_unittest.mm +++ b/content/browser/sandbox_mac_unittest.mm
@@ -15,7 +15,9 @@ #include "base/mac/foundation_util.h" #include "base/mac/mac_util.h" #include "base/mac/scoped_cftyperef.h" +#include "base/memory/read_only_shared_memory_region.h" #include "base/memory/ref_counted.h" +#include "base/memory/shared_memory_mapping.h" #include "base/posix/eintr_wrapper.h" #include "base/process/kill.h" #include "base/strings/strcat.h" @@ -204,24 +206,19 @@ size_t font_data_length = font_data.length(); CHECK(font_data_length > 0); - auto font_shmem = mojo::SharedBufferHandle::Create(font_data_length); - CHECK(font_shmem.is_valid()); + auto shmem_region_and_mapping = + base::ReadOnlySharedMemoryRegion::Create(font_data_length); + CHECK(shmem_region_and_mapping.IsValid()); - mojo::ScopedSharedBufferMapping mapping = font_shmem->Map(font_data_length); - CHECK(mapping); - - memcpy(mapping.get(), font_data.c_str(), font_data_length); + memcpy(shmem_region_and_mapping.mapping.memory(), font_data.c_str(), + font_data_length); // Now init the sandbox. CheckCreateSeatbeltServer(); - mojo::ScopedSharedBufferHandle shmem_handle = - font_shmem->Clone(mojo::SharedBufferHandle::AccessMode::READ_ONLY); - CHECK(shmem_handle.is_valid()); - base::ScopedCFTypeRef<CTFontDescriptorRef> data_descriptor; CHECK(FontLoader::CTFontDescriptorFromBuffer( - std::move(shmem_handle), font_data_length, &data_descriptor)); + std::move(shmem_region_and_mapping.region), &data_descriptor)); CHECK(data_descriptor); base::ScopedCFTypeRef<CTFontRef> sized_ctfont( @@ -244,18 +241,18 @@ std::unique_ptr<FontLoader::ResultInternal> result = FontLoader::LoadFontForTesting(u"Geeza Pro", 16); ASSERT_TRUE(result); - ASSERT_TRUE(result->font_data.is_valid()); - uint64_t font_data_size = result->font_data->GetSize(); + ASSERT_TRUE(result->font_data.IsValid()); + uint64_t font_data_size = result->font_data.GetSize(); EXPECT_GT(font_data_size, 0U); EXPECT_GT(result->font_id, 0U); - mojo::ScopedSharedBufferMapping mapping = - result->font_data->Map(font_data_size); - ASSERT_TRUE(mapping); + base::ReadOnlySharedMemoryMapping mapping = result->font_data.Map(); + ASSERT_TRUE(mapping.IsValid()); + ASSERT_EQ(font_data_size, mapping.size()); base::WriteFileDescriptor( fileno(temp_file.get()), - base::StringPiece(static_cast<const char*>(mapping.get()), + base::StringPiece(static_cast<const char*>(mapping.memory()), font_data_size)); extra_data_ = temp_file_path.value();
diff --git a/content/browser/scheduler/responsiveness/README b/content/browser/scheduler/responsiveness/README index c134599..4260fcc 100644 --- a/content/browser/scheduler/responsiveness/README +++ b/content/browser/scheduler/responsiveness/README
@@ -4,7 +4,7 @@ There are four types of work items executed on the UI and IO threads. 1) Both the UI and IO threads can have tasks posted to them via the Task - Scheduler [e.g. via base::PostTask with a BrowserThread::ID]. + Scheduler [e.g. via content::Get(UI|IO)ThreadTaskRunner()]. 2) The UI thread processes native events directly from the message loop [NSEvents on macOS, MSGs on Windows, InputEvents on Android, XEvents on X11, etc.]
diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc index d5615a6..89e6875 100644 --- a/content/browser/service_worker/service_worker_internals_ui.cc +++ b/content/browser/service_worker/service_worker_internals_ui.cc
@@ -162,6 +162,22 @@ for (const auto& registration : registrations) { base::Value registration_info(base::Value::Type::DICTIONARY); registration_info.SetStringKey("scope", registration.scope.spec()); + registration_info.SetBoolKey( + "third_party_storage_partitioning_enabled", + registration.key.IsThirdPartyStoragePartitioningEnabled()); + registration_info.SetStringKey( + "ancestor_chain_bit", registration.key.ancestor_chain_bit() == + blink::mojom::AncestorChainBit::kCrossSite + ? "CrossSite" + : "SameSite"); + registration_info.SetStringKey("nonce", + registration.key.nonce().has_value() + ? registration.key.nonce()->ToString() + : "<null>"); + registration_info.SetStringKey("origin", + registration.key.origin().GetDebugString()); + registration_info.SetStringKey( + "top_level_site", registration.key.top_level_site().Serialize()); registration_info.SetStringKey( "registration_id", base::NumberToString(registration.registration_id)); registration_info.SetBoolKey("navigation_preload_enabled",
diff --git a/content/browser/service_worker/service_worker_internals_ui_browsertest.cc b/content/browser/service_worker/service_worker_internals_ui_browsertest.cc new file mode 100644 index 0000000..74dd04c --- /dev/null +++ b/content/browser/service_worker/service_worker_internals_ui_browsertest.cc
@@ -0,0 +1,358 @@ +// Copyright 2022 The Chromium Authors. 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/bind.h" +#include "base/command_line.h" +#include "base/metrics/statistics_recorder.h" +#include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/test/bind.h" +#include "base/test/scoped_feature_list.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" +#include "content/browser/service_worker/service_worker_registration.h" +#include "content/browser/service_worker/service_worker_test_utils.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/test_utils.h" +#include "content/shell/browser/shell.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "third_party/blink/public/common/service_worker/service_worker_status_code.h" +#include "third_party/blink/public/common/storage_key/storage_key.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h" + +namespace content { +namespace { +const char kServiceWorkerInternalsUrl[] = "chrome://serviceworker-internals"; +const char kServiceWorkerSetupPage[] = "/service_worker/empty.html"; +const char kServiceWorkerUrl[] = "/service_worker/fetch_event.js"; +const char kServiceWorkerScope[] = "/service_worker/"; +const std::u16string kCompleteTitle = u"Complete"; + +void ExpectRegisterResultAndRun(blink::ServiceWorkerStatusCode expected, + base::RepeatingClosure continuation, + blink::ServiceWorkerStatusCode actual) { + ASSERT_EQ(expected, actual); + continuation.Run(); +} + +void ExpectUnregisterResultAndRun(bool expected, + base::RepeatingClosure continuation, + bool actual) { + ASSERT_EQ(expected, actual); + continuation.Run(); +} +} // namespace + +static int CountRenderProcessHosts() { + return RenderProcessHost::GetCurrentRenderProcessCountForTesting(); +} +class ServiceWorkerInternalsUIBrowserTest : public ContentBrowserTest { + public: + ServiceWorkerInternalsUIBrowserTest() = default; + + protected: + void SetUp() override { + ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); + ContentBrowserTest::SetUp(); + } + + void SetUpOnMainThread() override { + StartServer(); + StoragePartition* partition = shell() + ->web_contents() + ->GetBrowserContext() + ->GetDefaultStoragePartition(); + wrapper_ = static_cast<ServiceWorkerContextWrapper*>( + partition->GetServiceWorkerContext()); + } + + void TearDownOnMainThread() override { + // Flush remote storage control so that all pending callbacks are executed. + wrapper() + ->context() + ->registry() + ->GetRemoteStorageControl() + .FlushForTesting(); + content::RunAllTasksUntilIdle(); + wrapper_ = nullptr; + } + + void StartServer() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + embedded_test_server()->StartAcceptingConnections(); + } + + ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); } + ServiceWorkerContext* public_context() { return wrapper(); } + + blink::ServiceWorkerStatusCode FindRegistration() { + const GURL& document_url = + embedded_test_server()->GetURL(kServiceWorkerSetupPage); + blink::ServiceWorkerStatusCode status; + base::RunLoop loop; + wrapper()->FindReadyRegistrationForClientUrl( + document_url, blink::StorageKey(url::Origin::Create(document_url)), + base::BindLambdaForTesting( + [&](blink::ServiceWorkerStatusCode find_status, + scoped_refptr<ServiceWorkerRegistration> registration) { + status = find_status; + if (!registration.get()) + EXPECT_NE(blink::ServiceWorkerStatusCode::kOk, status); + loop.Quit(); + })); + loop.Run(); + return status; + } + + std::vector<ServiceWorkerRegistrationInfo> GetAllRegistrations() { + return wrapper()->GetAllLiveRegistrationInfo(); + } + + // Navigate to the page to set up a renderer page to embed a worker + void NavigateToServiceWorkerSetupPage() { + NavigateToURLBlockUntilNavigationsComplete( + active_shell_, embedded_test_server()->GetURL(kServiceWorkerSetupPage), + 1); + FocusContent(FROM_HERE); + } + + void NavigateToServiceWorkerInternalUI() { + ASSERT_TRUE(NavigateToURL(active_shell_, GURL(kServiceWorkerInternalsUrl))); + // Ensure the window has focus after the navigation. + FocusContent(FROM_HERE); + } + + void FocusContent(const base::Location& from_here) { + RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( + web_contents()->GetRenderWidgetHostView()->GetRenderWidgetHost()); + host->GotFocus(); + host->SetActive(true); + + ASSERT_TRUE(web_contents()->GetRenderWidgetHostView()->HasFocus()) + << "Location: " << from_here.ToString(); + } + + WebContentsImpl* web_contents() const { + return static_cast<WebContentsImpl*>(active_shell_->web_contents()); + } + + // Create a new window and navigate to about::blank. + Shell* CreateNewWindow() { + active_shell_ = CreateBrowser(); + return active_shell_; + } + + // Tear down the page. + void TearDownWindow() { + active_shell_->Close(); + active_shell_ = shell(); + } + + void MoveToWindow(Shell* window) { active_shell_ = window; } + void ReloadWindow() { active_shell_->Reload(); } + + // Registers a service worker and then tears down the process it used, for a + // clean slate going forward. + void RegisterServiceWorker() { + NavigateToServiceWorkerSetupPage(); + { + base::RunLoop run_loop; + blink::mojom::ServiceWorkerRegistrationOptions options( + embedded_test_server()->GetURL(kServiceWorkerScope), + blink::mojom::ScriptType::kClassic, + blink::mojom::ServiceWorkerUpdateViaCache::kImports); + // Set up the storage key for the service worker + blink::StorageKey key(url::Origin::Create(options.scope)); + // Register returns when the promise is resolved. + public_context()->RegisterServiceWorker( + embedded_test_server()->GetURL(kServiceWorkerUrl), key, options, + base::BindOnce(&ExpectRegisterResultAndRun, + blink::ServiceWorkerStatusCode::kOk, + run_loop.QuitClosure())); + run_loop.Run(); + } + } + + void UnRegisterServiceWorker() { + { + base::RunLoop run_loop; + blink::StorageKey key(url::Origin::Create( + embedded_test_server()->GetURL(kServiceWorkerScope))); + // Unregistering something should return true. + public_context()->UnregisterServiceWorker( + embedded_test_server()->GetURL(kServiceWorkerScope), key, + base::BindOnce(&ExpectUnregisterResultAndRun, true, + run_loop.QuitClosure())); + run_loop.Run(); + } + EXPECT_EQ(FindRegistration(), + blink::ServiceWorkerStatusCode::kErrorNotFound) + << "Should not be able to find any Service Worker."; + } + + testing::AssertionResult setMutationObserverSWPopulated() { + static constexpr char kScript[] = R"( + const elementToObserve = document.getElementById("serviceworker-list"); + const options = { childList: true, subtree: true }; + let placeholder = document.createElement("browserTestResult_SWPopulated"); + document.body.appendChild(placeholder); + + const callback = function (mutations, observer) { + mutations.forEach((mutation) => { + if (mutation.type === "childList") { + mutation.addedNodes.forEach((node) => { + if ( + node.classList && + node.classList.contains("serviceworker-registration") + ) { + placeholder.innerText = "done"; + document.title = $1; + observer.disconnect(); + } + }); + } + }); + }; + + const observer = new MutationObserver(callback); + observer.observe(elementToObserve, options); + )"; + return ExecJs(web_contents()->GetMainFrame(), + JsReplace(kScript, kCompleteTitle), + EXECUTE_SCRIPT_DEFAULT_OPTIONS, /*world_id=*/1); + } + + std::string cleanMutationObserver(std::string mutation_observer) { + static constexpr char kScript[] = R"( + const task = $1; + const result = document.querySelector("browserTestResult_"+task).innerText; + document.querySelector("browserTestResult_"+task).remove(); + document.title = null; + result; + )"; + return EvalJs(web_contents()->GetMainFrame(), + JsReplace(kScript, mutation_observer), + EXECUTE_SCRIPT_DEFAULT_OPTIONS, + /*world_id=*/1) + .ExtractString(); + } + + std::string getServiceWorkerInfoFromInternalUI( + int64_t registration_id, + std::string service_worker_info) { + static constexpr char kScript[] = R"( + var serviceworkers = document.querySelectorAll( + "div#serviceworker-list > div:not([style='display: none;'])\ + > div:not([class='serviceworker-summary']) > \ + div.serviceworker-registration" + ); + + let result = ""; + serviceworkers.forEach((serviceworker) => { + let target; + Array.prototype.forEach.call( + serviceworker.querySelectorAll("*"), + (node) => + { + if ( + node.attributes.jscontent && + RegExp("registration_id").test(node.attributes.jscontent.value) && + node.innerText === $1 + ) { + target = serviceworker; + } + }); + if (target) { + Array.prototype.forEach.call(target.querySelectorAll("*"), (node) => { + if ( + node.attributes.jscontent && + RegExp($2+$3+$4).test(node.attributes.jscontent.value) + ) { + result = node.innerText; + } + }); + } + }); + result; + )"; + return EvalJs(web_contents()->GetMainFrame(), + JsReplace(kScript, base::NumberToString(registration_id), + "^(.this\\.)?(", service_worker_info, ")$"), + EXECUTE_SCRIPT_DEFAULT_OPTIONS, /*world_id=*/1) + .ExtractString(); + } + + private: + base::test::ScopedFeatureList feature_list_; + scoped_refptr<ServiceWorkerContextWrapper> wrapper_; + Shell* active_shell_ = shell(); +}; + +// Tests +IN_PROC_BROWSER_TEST_F(ServiceWorkerInternalsUIBrowserTest, + NoRegisteredServiceWorker) { + EXPECT_TRUE(CreateNewWindow()); + EXPECT_EQ(1, CountRenderProcessHosts()); + + NavigateToServiceWorkerInternalUI(); + + EXPECT_EQ(0, EvalJs(web_contents()->GetMainFrame(), + R"(document.querySelectorAll( + "div#serviceworker-list > div:not([style='display: none;'])\ + > div:not([class='serviceworker-summary'])\ + > div.serviceworker-registration" + ).length)", + EXECUTE_SCRIPT_DEFAULT_OPTIONS, /*world_id=*/1) + .ExtractInt()); + + TearDownWindow(); + EXPECT_EQ(0, CountRenderProcessHosts()); +} + +IN_PROC_BROWSER_TEST_F(ServiceWorkerInternalsUIBrowserTest, + RegisteredSWReflectedOnInternalUI) { + Shell* SWInternalUIWindow = CreateNewWindow(); + NavigateToServiceWorkerInternalUI(); + EXPECT_EQ(1, CountRenderProcessHosts()); + + setMutationObserverSWPopulated(); + + Shell* SWREgistrationWindow = CreateNewWindow(); + RegisterServiceWorker(); + EXPECT_EQ(2, CountRenderProcessHosts()); + + MoveToWindow(SWInternalUIWindow); + + TitleWatcher title_watcher(web_contents(), kCompleteTitle); + EXPECT_EQ(kCompleteTitle, title_watcher.WaitAndGetTitle()); + EXPECT_EQ("done", cleanMutationObserver("SWPopulated")); + + std::vector<ServiceWorkerRegistrationInfo> registrations = + GetAllRegistrations(); + EXPECT_EQ(1u, registrations.size()) + << "There should be exactly one registration"; + EXPECT_EQ(registrations[0].scope.spec(), + getServiceWorkerInfoFromInternalUI(registrations[0].registration_id, + "scope")); + EXPECT_EQ("ACTIVATED", getServiceWorkerInfoFromInternalUI( + registrations[0].registration_id, "status")); + UnRegisterServiceWorker(); + EXPECT_GE(2, CountRenderProcessHosts()) << "Unregistering doesn't stop the" + "workers eagerly, so their RPHs" + "can still be running."; + MoveToWindow(SWREgistrationWindow); + TearDownWindow(); + + MoveToWindow(SWInternalUIWindow); + TearDownWindow(); + EXPECT_GE(1, CountRenderProcessHosts()); +} +} // namespace content
diff --git a/content/browser/service_worker/service_worker_new_script_fetcher.cc b/content/browser/service_worker/service_worker_new_script_fetcher.cc index d0c6181..373cceb 100644 --- a/content/browser/service_worker/service_worker_new_script_fetcher.cc +++ b/content/browser/service_worker/service_worker_new_script_fetcher.cc
@@ -108,7 +108,8 @@ // is about to start. It fires `Network.onRequestWillBeSent` event. request.devtools_request_id = version_->reporting_source().ToString(); devtools_instrumentation::OnServiceWorkerMainScriptRequestWillBeSent( - requesting_frame_id_, version_->reporting_source(), request); + requesting_frame_id_, context_.wrapper(), version_->version_id(), + request); mojo::MakeSelfOwnedReceiver( ServiceWorkerNewScriptLoader::CreateAndStart(
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index befddcf..2229bff8 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc
@@ -100,6 +100,7 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test_utils.h" #include "content/public/test/content_mock_cert_verifier.h" +#include "content/public/test/fenced_frame_test_util.h" #include "content/public/test/hit_test_region_observer.h" #include "content/public/test/navigation_handle_observer.h" #include "content/public/test/policy_container_utils.h" @@ -3768,6 +3769,98 @@ EXPECT_EQ(named_frame_url, foo_root->current_url()); } +class SitePerProcessFencedFrameTest + : public SitePerProcessBrowserTestBase, + public testing::WithParamInterface< + blink::features::FencedFramesImplementationType> { + public: + SitePerProcessFencedFrameTest() { + if (GetParam() == + blink::features::FencedFramesImplementationType::kMPArch) { + fenced_frame_helper_ = + std::make_unique<content::test::FencedFrameTestHelper>(); + } else { + feature_list_.InitAndEnableFeatureWithParameters( + blink::features::kFencedFrames, + {{"implementation_type", "shadow_dom"}}); + } + } + + protected: + content::RenderFrameHost* CreateFencedFrame(content::RenderFrameHost* parent, + const GURL& url) { + if (fenced_frame_helper_) { + return fenced_frame_helper_->CreateFencedFrame(parent, url); + } + + // FencedFrameTestHelper only supports the MPArch version of fenced frames. + // So need to maually create a fenced frame for the ShadowDOM version. + content::TestNavigationManager navigation(web_contents(), url); + + constexpr char kAddFencedFrameScript[] = R"({ + const fenced_frame = document.createElement('fencedframe'); + fenced_frame.src = $1; + document.body.appendChild(fenced_frame); + })"; + EXPECT_TRUE(ExecJs(parent, content::JsReplace(kAddFencedFrameScript, url))); + navigation.WaitForNavigationFinished(); + + return ChildFrameAt(parent, 0); + } + + private: + base::test::ScopedFeatureList feature_list_; + std::unique_ptr<content::test::FencedFrameTestHelper> fenced_frame_helper_; +}; + +INSTANTIATE_TEST_SUITE_P( + SitePerProcessFencedFrameTest, + SitePerProcessFencedFrameTest, + testing::Values(blink::features::FencedFramesImplementationType::kShadowDOM, + blink::features::FencedFramesImplementationType::kMPArch)); + +IN_PROC_BROWSER_TEST_P(SitePerProcessFencedFrameTest, + PopupFromFencedFrameDoesNotCreateProxy) { + GURL main_url(embedded_test_server()->GetURL("/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + // It is safe to obtain the root frame tree node here, as it doesn't change. + FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root(); + + // Create a fenced frame. + GURL fenced_frame_url( + embedded_test_server()->GetURL("/fenced_frames/title1.html")); + RenderFrameHost* fenced_frame_host = + CreateFencedFrame(web_contents()->GetMainFrame(), fenced_frame_url); + EXPECT_NE(nullptr, fenced_frame_host); + + // Open a popup named "foo" from the fenced frame. + Shell* popup_shell = + OpenPopup(fenced_frame_host, GURL(url::kAboutBlankURL), "foo", "", false); + EXPECT_TRUE(popup_shell); + + // Check that the popup from the fenced frame didn't create a proxy. + // Opening popups from fenced frames forces noopener, which makes named + // frames not discoverable. + FrameTreeNode* popup_root = + static_cast<WebContentsImpl*>(popup_shell->web_contents()) + ->GetPrimaryFrameTree() + .root(); + EXPECT_EQ(nullptr, popup_root->opener()); + + SiteInstanceImpl* site_instance = + root->current_frame_host()->GetSiteInstance(); + EXPECT_FALSE(popup_root->current_frame_host() + ->browsing_context_state() + ->GetRenderFrameProxyHost(site_instance->group())); + + SiteInstanceImpl* embedder_site_instance = + static_cast<RenderFrameHostImpl*>(fenced_frame_host)->GetSiteInstance(); + EXPECT_FALSE(popup_root->current_frame_host() + ->browsing_context_state() + ->GetRenderFrameProxyHost(embedder_site_instance->group())); +} + // Similar to DiscoverNamedFrameFromAncestorOfOpener, but check that if a // window is created without a name and acquires window.name later, it will // still be discoverable from its opener's ancestors. Also, instead of using
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 62818b1..61bc6305 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -151,6 +151,7 @@ #include "services/network/public/cpp/web_sandbox_flags.h" #include "services/network/public/mojom/network_context.mojom.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/loader/resource_type_util.h" #include "third_party/blink/public/common/mime_util/mime_util.h" @@ -310,10 +311,23 @@ if (policy->IsPseudoScheme(protocol)) return false; + // Implementation of the protocol handler arguments normalization steps defined + // in the spec. + // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters + // + // Verify custom handler schemes for errors as described in steps 1 and 2 + if (!blink::IsValidCustomHandlerScheme(protocol, security_level)) + return false; + + // TODO(jfernandez): Should we include syntax checks (step 3) as we do in the + // renderer process ? + + // Verify custom handler URL security as described in steps 6 and 7 + if (!blink::IsAllowedCustomHandlerURL(url, security_level)) + return false; url::Origin url_origin = url::Origin::Create(url); if (url_origin.opaque()) return false; - if (security_level < blink::ProtocolHandlerSecurityLevel::kUntrustedOrigins && !origin.IsSameOriginWith(url)) return false; @@ -6180,6 +6194,9 @@ blink::ProtocolHandlerSecurityLevel security_level = delegate_->GetProtocolHandlerSecurityLevel(source); + // Run the protocol handler arguments normalization process defined in the + // spec. + // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters if (!AreValidRegisterProtocolHandlerArguments( protocol, url, source->GetLastCommittedOrigin(), security_level)) { ReceivedBadMessage(source->GetProcess(),
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc index ecab8fcc..2f39947 100644 --- a/content/browser/webid/federated_auth_request_impl_unittest.cc +++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -135,19 +135,11 @@ // absl::optional fields should be nullopt to prevent the corresponding // methods from having EXPECT_CALL set on the mocks. typedef struct { - std::string test_name; RequestParameters inputs; RequestExpectations expected; MockConfiguration config; } AuthRequestTestCase; -std::ostream& operator<<(std::ostream& os, - const AuthRequestTestCase& testcase) { - std::string name; - base::ReplaceChars(testcase.test_name, " ", "", &name); - return os << name; -} - static const MockMediatedConfiguration kMediatedNoop{absl::nullopt, kAccounts, absl::nullopt}; static const MockClientIdConfiguration kSuccessfulClientId{ @@ -165,8 +157,8 @@ static const MockClientIdConfiguration kClientMetadataNoPrivacyPolicyUrl{ FetchStatus::kSuccess, "", ""}; +// Error parsing FedCM manifest due to missing token endpoint. static const AuthRequestTestCase kMissingTokenEndpoint{ - "Error parsing FedCM manifest for Mediated mode missing token endpoint", {kIdpTestOrigin, kClientId, kNonce}, {RequestIdTokenStatus::kError, FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse, @@ -174,8 +166,8 @@ {kToken, FetchStatus::kSuccess, absl::nullopt, kAccountsEndpoint, "", kClientMetadataEndpoint, kMediatedNoop}}; +// Error parsing FedCM manifest due to missing accounts endpoint. static const AuthRequestTestCase kMissingAccountsEndpoint{ - "Error parsing FedCM manifest for Mediated mode missing accounts endpoint", {kIdpTestOrigin, kClientId, kNonce}, {RequestIdTokenStatus::kError, FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse, @@ -183,9 +175,8 @@ {kToken, FetchStatus::kSuccess, absl::nullopt, "", kTokenEndpoint, kClientMetadataEndpoint, kMediatedNoop}}; +// Error parsing FedCM manifest due to missing client metadata endpoint. static const AuthRequestTestCase kMissingClientMetadata{ - "Error parsing FedCM manifest for Mediated mode missing client metadata " - "endpoint", {kIdpTestOrigin, kClientId, kNonce}, {RequestIdTokenStatus::kError, FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse, @@ -193,96 +184,6 @@ {kToken, FetchStatus::kSuccess, absl::nullopt, kAccountsEndpoint, kTokenEndpoint, "", kMediatedNoop}}; -static const AuthRequestTestCase kMediatedTestCases[]{ - kMissingTokenEndpoint, - kMissingAccountsEndpoint, - kMissingClientMetadata, - - {"Error due to accounts endpoint in different origin than identity " - "provider", - {kIdpTestOrigin, kClientId, kNonce}, - {RequestIdTokenStatus::kError, - FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse, - kEmptyToken}, - {kToken, FetchStatus::kSuccess, absl::nullopt, - kCrossOriginAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint, - kMediatedNoop}}, - - {"Error reaching Accounts endpoint", - {kIdpTestOrigin, kClientId, kNonce}, - {RequestIdTokenStatus::kError, - FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse, - kEmptyToken}, - {kEmptyToken, - FetchStatus::kSuccess, - kSuccessfulClientId, - kAccountsEndpoint, - kTokenEndpoint, - kClientMetadataEndpoint, - {FetchStatus::kNoResponseError, kAccounts, absl::nullopt}}}, - - {"Error parsing Accounts response", - {kIdpTestOrigin, kClientId, kNonce}, - {RequestIdTokenStatus::kError, - FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse, - kEmptyToken}, - {kToken, - FetchStatus::kSuccess, - kSuccessfulClientId, - kAccountsEndpoint, - kTokenEndpoint, - kClientMetadataEndpoint, - {FetchStatus::kInvalidResponseError, kAccounts, absl::nullopt}}}, - - {"Successful Mediated flow", - {kIdpTestOrigin, kClientId, kNonce}, - {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess, - kToken}, - {kToken, - FetchStatus::kSuccess, - kSuccessfulClientId, - kAccountsEndpoint, - kTokenEndpoint, - kClientMetadataEndpoint, - {FetchStatus::kSuccess, kAccounts, FetchStatus::kSuccess}}}, - - {"Client metadata file not found", - {kIdpTestOrigin, kClientId, kNonce}, - {RequestIdTokenStatus::kError, - FederatedAuthRequestResult::kErrorFetchingClientMetadataHttpNotFound, - kEmptyToken}, - {kToken, FetchStatus::kSuccess, kClientMetadataHttpNotFound, - kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint, - kMediatedNoop}}, - - {"Client metadata empty response", - {kIdpTestOrigin, kClientId, kNonce}, - {RequestIdTokenStatus::kError, - FederatedAuthRequestResult::kErrorFetchingClientMetadataNoResponse, - kEmptyToken}, - {kToken, FetchStatus::kSuccess, kClientMetadataNoResponse, - kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint, - kMediatedNoop}}, - - {"Client metadata invalid response", - {kIdpTestOrigin, kClientId, kNonce}, - {RequestIdTokenStatus::kError, - FederatedAuthRequestResult::kErrorFetchingClientMetadataInvalidResponse, - kEmptyToken}, - {kToken, FetchStatus::kSuccess, kClientMetadataInvalidResponse, - kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint, - kMediatedNoop}}, - - {"Client metadata has no privacy policy url", - {kIdpTestOrigin, kClientId, kNonce}, - {RequestIdTokenStatus::kError, - FederatedAuthRequestResult::kErrorClientMetadataMissingPrivacyPolicyUrl, - kEmptyToken}, - {kToken, FetchStatus::kSuccess, kClientMetadataNoPrivacyPolicyUrl, - kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint, - kMediatedNoop}}, -}; - // Helper class for receiving the mojo method callback. class AuthRequestCallbackHelper { public: @@ -421,6 +322,74 @@ } ~FederatedAuthRequestImplTest() override = default; + void RunAuthTest(const AuthRequestTestCase& test_case) { + CreateAuthRequest(GURL(test_case.inputs.provider)); + SetMockExpectations(test_case); + auto auth_response = + PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce, + test_case.inputs.prefer_auto_sign_in); + EXPECT_EQ(auth_response.first, test_case.expected.return_status); + EXPECT_EQ(auth_response.second, test_case.expected.token); + + EXPECT_EQ(main_test_rfh()->GetFederatedAuthRequestIssueCount( + test_case.expected.devtools_issue_status), + auth_response.first == RequestIdTokenStatus::kSuccess ? 0 : 1); + CheckConsoleMessages(test_case.expected.devtools_issue_status); + } + + void CheckConsoleMessages(FederatedAuthRequestResult devtools_issue_status) { + static std::unordered_map<FederatedAuthRequestResult, + absl::optional<std::string>> + status_to_message = { + {FederatedAuthRequestResult::kSuccess, absl::nullopt}, + {FederatedAuthRequestResult::kApprovalDeclined, + "User declined the sign-in attempt."}, + {FederatedAuthRequestResult::kErrorFetchingManifestHttpNotFound, + "The provider's FedCM manifest configuration cannot be found."}, + {FederatedAuthRequestResult::kErrorFetchingManifestNoResponse, + "The provider's FedCM manifest configuration fetch resulted in an " + "error response code."}, + {FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse, + "Provider's FedCM manifest configuration is invalid."}, + {FederatedAuthRequestResult::kErrorFetchingSignin, + "Error attempting to reach the provider's sign-in endpoint."}, + {FederatedAuthRequestResult::kErrorInvalidSigninResponse, + "Provider's sign-in response is invalid."}, + {FederatedAuthRequestResult::kError, + "Error retrieving an id token."}, + {FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse, + "The provider's accounts list fetch resulted in an error response " + "code."}, + {FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse, + "Provider's accounts list is invalid. Should have received an " + "\"accounts\" list, where each account must " + "have at least \"id\", \"name\", and \"email\"."}, + {FederatedAuthRequestResult:: + kErrorFetchingClientMetadataHttpNotFound, + "The provider's client metadata endpoint cannot be found."}, + {FederatedAuthRequestResult::kErrorFetchingClientMetadataNoResponse, + "The provider's client metadata fetch resulted in an error " + "response " + "code."}, + {FederatedAuthRequestResult:: + kErrorFetchingClientMetadataInvalidResponse, + "Provider's client metadata is invalid."}, + {FederatedAuthRequestResult:: + kErrorClientMetadataMissingPrivacyPolicyUrl, + "Provider's client metadata is missing or has an invalid privacy " + "policy url."}}; + std::vector<std::string> messages = + RenderFrameHostTester::For(main_rfh())->GetConsoleMessages(); + absl::optional<std::string> expected_message = + status_to_message[devtools_issue_status]; + if (!expected_message) { + EXPECT_EQ(0u, messages.size()); + } else { + ASSERT_LE(1u, messages.size()); + EXPECT_EQ(expected_message.value(), messages[messages.size() - 1]); + } + } + FederatedAuthRequestImpl& CreateAuthRequest(const GURL& provider) { provider_ = provider; // `FederatedAuthRequestService` derives from `DocumentService` and @@ -727,93 +696,29 @@ std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_; }; -class BasicFederatedAuthRequestImplTest - : public FederatedAuthRequestImplTest, - public ::testing::WithParamInterface<AuthRequestTestCase> {}; +class BasicFederatedAuthRequestImplTest : public FederatedAuthRequestImplTest { +}; -INSTANTIATE_TEST_SUITE_P(MediatedTests, - BasicFederatedAuthRequestImplTest, - ::testing::ValuesIn(kMediatedTestCases), - ::testing::PrintToStringParamName()); - -// Exercise the auth test case give the configuration. -TEST_P(BasicFederatedAuthRequestImplTest, FederatedAuthRequests) { - AuthRequestTestCase test_case = GetParam(); - CreateAuthRequest(GURL(test_case.inputs.provider)); - SetMockExpectations(test_case); - auto auth_response = - PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce, - test_case.inputs.prefer_auto_sign_in); - EXPECT_EQ(auth_response.first, test_case.expected.return_status); - EXPECT_EQ(auth_response.second, test_case.expected.token); +// Test successful FedCM request. +TEST_F(BasicFederatedAuthRequestImplTest, SuccessfulRequest) { + AuthRequestTestCase test_case = { + {kIdpTestOrigin, kClientId, kNonce}, + {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess, + kToken}, + {kToken, + FetchStatus::kSuccess, + kSuccessfulClientId, + kAccountsEndpoint, + kTokenEndpoint, + kClientMetadataEndpoint, + {FetchStatus::kSuccess, kAccounts, FetchStatus::kSuccess}}}; + RunAuthTest(test_case); } -TEST_P(BasicFederatedAuthRequestImplTest, FederatedAuthRequestIssue) { - AuthRequestTestCase test_case = GetParam(); - CreateAuthRequest(GURL(test_case.inputs.provider)); - SetMockExpectations(test_case); - auto auth_response = - PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce, - test_case.inputs.prefer_auto_sign_in); - EXPECT_EQ(main_test_rfh()->GetFederatedAuthRequestIssueCount( - test_case.expected.devtools_issue_status), - auth_response.first == RequestIdTokenStatus::kSuccess ? 0 : 1); - static std::unordered_map<FederatedAuthRequestResult, - absl::optional<std::string>> - status_to_message = { - {FederatedAuthRequestResult::kSuccess, absl::nullopt}, - {FederatedAuthRequestResult::kApprovalDeclined, - "User declined the sign-in attempt."}, - {FederatedAuthRequestResult::kErrorFetchingManifestHttpNotFound, - "The provider's FedCM manifest configuration cannot be found."}, - {FederatedAuthRequestResult::kErrorFetchingManifestNoResponse, - "The provider's FedCM manifest configuration fetch resulted in an " - "error response code."}, - {FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse, - "Provider's FedCM manifest configuration is invalid."}, - {FederatedAuthRequestResult::kErrorFetchingSignin, - "Error attempting to reach the provider's sign-in endpoint."}, - {FederatedAuthRequestResult::kErrorInvalidSigninResponse, - "Provider's sign-in response is invalid."}, - {FederatedAuthRequestResult::kError, "Error retrieving an id token."}, - {FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse, - "The provider's accounts list fetch resulted in an error response " - "code."}, - {FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse, - "Provider's accounts list is invalid. Should have received an " - "\"accounts\" list, where each account must " - "have at least \"id\", \"name\", and \"email\"."}, - {FederatedAuthRequestResult::kErrorFetchingClientMetadataHttpNotFound, - "The provider's client metadata endpoint cannot be found."}, - {FederatedAuthRequestResult::kErrorFetchingClientMetadataNoResponse, - "The provider's client metadata fetch resulted in an error response " - "code."}, - {FederatedAuthRequestResult:: - kErrorFetchingClientMetadataInvalidResponse, - "Provider's client metadata is invalid."}, - {FederatedAuthRequestResult:: - kErrorClientMetadataMissingPrivacyPolicyUrl, - "Provider's client metadata is missing or has an invalid privacy " - "policy url."}}; - std::vector<std::string> messages = - RenderFrameHostTester::For(main_rfh())->GetConsoleMessages(); - absl::optional<std::string> expected_message = - status_to_message[test_case.expected.devtools_issue_status]; - if (!expected_message) { - EXPECT_EQ(0u, messages.size()); - } else { - ASSERT_LE(1u, messages.size()); - EXPECT_EQ(expected_message.value(), messages[messages.size() - 1]); - } -} - +// Test that request fails if manifest is missing token endpoint. TEST_F(BasicFederatedAuthRequestImplTest, MissingTokenEndpoint) { - const auto& test_case = kMissingTokenEndpoint; - CreateAuthRequest(GURL(test_case.inputs.provider)); - SetMockExpectations(test_case); - auto auth_response = - PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce, - test_case.inputs.prefer_auto_sign_in); + RunAuthTest(kMissingTokenEndpoint); + std::vector<std::string> messages = RenderFrameHostTester::For(main_rfh())->GetConsoleMessages(); ASSERT_EQ(2U, messages.size()); @@ -825,13 +730,10 @@ EXPECT_EQ("Provider's FedCM manifest configuration is invalid.", messages[1]); } +// Test that request fails if manifest is missing accounts endpoint. TEST_F(BasicFederatedAuthRequestImplTest, MissingAccountsEndpoint) { - const auto& test_case = kMissingAccountsEndpoint; - CreateAuthRequest(GURL(test_case.inputs.provider)); - SetMockExpectations(test_case); - auto auth_response = - PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce, - test_case.inputs.prefer_auto_sign_in); + RunAuthTest(kMissingAccountsEndpoint); + std::vector<std::string> messages = RenderFrameHostTester::For(main_rfh())->GetConsoleMessages(); ASSERT_EQ(2U, messages.size()); @@ -843,13 +745,10 @@ EXPECT_EQ("Provider's FedCM manifest configuration is invalid.", messages[1]); } +// Test that request fails if manifest is missing client metadata endpoint. TEST_F(BasicFederatedAuthRequestImplTest, MissingClientMetadataEndpoint) { - const auto& test_case = kMissingClientMetadata; - CreateAuthRequest(GURL(test_case.inputs.provider)); - SetMockExpectations(test_case); - auto auth_response = - PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce, - test_case.inputs.prefer_auto_sign_in); + RunAuthTest(kMissingClientMetadata); + std::vector<std::string> messages = RenderFrameHostTester::For(main_rfh())->GetConsoleMessages(); ASSERT_EQ(2U, messages.size()); @@ -861,10 +760,111 @@ EXPECT_EQ("Provider's FedCM manifest configuration is invalid.", messages[1]); } +// Test that request fails if the accounts endpoint is in a different origin +// than identity provider. +TEST_F(BasicFederatedAuthRequestImplTest, AccountEndpointDifferentOriginIdp) { + AuthRequestTestCase test_case = { + {kIdpTestOrigin, kClientId, kNonce}, + {RequestIdTokenStatus::kError, + FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse, + kEmptyToken}, + {kToken, FetchStatus::kSuccess, absl::nullopt, + kCrossOriginAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint, + kMediatedNoop}}; + RunAuthTest(test_case); +} + +// Test that request fails if accounts endpoint cannot be reached. +TEST_F(BasicFederatedAuthRequestImplTest, AccountEndpointCannotBeReached) { + AuthRequestTestCase test_case = { + {kIdpTestOrigin, kClientId, kNonce}, + {RequestIdTokenStatus::kError, + FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse, + kEmptyToken}, + {kEmptyToken, + FetchStatus::kSuccess, + kSuccessfulClientId, + kAccountsEndpoint, + kTokenEndpoint, + kClientMetadataEndpoint, + {FetchStatus::kNoResponseError, kAccounts, absl::nullopt}}}; + RunAuthTest(test_case); +} + +// Test that request fails if account endpoint response cannot be parsed. +TEST_F(BasicFederatedAuthRequestImplTest, AccountsCannotBeParsed) { + AuthRequestTestCase test_case = { + {kIdpTestOrigin, kClientId, kNonce}, + {RequestIdTokenStatus::kError, + FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse, + kEmptyToken}, + {kToken, + FetchStatus::kSuccess, + kSuccessfulClientId, + kAccountsEndpoint, + kTokenEndpoint, + kClientMetadataEndpoint, + {FetchStatus::kInvalidResponseError, kAccounts, absl::nullopt}}}; + RunAuthTest(test_case); +} + +// Test that request fails if client metadata cannot be found. +TEST_F(BasicFederatedAuthRequestImplTest, ClientMetadataNotFound) { + AuthRequestTestCase test_case = { + {kIdpTestOrigin, kClientId, kNonce}, + {RequestIdTokenStatus::kError, + FederatedAuthRequestResult::kErrorFetchingClientMetadataHttpNotFound, + kEmptyToken}, + {kToken, FetchStatus::kSuccess, kClientMetadataHttpNotFound, + kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint, + kMediatedNoop}}; + RunAuthTest(test_case); +} + +// Test that request fails if client metadata endpoint returns empty response. +TEST_F(BasicFederatedAuthRequestImplTest, ClientMetadataEmptyResponse) { + AuthRequestTestCase test_case = { + {kIdpTestOrigin, kClientId, kNonce}, + {RequestIdTokenStatus::kError, + FederatedAuthRequestResult::kErrorFetchingClientMetadataNoResponse, + kEmptyToken}, + {kToken, FetchStatus::kSuccess, kClientMetadataNoResponse, + kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint, + kMediatedNoop}}; + RunAuthTest(test_case); +} + +// Test that request fails if client metadata returns invalid response. +TEST_F(BasicFederatedAuthRequestImplTest, ClientMetadataInvalidResponse) { + AuthRequestTestCase test_case = { + {kIdpTestOrigin, kClientId, kNonce}, + {RequestIdTokenStatus::kError, + FederatedAuthRequestResult::kErrorFetchingClientMetadataInvalidResponse, + kEmptyToken}, + {kToken, FetchStatus::kSuccess, kClientMetadataInvalidResponse, + kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint, + kMediatedNoop}}; + RunAuthTest(test_case); +} + +// Test that request fails if client metadata does not contain a privacy policy +// URL. +TEST_F(BasicFederatedAuthRequestImplTest, ClientMetadataNoPrivacyUrl) { + AuthRequestTestCase test_case = { + {kIdpTestOrigin, kClientId, kNonce}, + {RequestIdTokenStatus::kError, + FederatedAuthRequestResult::kErrorClientMetadataMissingPrivacyPolicyUrl, + kEmptyToken}, + {kToken, FetchStatus::kSuccess, kClientMetadataNoPrivacyPolicyUrl, + kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint, + kMediatedNoop}}; + RunAuthTest(test_case); +} + +// Test that request fails if all of the endpoints in the manifest are invalid. TEST_F(BasicFederatedAuthRequestImplTest, AllInvalidEndpoints) { // Both an empty url and cross origin urls are invalid endpoints. AuthRequestTestCase test_case = { - "FedCM manifest missing all endpoints", {kIdpTestOrigin, kClientId, kNonce}, {RequestIdTokenStatus::kError, FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse, @@ -946,8 +946,8 @@ // Tests for Login State +// Successful mediated flow with one account. static const AuthRequestTestCase kSuccessfulMediatedSignUpTestCase{ - "Successful mediated flow with one account", {kIdpTestOrigin, kClientId, kNonce, kNotPreferAutoSignIn}, {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess, kToken}, @@ -959,8 +959,8 @@ kClientMetadataEndpoint, {FetchStatus::kSuccess, kAccounts, FetchStatus::kSuccess}}}; +// Failed mediated flow with one account. static const AuthRequestTestCase kFailedMediatedSignUpTestCase{ - "Failed mediated flow with one account", {kIdpTestOrigin, kClientId, kNonce, kNotPreferAutoSignIn}, {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess, kToken}, @@ -972,8 +972,8 @@ kClientMetadataEndpoint, {FetchStatus::kSuccess, kAccounts, FetchStatus::kInvalidResponseError}}}; +// Successful mediated flow with one account. static const AuthRequestTestCase kSuccessfulMediatedAutoSignInTestCase{ - "Successful mediated flow with one account", {kIdpTestOrigin, kClientId, kNonce, kPreferAutoSignIn}, {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess, kToken}, @@ -1346,12 +1346,12 @@ ExpectRequestIdTokenStatusUKM(IdTokenStatus::kSuccess); } +// Test that request fails if user does not select an account. TEST_F(BasicFederatedAuthRequestImplTest, MetricsForNotSelectingAccount) { base::HistogramTester histogram_tester_; AccountList displayed_accounts; const AuthRequestTestCase test_case = { - "Failed mediated flow due to user not selecting an account", {kIdpTestOrigin, kClientId, kNonce, kNotPreferAutoSignIn}, {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess, kToken}, @@ -1440,6 +1440,7 @@ histogram_tester_.ExpectUniqueSample("Blink.FedCm.WebContentsVisible", 1, 1); } +// Test that request fails if the web contents are hidden. TEST_F(BasicFederatedAuthRequestImplTest, MetricsForWebContentsInvisible) { base::HistogramTester histogram_tester; WebContentsImpl* web_contents_impl = @@ -1448,7 +1449,6 @@ ASSERT_EQ(web_contents_impl->GetVisibility(), Visibility::VISIBLE); const AuthRequestTestCase test_case = { - "Failed mediated flow due to user leaving the page", {kIdpTestOrigin, kClientId, kNonce, kNotPreferAutoSignIn}, {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess, kToken},
diff --git a/content/child/child_process_sandbox_support_impl_mac.cc b/content/child/child_process_sandbox_support_impl_mac.cc index fc7f56d..dc7a9464 100644 --- a/content/child/child_process_sandbox_support_impl_mac.cc +++ b/content/child/child_process_sandbox_support_impl_mac.cc
@@ -9,11 +9,10 @@ #include "base/bind.h" #include "base/mac/scoped_cftyperef.h" -#include "base/numerics/safe_conversions.h" +#include "base/memory/read_only_shared_memory_region.h" #include "base/strings/sys_string_conversions.h" #include "content/common/mac/font_loader.h" #include "content/public/child/child_thread.h" -#include "mojo/public/cpp/system/buffer.h" namespace content { @@ -36,10 +35,10 @@ base::ScopedCFTypeRef<CFStringRef> name_ref(CTFontCopyPostScriptName(font)); std::u16string font_name = SysCFStringRefToUTF16(name_ref); float font_point_size = CTFontGetSize(font); - mojo::ScopedSharedBufferHandle font_data; + base::ReadOnlySharedMemoryRegion font_data; bool success = sandbox_support_->LoadFont(font_name, font_point_size, &font_data, font_id) && - *font_id > 0 && font_data.is_valid(); + *font_id > 0 && font_data.IsValid(); if (!success) { DLOG(ERROR) << "Bad response from LoadFont() for " << font_name; out_descriptor->reset(); @@ -47,16 +46,14 @@ return false; } - uint64_t font_data_size = font_data->GetSize(); + size_t font_data_size = font_data.GetSize(); DCHECK_GT(font_data_size, 0U); - DCHECK(base::IsValueInRangeForNumericType<uint32_t>(font_data_size)); // TODO(jeremy): Need to call back into the requesting process to make sure // that the font isn't already activated, based on the font id. If it's // already activated, don't reactivate it here - https://crbug.com/72727 . - return FontLoader::CTFontDescriptorFromBuffer( - std::move(font_data), static_cast<uint32_t>(font_data_size), - out_descriptor); + return FontLoader::CTFontDescriptorFromBuffer(std::move(font_data), + out_descriptor); } SkColor WebSandboxSupportMac::GetSystemColor(
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 81ea9545..e9db09d 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -376,6 +376,7 @@ {"ThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes", blink::features:: kThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes}, + {"TopicsAPI", blink::features::kBrowsingTopics}, {"TrustedDOMTypes", features::kTrustedDOMTypes}, {"UserAgentClientHint", blink::features::kUserAgentClientHint}, {"ViewportHeightClientHintHeader",
diff --git a/content/common/mac/font_loader.h b/content/common/mac/font_loader.h index 4d3f3c8a..e0ccbb3 100644 --- a/content/common/mac/font_loader.h +++ b/content/common/mac/font_loader.h
@@ -13,8 +13,8 @@ #include "base/callback_forward.h" #include "base/mac/scoped_cftyperef.h" +#include "base/memory/read_only_shared_memory_region.h" #include "content/common/content_export.h" -#include "mojo/public/cpp/system/buffer.h" namespace content { @@ -30,17 +30,17 @@ ResultInternal(); ~ResultInternal(); - mojo::ScopedSharedBufferHandle font_data; + base::ReadOnlySharedMemoryRegion font_data; uint32_t font_id = 0; }; // Callback for the reporting result of LoadFont(). - // - The ScopedSharedBufferHandle points to a shared memory buffer containing - // the raw data for the font file. + // - The base::ReadOnlySharedMemoryRegion points to a shared memory region + // containing the raw data for the font file. // - The last argument is the font_id: a unique identifier for the on-disk // file we load for the font. using LoadedCallback = - base::OnceCallback<void(mojo::ScopedSharedBufferHandle, uint32_t)>; + base::OnceCallback<void(base::ReadOnlySharedMemoryRegion, uint32_t)>; // Load a font specified by |font| into a shared memory buffer suitable for // sending over IPC. On failure, zeroes and an invalid handle are reported @@ -50,10 +50,10 @@ float font_point_size, LoadedCallback callback); - // Given a shared memory buffer containing the raw data for a font file, load + // Given a shared memory region containing the raw data for a font file, load // the font and turn them into a CTFontDescriptor. // - // |data| - A shared memory handle pointing to the raw data from a font file. + // |data| - A shared memory region contained the raw data from a font file. // |data_size| - Size of |data|. // // On return: @@ -62,8 +62,7 @@ // The caller is responsible for releasing this value via CFRelease(). CONTENT_EXPORT static bool CTFontDescriptorFromBuffer( - mojo::ScopedSharedBufferHandle font_data, - uint32_t font_data_size, + base::ReadOnlySharedMemoryRegion font_data, base::ScopedCFTypeRef<CTFontDescriptorRef>* out_descriptor); CONTENT_EXPORT
diff --git a/content/common/mac/font_loader.mm b/content/common/mac/font_loader.mm index 51c0bf2..fca241f0 100644 --- a/content/common/mac/font_loader.mm +++ b/content/common/mac/font_loader.mm
@@ -18,6 +18,8 @@ #import "base/mac/foundation_util.h" #include "base/mac/scoped_cftyperef.h" #import "base/mac/scoped_nsobject.h" +#include "base/memory/read_only_shared_memory_region.h" +#include "base/numerics/safe_conversions.h" #include "base/strings/sys_string_conversions.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" @@ -76,22 +78,22 @@ auto result = std::make_unique<FontLoader::ResultInternal>(); - int32_t font_file_size_32 = static_cast<int32_t>(font_file_size_64); - result->font_data = mojo::SharedBufferHandle::Create(font_file_size_32); - if (!result->font_data.is_valid()) { + // Note: despite the fact that CTFontDescriptorFromBuffer() takes a uint32_t + // size, this intentionally works with int32_t since base::ReadFile()'s max + // size arg is an int... + int32_t font_file_size_32 = base::checked_cast<int32_t>(font_file_size_64); + auto region_and_mapping = + base::ReadOnlySharedMemoryRegion::Create(font_file_size_32); + + if (!region_and_mapping.IsValid()) { DLOG(ERROR) << "Failed to create shmem area for " << font_name; return nullptr; } + result->font_data = std::move(region_and_mapping.region); - mojo::ScopedSharedBufferMapping mapping = - result->font_data->Map(font_file_size_32); - if (!mapping) { - DLOG(ERROR) << "Failed to create shmem mapping for " << font_name; - return nullptr; - } - - int32_t amt_read = base::ReadFile( - font_path, static_cast<char*>(mapping.get()), font_file_size_32); + int32_t amt_read = + base::ReadFile(font_path, region_and_mapping.mapping.GetMemoryAs<char>(), + font_file_size_32); if (amt_read != font_file_size_32) { DLOG(ERROR) << "Failed to read font data for " << font_path.value(); return nullptr; @@ -112,7 +114,7 @@ void ReplyOnUIThread(FontLoader::LoadedCallback callback, std::unique_ptr<FontLoader::ResultInternal> result) { if (!result) { - std::move(callback).Run(mojo::ScopedSharedBufferHandle(), 0); + std::move(callback).Run(base::ReadOnlySharedMemoryRegion(), 0); return; } @@ -144,15 +146,14 @@ // static bool FontLoader::CTFontDescriptorFromBuffer( - mojo::ScopedSharedBufferHandle font_data, - uint32_t font_data_size, + base::ReadOnlySharedMemoryRegion font_data, base::ScopedCFTypeRef<CTFontDescriptorRef>* out_descriptor) { out_descriptor->reset(); - mojo::ScopedSharedBufferMapping mapping = font_data->Map(font_data_size); - if (!mapping) + base::ReadOnlySharedMemoryMapping mapping = font_data.Map(); + if (!mapping.IsValid()) return false; - NSData* data = [NSData dataWithBytes:mapping.get() length:font_data_size]; + NSData* data = [NSData dataWithBytes:mapping.memory() length:mapping.size()]; base::ScopedCFTypeRef<CTFontDescriptorRef> data_descriptor( CTFontManagerCreateFontDescriptorFromData(base::mac::NSToCFCast(data)));
diff --git a/content/common/sandbox_support_mac.mojom b/content/common/sandbox_support_mac.mojom index 4bc19e2a..f7062a2 100644 --- a/content/common/sandbox_support_mac.mojom +++ b/content/common/sandbox_support_mac.mojom
@@ -17,5 +17,6 @@ // Request the browser to load a font into a shared memory buffer. [Sync] LoadFont(mojo_base.mojom.String16 font_name, float font_point_size) - => (handle<shared_buffer>? font_data, uint32 font_id); + => (mojo_base.mojom.ReadOnlySharedMemoryRegion? font_data, + uint32 font_id); };
diff --git a/content/public/browser/browsing_topics_site_data_manager.h b/content/public/browser/browsing_topics_site_data_manager.h index bb81bb2f..0b194814 100644 --- a/content/public/browser/browsing_topics_site_data_manager.h +++ b/content/public/browser/browsing_topics_site_data_manager.h
@@ -40,7 +40,7 @@ // Persist the browsing topics api usage context to storage. Called when the // usage is detected in a context on a page. virtual void OnBrowsingTopicsApiUsed( - const browsing_topics::HashedHost& hashed_top_host, + const browsing_topics::HashedHost& hashed_main_frame_host, const base::flat_set<browsing_topics::HashedDomain>& hashed_context_domains) = 0; };
diff --git a/content/public/browser/download_manager_delegate.h b/content/public/browser/download_manager_delegate.h index ee482dfe..20b2e89 100644 --- a/content/public/browser/download_manager_delegate.h +++ b/content/public/browser/download_manager_delegate.h
@@ -56,6 +56,9 @@ // |intermediate_path| could be the same as |target_path|. Both paths must // be in the same directory. // +// |display_name| specifies the suggested file name in case the file name cannot +// be determined from target_path. Could be empty. +// // |interrupt_reason| should be set to DOWNLOAD_INTERRUPT_REASON_NONE in // order to proceed with the download. DOWNLOAD_INTERRUPT_REASON_USER_CANCEL // results in the download being marked cancelled. Any other value results @@ -67,6 +70,7 @@ download::DownloadDangerType danger_type, download::DownloadItem::MixedContentStatus mixed_content_status, const base::FilePath& intermediate_path, + const base::FilePath& display_name, absl::optional<download::DownloadSchedule> download_schedule, download::DownloadInterruptReason interrupt_reason)>;
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index 68b521f..cb672eb 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h
@@ -585,7 +585,7 @@ // Returns whether a navigation is currently in progress that should show // loading UI if such UI exists (progress bar, loading spinner, stop button, - // etc.) True for different-document navigations and appHistory's + // etc.) True for different-document navigations and the navigation API's // transitionWhile(). This being true implies that IsLoading() is also true. virtual bool ShouldShowLoadingUI() = 0;
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h index f889d0b..9c70cc9 100644 --- a/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h
@@ -166,9 +166,9 @@ // in UI elements. It is generally true for different-document navigations and // false for most same-document navigations (because same-documents are // typically instantaneous so there's no point in flickering the UI). The - // exception is appHistory's transitionWhile, which is the sole type of - // same-document navigation that is asynchronous, and therefore a UI change is - // sensible. + // exception is the navigation API's transitionWhile(), which is the sole type + // of same-document navigation that is asynchronous, and therefore a UI change + // is sensible. virtual void LoadingStateChanged(WebContents* source, bool should_show_loading_ui) {}
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc index 5bd1b99..48a524a7 100644 --- a/content/public/renderer/content_renderer_client.cc +++ b/content/public/renderer/content_renderer_client.cc
@@ -161,10 +161,6 @@ std::move(cb).Run({}); } -bool ContentRendererClient::IsKeySystemsUpdateNeeded() { - return false; -} - bool ContentRendererClient::IsSupportedAudioType(const media::AudioType& type) { // Defer to media's default support. return ::media::IsDefaultSupportedAudioType(type);
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h index 63ead7f8..eb8968c 100644 --- a/content/public/renderer/content_renderer_client.h +++ b/content/public/renderer/content_renderer_client.h
@@ -254,11 +254,6 @@ // Allows embedder to register the key system(s) it supports. virtual void GetSupportedKeySystems(media::GetSupportedKeySystemsCB cb); - // Signal that embedder has changed key systems. - // TODO(chcunningham): Refactor this to a proper change "observer" API that is - // less fragile (don't assume GetSupportedKeySystems has just one caller). - virtual bool IsKeySystemsUpdateNeeded(); - // Allows embedder to describe customized audio capabilities. virtual bool IsSupportedAudioType(const media::AudioType& type);
diff --git a/content/public/renderer/key_system_support.cc b/content/public/renderer/key_system_support.cc index 7ffed2be..7aee5af 100644 --- a/content/public/renderer/key_system_support.cc +++ b/content/public/renderer/key_system_support.cc
@@ -6,36 +6,50 @@ #include "base/logging.h" #include "content/public/renderer/render_thread.h" -#include "media/mojo/mojom/key_system_support.mojom.h" #include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" namespace content { namespace { -// Helper function to help hold the `key_system_support` remote. -void OnIsKeySystemSupportedResult( - mojo::Remote<media::mojom::KeySystemSupport> key_system_support, - IsKeySystemSupportedCB cb, - bool is_supported, - media::mojom::KeySystemCapabilityPtr capability) { - std::move(cb).Run(is_supported, std::move(capability)); -} +class KeySystemSupportObserverImpl + : public media::mojom::KeySystemSupportObserver { + public: + explicit KeySystemSupportObserverImpl(KeySystemSupportCB cb) + : key_system_support_cb_(std::move(cb)) {} + KeySystemSupportObserverImpl(const KeySystemSupportObserverImpl&) = delete; + KeySystemSupportObserverImpl& operator=(const KeySystemSupportObserverImpl&) = + delete; + ~KeySystemSupportObserverImpl() override = default; + + // media::mojom::KeySystemSupportObserver + void OnKeySystemSupportUpdated( + KeySystemCapabilityPtrMap key_system_capabilities) final { + key_system_support_cb_.Run(std::move(key_system_capabilities)); + } + + private: + KeySystemSupportCB key_system_support_cb_; +}; } // namespace -void IsKeySystemSupported(const std::string& key_system, - IsKeySystemSupportedCB cb) { - DVLOG(3) << __func__ << ": key_system=" << key_system; +void ObserveKeySystemSupportUpdate(KeySystemSupportCB cb) { + DVLOG(1) << __func__; + // `key_system_support` will be destructed after this function returns. This + // is fine since the observer will stay registered in KeySystemSupportImpl, + // which is a singleton in the browser process. mojo::Remote<media::mojom::KeySystemSupport> key_system_support; content::RenderThread::Get()->BindHostReceiver( key_system_support.BindNewPipeAndPassReceiver()); - auto* key_system_support_raw = key_system_support.get(); - key_system_support_raw->IsKeySystemSupported( - key_system, base::BindOnce(&OnIsKeySystemSupportedResult, - std::move(key_system_support), std::move(cb))); + mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_remote; + mojo::MakeSelfOwnedReceiver( + std::make_unique<KeySystemSupportObserverImpl>(std::move(cb)), + observer_remote.InitWithNewPipeAndPassReceiver()); + key_system_support->AddObserver(std::move(observer_remote)); } } // namespace content
diff --git a/content/public/renderer/key_system_support.h b/content/public/renderer/key_system_support.h index 911799c..b82376d 100644 --- a/content/public/renderer/key_system_support.h +++ b/content/public/renderer/key_system_support.h
@@ -6,20 +6,23 @@ #define CONTENT_PUBLIC_RENDERER_KEY_SYSTEM_SUPPORT_H_ #include <string> -#include <vector> +#include "base/callback.h" +#include "base/containers/flat_map.h" #include "content/common/content_export.h" #include "media/mojo/mojom/key_system_support.mojom.h" namespace content { -using IsKeySystemSupportedCB = - media::mojom::KeySystemSupport::IsKeySystemSupportedCallback; +using KeySystemCapabilityPtrMap = + base::flat_map<std::string, media::mojom::KeySystemCapabilityPtr>; +using KeySystemSupportCB = + base::RepeatingCallback<void(KeySystemCapabilityPtrMap)>; -// Determines if |key_system| is supported by calling into the browser and -// calls the `cb` with the result. -CONTENT_EXPORT void IsKeySystemSupported(const std::string& key_system, - IsKeySystemSupportedCB cb); +// Observes key system support updates. The callback `cb` will be called with +// the current key system support, then called every time the key system support +// changes. +CONTENT_EXPORT void ObserveKeySystemSupportUpdate(KeySystemSupportCB cb); } // namespace content
diff --git a/content/public/test/browsing_topics_test_util.cc b/content/public/test/browsing_topics_test_util.cc new file mode 100644 index 0000000..bba7d29c --- /dev/null +++ b/content/public/test/browsing_topics_test_util.cc
@@ -0,0 +1,44 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/public/test/browsing_topics_test_util.h" + +#include "content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h" + +namespace content { + +TesterBrowsingTopicsSiteDataManager::TesterBrowsingTopicsSiteDataManager( + const base::FilePath& user_data_directory) + : manager_impl_( + new BrowsingTopicsSiteDataManagerImpl(user_data_directory)) {} + +void TesterBrowsingTopicsSiteDataManager::ExpireDataBefore(base::Time time) { + manager_impl_->ExpireDataBefore(time); +} + +TesterBrowsingTopicsSiteDataManager::~TesterBrowsingTopicsSiteDataManager() = + default; + +void TesterBrowsingTopicsSiteDataManager::OnBrowsingTopicsApiUsed( + const browsing_topics::HashedHost& hashed_top_host, + const base::flat_set<browsing_topics::HashedDomain>& + hashed_context_domains) { + manager_impl_->OnBrowsingTopicsApiUsed(hashed_top_host, + hashed_context_domains); +} + +void TesterBrowsingTopicsSiteDataManager::GetBrowsingTopicsApiUsage( + base::Time begin_time, + base::Time end_time, + GetBrowsingTopicsApiUsageCallback callback) { + if (!query_failure_override_) { + manager_impl_->GetBrowsingTopicsApiUsage(begin_time, end_time, + std::move(callback)); + return; + } + + std::move(callback).Run(browsing_topics::ApiUsageContextQueryResult()); +} + +} // namespace content
diff --git a/content/public/test/browsing_topics_test_util.h b/content/public/test/browsing_topics_test_util.h new file mode 100644 index 0000000..eab0e193 --- /dev/null +++ b/content/public/test/browsing_topics_test_util.h
@@ -0,0 +1,60 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_PUBLIC_TEST_BROWSING_TOPICS_TEST_UTIL_H_ +#define CONTENT_PUBLIC_TEST_BROWSING_TOPICS_TEST_UTIL_H_ + +#include "base/files/file_path.h" +#include "content/public/browser/browsing_topics_site_data_manager.h" + +namespace content { + +class BrowsingTopicsSiteDataManagerImpl; + +// A tester class that allows mocking a query failure (e.g. database error). +class TesterBrowsingTopicsSiteDataManager + : public BrowsingTopicsSiteDataManager { + public: + explicit TesterBrowsingTopicsSiteDataManager( + const base::FilePath& user_data_directory); + + ~TesterBrowsingTopicsSiteDataManager() override; + + TesterBrowsingTopicsSiteDataManager( + const TesterBrowsingTopicsSiteDataManager&) = delete; + TesterBrowsingTopicsSiteDataManager& operator=( + const TesterBrowsingTopicsSiteDataManager&) = delete; + TesterBrowsingTopicsSiteDataManager(TesterBrowsingTopicsSiteDataManager&&) = + delete; + TesterBrowsingTopicsSiteDataManager& operator=( + TesterBrowsingTopicsSiteDataManager&&) = delete; + + // Use the default handling from `BrowsingTopicsSiteDataManagerImpl`. + void ExpireDataBefore(base::Time time) override; + + // Use the default handling from `BrowsingTopicsSiteDataManagerImpl`. + void OnBrowsingTopicsApiUsed( + const browsing_topics::HashedHost& hashed_top_host, + const base::flat_set<browsing_topics::HashedDomain>& + hashed_context_domains) override; + + void SetQueryFailureOverride() { query_failure_override_ = true; } + + // Return a default/failed `ApiUsageContextQueryResult` if + // `query_failure_override_` is true; otherwise, sse the default handling from + // `BrowsingTopicsSiteDataManagerImpl`. + void GetBrowsingTopicsApiUsage( + base::Time begin_time, + base::Time end_time, + GetBrowsingTopicsApiUsageCallback callback) override; + + private: + std::unique_ptr<BrowsingTopicsSiteDataManagerImpl> manager_impl_; + + bool query_failure_override_ = false; +}; + +} // namespace content + +#endif // CONTENT_PUBLIC_TEST_BROWSING_TOPICS_TEST_UTIL_H_
diff --git a/content/renderer/media/render_media_client.cc b/content/renderer/media/render_media_client.cc index 9d8112c..a2c7b34 100644 --- a/content/renderer/media/render_media_client.cc +++ b/content/renderer/media/render_media_client.cc
@@ -30,10 +30,6 @@ GetContentClient()->renderer()->GetSupportedKeySystems(std::move(cb)); } -bool RenderMediaClient::IsKeySystemsUpdateNeeded() { - return GetContentClient()->renderer()->IsKeySystemsUpdateNeeded(); -} - bool RenderMediaClient::IsSupportedAudioType(const media::AudioType& type) { return GetContentClient()->renderer()->IsSupportedAudioType(type); }
diff --git a/content/renderer/media/render_media_client.h b/content/renderer/media/render_media_client.h index 6997146..189f7344 100644 --- a/content/renderer/media/render_media_client.h +++ b/content/renderer/media/render_media_client.h
@@ -23,7 +23,6 @@ // MediaClient implementation. void GetSupportedKeySystems(media::GetSupportedKeySystemsCB cb) final; - bool IsKeySystemsUpdateNeeded() final; bool IsSupportedAudioType(const media::AudioType& type) final; bool IsSupportedVideoType(const media::VideoType& type) final; bool IsSupportedBitstreamAudioCodec(media::AudioCodec codec) final;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index e4db74c..47dfca7 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -852,26 +852,6 @@ return cloned_token; } -// Whether the navigation should be treated as a "loadDataWithBaseURL" -// navigation, where the "document URL" is set to the supplied base URL instead -// of the data URL set in CommonNavigationParams' `url` and the "unreachable -// URL" is set to the supplied history URL. If this returns false, the data: -// URL will still be loaded, but we won't try to use the supplied base URL and -// history URL. -// This should be kept in sync with NavigationRequest::IsLoadDataWithBaseURL(). -bool ShouldLoadDataWithBaseURL( - bool is_main_frame, - const blink::mojom::CommonNavigationParams& common_params, - const blink::mojom::CommitNavigationParams& commit_params) { - if (!is_main_frame) - return false; - // If no base URL is supplied, we should treat this as a normal data: URL - // navigation. - bool should_load_data_url = !common_params.base_url_for_data_url.is_empty(); - DCHECK(!should_load_data_url || common_params.url.SchemeIs(url::kDataScheme)); - return should_load_data_url; -} - // Creates a fully functional DocumentState in the case where we do not have // navigation parameters available. std::unique_ptr<DocumentState> BuildDocumentState() { @@ -890,8 +870,7 @@ mojom::NavigationClient::CommitNavigationCallback commit_callback, std::unique_ptr<NavigationClient> navigation_client, int request_id, - bool was_initiated_in_this_frame, - bool is_main_frame) { + bool was_initiated_in_this_frame) { std::unique_ptr<DocumentState> document_state(new DocumentState()); DCHECK(!common_params.navigation_start.is_null()); @@ -907,10 +886,9 @@ // If this is a loadDataWithBaseURL request, save the commit URL so that we // can send a DidCommit message with the URL that was originally sent by the // browser in CommonNavigationParams (See MaybeGetOverriddenURL()). - bool load_data = - ShouldLoadDataWithBaseURL(is_main_frame, common_params, commit_params); - document_state->set_was_load_data_with_base_url_request(load_data); - if (load_data) + document_state->set_was_load_data_with_base_url_request( + commit_params.is_load_data_with_base_url); + if (commit_params.is_load_data_with_base_url) document_state->set_data_url(common_params.url); document_state->set_navigation_state(NavigationState::Create( @@ -2680,7 +2658,7 @@ std::unique_ptr<DocumentState> document_state = BuildDocumentStateFromParams( *common_params, *commit_params, std::move(commit_callback), std::move(navigation_client_impl_), request_id, - was_initiated_in_this_frame, is_main_frame_); + was_initiated_in_this_frame); // Check if the navigation being committed originated as a client redirect. bool is_client_redirect = @@ -2716,8 +2694,7 @@ #endif if (should_handle_data_url_as_string || - ShouldLoadDataWithBaseURL(is_main_frame_, *common_params, - *commit_params)) { + commit_params->is_load_data_with_base_url) { std::string mime_type, charset, data; // `base_url` will be set to `base_url_for_data_url` from `common_params`, // unless it's empty (the `data_url_as_string` handling case), in which @@ -3059,7 +3036,7 @@ *common_params, *commit_params, std::move(callback), std::move(navigation_client_impl_), blink::WebResourceRequestSender::MakeRequestID(), - false /* was_initiated_in_this_frame */, is_main_frame_); + false /* was_initiated_in_this_frame */); DCHECK(!pending_loader_factories_); pending_loader_factories_ = std::move(new_loader_factories); @@ -5911,7 +5888,7 @@ // - For loadDataWithBaseURL() navigations, it will return the data: URL // used to commit, instead of the "base URL" used as the document URL in the // DocumentLoader. See comments in BuildDocumentStateFromParams() and -// ShouldLoadDataWithBaseURL() for more details. +// in navigation_params.mojom's `is_load_data_with_base_url` for more details. GURL RenderFrameImpl::GetLoadingUrl() const { WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
diff --git a/content/shell/browser/shell_download_manager_delegate.cc b/content/shell/browser/shell_download_manager_delegate.cc index 28567796..25f8bb3e 100644 --- a/content/shell/browser/shell_download_manager_delegate.cc +++ b/content/shell/browser/shell_download_manager_delegate.cc
@@ -79,7 +79,8 @@ download::DownloadItem::TARGET_DISPOSITION_OVERWRITE, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, download::DownloadItem::MixedContentStatus::UNKNOWN, - download->GetForcedFilePath(), absl::nullopt /*download_schedule*/, + download->GetForcedFilePath(), base::FilePath(), + absl::nullopt /*download_schedule*/, download::DOWNLOAD_INTERRUPT_REASON_NONE); return true; } @@ -146,7 +147,7 @@ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, download::DownloadItem::MixedContentStatus::UNKNOWN, suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")), - absl::nullopt /*download_schedule*/, + base::FilePath(), absl::nullopt /*download_schedule*/, download::DOWNLOAD_INTERRUPT_REASON_NONE); return; } @@ -194,12 +195,12 @@ NOTIMPLEMENTED(); #endif - std::move(callback).Run(result, - download::DownloadItem::TARGET_DISPOSITION_PROMPT, - download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, - download::DownloadItem::MixedContentStatus::UNKNOWN, - result, absl::nullopt /*download_schedule*/, - download::DOWNLOAD_INTERRUPT_REASON_NONE); + std::move(callback).Run( + result, download::DownloadItem::TARGET_DISPOSITION_PROMPT, + download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + download::DownloadItem::MixedContentStatus::UNKNOWN, result, + base::FilePath(), absl::nullopt /*download_schedule*/, + download::DOWNLOAD_INTERRUPT_REASON_NONE); } void ShellDownloadManagerDelegate::SetDownloadBehaviorForTesting(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 0fba6292..cc5047c 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -130,6 +130,8 @@ "../public/test/browser_test_utils.h", "../public/test/browsing_data_remover_test_util.cc", "../public/test/browsing_data_remover_test_util.h", + "../public/test/browsing_topics_test_util.cc", + "../public/test/browsing_topics_test_util.h", "../public/test/commit_message_delayer.cc", "../public/test/commit_message_delayer.h", "../public/test/content_mock_cert_verifier.cc", @@ -1288,6 +1290,7 @@ "../browser/service_worker/service_worker_clients_api_browsertest.cc", "../browser/service_worker/service_worker_fetch_dispatcher_browsertest.cc", "../browser/service_worker/service_worker_file_upload_browsertest.cc", + "../browser/service_worker/service_worker_internals_ui_browsertest.cc", "../browser/service_worker/service_worker_no_best_effort_tasks_browsertest.cc", "../browser/service_worker/service_worker_offline_capability_check_browsertest.cc", "../browser/service_worker/service_worker_process_browsertest.cc",
diff --git a/content/test/data/accessibility/html/math-expected-android-external.txt b/content/test/data/accessibility/html/math-expected-android-external.txt index f71b758..d9d4e0f 100644 --- a/content/test/data/accessibility/html/math-expected-android-external.txt +++ b/content/test/data/accessibility/html/math-expected-android-external.txt
@@ -1,3 +1,13 @@ WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"] ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"] -++++View text:"𝐴2 + 𝐵2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="math", roleDescription="math"] \ No newline at end of file +++++View actions:[AX_FOCUS] bundle:[chromeRole="math", roleDescription="math"] +++++++View actions:[AX_FOCUS] bundle:[chromeRole="mathMLRow"] +++++++++View actions:[AX_FOCUS] bundle:[chromeRole="mathMLSup"] +++++++++++View text:"𝐴" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLIdentifier"] +++++++++++View text:"2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLNumber"] +++++++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] +++++++++View text:"+" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLOperator"] +++++++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] +++++++++View actions:[AX_FOCUS] bundle:[chromeRole="mathMLSup"] +++++++++++View text:"𝐵" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLIdentifier"] +++++++++++View text:"2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLNumber"] \ No newline at end of file
diff --git a/content/test/data/back_forward_cache/page_with_dedicated_worker_and_importscripts.html b/content/test/data/back_forward_cache/page_with_dedicated_worker_and_importscripts.html new file mode 100644 index 0000000..8fa30a4 --- /dev/null +++ b/content/test/data/back_forward_cache/page_with_dedicated_worker_and_importscripts.html
@@ -0,0 +1,12 @@ +<html> +<head></head> +<script> +const worker = new Worker('/back_forward_cache/worker_with_importscripts.js'); +window.receivedMessagePromise = new Promise(resolve => { + worker.addEventListener('message', (msg) => { + resolve(msg.data); + }); +}); + +</script> +</html>
diff --git a/content/test/data/back_forward_cache/worker_with_importscripts.js b/content/test/data/back_forward_cache/worker_with_importscripts.js new file mode 100644 index 0000000..58bc6ba --- /dev/null +++ b/content/test/data/back_forward_cache/worker_with_importscripts.js
@@ -0,0 +1,4 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +self.importScripts('worker.js');
diff --git a/content/test/data/browsing_topics/v0.init_too_old.sql b/content/test/data/browsing_topics/v0.init_too_old.sql index 6229824..1393ca290 100644 --- a/content/test/data/browsing_topics/v0.init_too_old.sql +++ b/content/test/data/browsing_topics/v0.init_too_old.sql
@@ -4,9 +4,9 @@ CREATE TABLE browsing_topics_api_usages ( hashed_context_domain INTEGER NOT NULL, -hashed_top_host INTEGER NOT NULL, +hashed_main_frame_host INTEGER NOT NULL, last_usage_time INTEGER NOT NULL, -PRIMARY KEY (hashed_context_domain, hashed_top_host)); +PRIMARY KEY (hashed_context_domain, hashed_main_frame_host)); CREATE INDEX last_usage_time_idx ON browsing_topics_api_usages(last_usage_time);
diff --git a/content/test/data/browsing_topics/v1.init_too_new.sql b/content/test/data/browsing_topics/v1.init_too_new.sql index 825043b..2284c94 100644 --- a/content/test/data/browsing_topics/v1.init_too_new.sql +++ b/content/test/data/browsing_topics/v1.init_too_new.sql
@@ -4,9 +4,9 @@ CREATE TABLE browsing_topics_api_usages ( hashed_context_domain INTEGER NOT NULL, -hashed_top_host INTEGER NOT NULL, +hashed_main_frame_host INTEGER NOT NULL, last_usage_time INTEGER NOT NULL, -PRIMARY KEY (hashed_context_domain, hashed_top_host)); +PRIMARY KEY (hashed_context_domain, hashed_main_frame_host)); CREATE INDEX last_usage_time_idx ON browsing_topics_api_usages(last_usage_time);
diff --git a/content/test/data/browsing_topics/v1.sql b/content/test/data/browsing_topics/v1.sql index 10d63732..9d5ce992c 100644 --- a/content/test/data/browsing_topics/v1.sql +++ b/content/test/data/browsing_topics/v1.sql
@@ -4,9 +4,9 @@ CREATE TABLE browsing_topics_api_usages ( hashed_context_domain INTEGER NOT NULL, -hashed_top_host INTEGER NOT NULL, +hashed_main_frame_host INTEGER NOT NULL, last_usage_time INTEGER NOT NULL, -PRIMARY KEY (hashed_context_domain, hashed_top_host)); +PRIMARY KEY (hashed_context_domain, hashed_main_frame_host)); CREATE INDEX last_usage_time_idx ON browsing_topics_api_usages(last_usage_time);
diff --git a/content/test/data/gpu/vc/webgpu_video.js b/content/test/data/gpu/vc/webgpu_video.js index 06d2ecb..1199d3cb 100644 --- a/content/test/data/gpu/vc/webgpu_video.js +++ b/content/test/data/gpu/vc/webgpu_video.js
@@ -569,7 +569,11 @@ passEncoder.endPass(); device.queue.submit([commandEncoder.finish()]); - window.requestAnimationFrame(oneFrame); + // TODO(crbug.com/1289482): Workaround for backpressure mechanism + // not working properly. + device.queue.onSubmittedWorkDone().then(() => { + window.requestAnimationFrame(oneFrame); + }); }); }; @@ -634,7 +638,11 @@ functionDuration, 'ms, longer than 33.3 ms (1sec/30fps)'); } - window.requestAnimationFrame(oneFrameWithImportTextureApi); + // TODO(crbug.com/1289482): Workaround for backpressure mechanism + // not working properly. + device.queue.onSubmittedWorkDone().then(() => { + window.requestAnimationFrame(oneFrameWithImportTextureApi); + }); }; if (useImportTextureApi) {
diff --git a/content/test/fuzzer/BUILD.gn b/content/test/fuzzer/BUILD.gn index 7ceed2a..ef935a2 100644 --- a/content/test/fuzzer/BUILD.gn +++ b/content/test/fuzzer/BUILD.gn
@@ -134,6 +134,28 @@ environment_variables = [ "AFL_DRIVER_DONT_DEFER=1" ] } +if (enable_mojom_fuzzer) { + source_set("mojolpm_fuzzer_support") { + testonly = true + + sources = [ + "mojolpm_fuzzer_support.cc", + "mojolpm_fuzzer_support.h", + ] + + deps = [ "//mojo/core/embedder:embedder" ] + + public_deps = [ + "//base/test:test_support", + "//content/browser:for_content_tests", + "//content/test:test_support", + "//services/network:test_support", + "//storage/browser:test_support", + "//third_party/icu:icudata", + ] + } +} + mojolpm_fuzzer_test("code_cache_host_mojolpm_fuzzer") { sources = [ "code_cache_host_mojolpm_fuzzer.cc" ] @@ -141,13 +163,9 @@ testcase_proto_kind = "content.fuzzing.code_cache_host.proto.Testcase" deps = [ - "//base/test:test_support", + ":mojolpm_fuzzer_support", "//content/browser:for_content_tests", "//content/public/browser:browser_sources", - "//content/test:test_support", - "//services/network:test_support", - "//storage/browser:test_support", - "//third_party/icu:icudata", ] proto_deps = [ "//third_party/blink/public/mojom:mojom_platform_mojolpm" ] @@ -164,15 +182,9 @@ proto_source = "file_system_manager_mojolpm_fuzzer.proto" deps = [ - "//base/test:test_support", - "//content/browser:for_content_tests", - "//content/public/browser:browser_sources", - "//content/test:test_support", - "//services/network:test_support", - "//storage/browser:test_support", + ":mojolpm_fuzzer_support", "//testing/libfuzzer/proto:url_proto_converter", "//third_party/blink/public/common:storage_key_proto_converter", - "//third_party/icu:icudata", ] proto_deps = [ @@ -187,11 +199,9 @@ proto_source = "audio_context_manager_mojolpm_fuzzer.proto" deps = [ - "//base/test:test_support", + ":mojolpm_fuzzer_support", "//content/browser:for_content_tests", - "//content/test:test_support", - "//services/network:test_support", - "//storage/browser:test_support", + "//content/public/browser:browser_sources", ] proto_deps = [ "//third_party/blink/public/mojom:mojom_platform_mojolpm" ] @@ -272,13 +282,10 @@ proto_source = "media_stream_dispatcher_host_mojolpm_fuzzer.proto" deps = [ - "//base/test:test_support", + ":mojolpm_fuzzer_support", "//content/browser:browser", "//content/browser:for_content_tests", "//content/public/browser:browser_sources", - "//content/test:test_support", - "//services/network:test_support", - "//storage/browser:test_support", ] proto_deps = [ "//third_party/blink/public/mojom:mojom_platform_mojolpm" ] @@ -293,18 +300,14 @@ proto_source = "video_capture_host_mojolpm_fuzzer.proto" deps = [ - "//base/test:test_support", + ":mojolpm_fuzzer_support", "//content/browser:for_content_tests", - "//content/public/browser:browser", + "//content/public/browser:browser_sources", "//content/test:content_unittests", - "//content/test:test_support", "//media:test_support", "//media/capture:capture_lib", "//media/capture:test_support", - "//services/network:test_support", - "//storage/browser:test_support", "//third_party/blink/public/common:common", - "//third_party/icu:icudata", ] proto_deps = [ "//media/capture/mojom:video_capture_mojolpm" ] @@ -318,13 +321,8 @@ deps = [ ":controller_presentation_service_delegate_for_fuzzing", - "//base/test:test_support", + ":mojolpm_fuzzer_support", "//content/browser:for_content_tests", - "//content/public/browser:browser", - "//content/test:content_unittests", - "//content/test:test_support", - "//services/network:test_support", - "//storage/browser:test_support", "//ui/events/devices:devices", ]
diff --git a/content/test/fuzzer/audio_context_manager_mojolpm_fuzzer.cc b/content/test/fuzzer/audio_context_manager_mojolpm_fuzzer.cc index ba1597d..5322cfd6 100644 --- a/content/test/fuzzer/audio_context_manager_mojolpm_fuzzer.cc +++ b/content/test/fuzzer/audio_context_manager_mojolpm_fuzzer.cc
@@ -6,38 +6,23 @@ #include <cstddef> #include <utility> -#include "base/at_exit.h" -#include "base/base_switches.h" #include "base/bind.h" #include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/i18n/icu_util.h" #include "base/no_destructor.h" -#include "base/run_loop.h" #include "base/test/simple_test_tick_clock.h" -#include "base/test/test_switches.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread.h" #include "content/browser/media/webaudio/audio_context_manager_impl.h" // [nogncheck] -#include "content/browser/network_service_instance_impl.h" // nogncheck #include "content/browser/site_instance_impl.h" // nogncheck #include "content/public/browser/audio_service.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" #include "content/public/browser/site_instance.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" -#include "content/public/test/browser_task_environment.h" #include "content/public/test/mock_navigation_handle.h" #include "content/public/test/test_browser_context.h" -#include "content/public/test/test_content_client_initializer.h" #include "content/public/test/test_renderer_host.h" #include "content/test/fuzzer/audio_context_manager_mojolpm_fuzzer.pb.h" +#include "content/test/fuzzer/mojolpm_fuzzer_support.h" #include "content/test/test_render_frame_host.h" #include "content/test/test_web_contents.h" -#include "mojo/core/embedder/embedder.h" -#include "mojo/public/cpp/bindings/lib/validation_errors.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -48,32 +33,11 @@ #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h" #include "url/origin.h" -const char* const cmdline[] = {"audio_context_manager_mojolpm_fuzzer", nullptr}; -class ContentFuzzerEnvironment { - public: - ContentFuzzerEnvironment() - : fuzzer_thread_((base::CommandLine::Init(1, cmdline), "fuzzer_thread")) { - TestTimeouts::Initialize(); - logging::SetMinLogLevel(logging::LOG_FATAL); - mojo::core::Init(); - base::i18n::InitializeICU(); - fuzzer_thread_.StartAndWaitForTesting(); +const char* kCmdline[] = {"audio_context_manager_mojolpm_fuzzer", nullptr}; - content::ForceCreateNetworkServiceDirectlyForTesting(); - } - - scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() { - return fuzzer_thread_.task_runner(); - } - - private: - base::AtExitManager at_exit_manager_; - base::Thread fuzzer_thread_; - content::TestContentClientInitializer content_client_initializer_; -}; - -ContentFuzzerEnvironment& GetEnvironment() { - static base::NoDestructor<ContentFuzzerEnvironment> environment; +content::mojolpm::FuzzerEnvironment& GetEnvironment() { + static base::NoDestructor<content::mojolpm::FuzzerEnvironment> environment( + 1, kCmdline); return *environment; }
diff --git a/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc b/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc index 7a07f737..0e3f21d 100644 --- a/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc +++ b/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc
@@ -6,32 +6,22 @@ #include <utility> -#include "base/at_exit.h" -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/files/scoped_temp_dir.h" #include "base/i18n/icu_util.h" #include "base/no_destructor.h" #include "base/task/sequenced_task_runner.h" -#include "base/test/test_switches.h" -#include "base/test/test_timeouts.h" #include "base/threading/platform_thread.h" #include "base/threading/thread.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" // [nogncheck] #include "content/browser/cache_storage/cache_storage_control_wrapper.h" // [nogncheck] #include "content/browser/code_cache/generated_code_cache_context.h" // [nogncheck] -#include "content/browser/network_service_instance_impl.h" // [nogncheck] #include "content/browser/renderer_host/code_cache_host_impl.h" // [nogncheck] #include "content/browser/storage_partition_impl.h" // [nogncheck] #include "content/browser/storage_partition_impl_map.h" // [nogncheck] #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" -#include "content/public/test/browser_task_environment.h" #include "content/public/test/test_browser_context.h" -#include "content/public/test/test_content_client_initializer.h" #include "content/test/fuzzer/code_cache_host_mojolpm_fuzzer.pb.h" -#include "mojo/core/embedder/embedder.h" -#include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "content/test/fuzzer/mojolpm_fuzzer_support.h" #include "storage/browser/quota/quota_manager.h" #include "storage/browser/quota/special_storage_policy.h" #include "storage/browser/test/mock_special_storage_policy.h" @@ -41,59 +31,12 @@ using url::Origin; -const char* const kCmdline[] = {"code_cache_host_mojolpm_fuzzer", nullptr}; +const char* kCmdline[] = {"code_cache_host_mojolpm_fuzzer", nullptr}; -// Global environment needed to run the interface being tested. -// -// This will be created once, before fuzzing starts, and will be shared between -// all testcases. It is created on the main thread. -// -// At a minimum, we should always be able to set up the command line, i18n and -// mojo, and create the thread on which the fuzzer will be run. We want to avoid -// (as much as is reasonable) any state being preserved between testcases. -// -// For CodeCacheHost, we can also safely re-use a single BrowserTaskEnvironment -// and the TestContentClientInitializer between testcases. We try to create an -// environment that matches the real browser process as much as possible, so we -// use real platform threads in the task environment. -class ContentFuzzerEnvironment { - public: - ContentFuzzerEnvironment() - : fuzzer_thread_("fuzzer_thread"), - task_environment_( - (base::CommandLine::Init(1, kCmdline), - TestTimeouts::Initialize(), - base::test::TaskEnvironment::MainThreadType::DEFAULT), - base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC, - base::test::TaskEnvironment::ThreadingMode::MULTIPLE_THREADS, - content::BrowserTaskEnvironment::REAL_IO_THREAD) { - logging::SetMinLogLevel(logging::LOG_FATAL); - mojo::core::Init(); - base::i18n::InitializeICU(); - - content::ForceCreateNetworkServiceDirectlyForTesting(); - content::StoragePartitionImpl::ForceInProcessStorageServiceForTesting(); - - fuzzer_thread_.StartAndWaitForTesting(); - } - - scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() { - return fuzzer_thread_.task_runner(); - } - - private: - base::AtExitManager at_exit_manager_; - base::Thread fuzzer_thread_; - content::BrowserTaskEnvironment task_environment_; - content::TestContentClientInitializer content_client_initializer_; - mojo::internal::ScopedSuppressValidationErrorLoggingForTests - validation_error_suppressor_; - mojo::internal::SerializationWarningObserverForTesting - serialization_error_suppressor_; -}; - -ContentFuzzerEnvironment& GetEnvironment() { - static base::NoDestructor<ContentFuzzerEnvironment> environment; +content::mojolpm::FuzzerEnvironment& GetEnvironment() { + static base::NoDestructor< + content::mojolpm::FuzzerEnvironmentWithTaskEnvironment> + environment(1, kCmdline); return *environment; }
diff --git a/content/test/fuzzer/file_system_manager_mojolpm_fuzzer.cc b/content/test/fuzzer/file_system_manager_mojolpm_fuzzer.cc index deccede..3338e86 100644 --- a/content/test/fuzzer/file_system_manager_mojolpm_fuzzer.cc +++ b/content/test/fuzzer/file_system_manager_mojolpm_fuzzer.cc
@@ -24,6 +24,7 @@ #include "content/public/test/test_browser_context.h" #include "content/public/test/test_content_client_initializer.h" #include "content/test/fuzzer/file_system_manager_mojolpm_fuzzer.pb.h" +#include "content/test/fuzzer/mojolpm_fuzzer_support.h" #include "mojo/core/embedder/embedder.h" #include "storage/browser/quota/quota_manager_proxy.h" #include "storage/browser/quota/special_storage_policy.h" @@ -43,52 +44,11 @@ namespace content { const size_t kNumRenderers = 2; -const char* cmdline[] = {"file_system_manager_mojolpm_fuzzer", nullptr}; +const char* kCmdline[] = {"file_system_manager_mojolpm_fuzzer", nullptr}; -// Global environment needed to run the interface being tested. -// -// This will be created once, before fuzzing starts, and will be shared between -// all testcases. It is created on the main thread. -// -// At a minimum, we should always be able to set up the command line, i18n and -// mojo, and create the thread on which the fuzzer will be run. We want to avoid -// (as much as is reasonable) any state being preserved between testcases. -// -// For FileSystemManager, we can also safely re-use a single -// BrowserTaskEnvironment and the TestContentClientInitializer between -// testcases. We try to create an environment that matches the real browser -// process as much as possible, so we use real platform threads in the task -// environment. -class ContentFuzzerEnvironment { - public: - ContentFuzzerEnvironment() - : fuzzer_thread_("fuzzer_thread"), - task_environment_( - (base::CommandLine::Init(1, cmdline), - TestTimeouts::Initialize(), - base::test::TaskEnvironment::MainThreadType::DEFAULT), - base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC, - base::test::TaskEnvironment::ThreadingMode::MULTIPLE_THREADS, - content::BrowserTaskEnvironment::REAL_IO_THREAD) { - logging::SetMinLogLevel(logging::LOG_FATAL); - mojo::core::Init(); - base::i18n::InitializeICU(); - fuzzer_thread_.StartAndWaitForTesting(); - } - - scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() { - return fuzzer_thread_.task_runner(); - } - - private: - base::AtExitManager at_exit_manager_; - base::Thread fuzzer_thread_; - content::BrowserTaskEnvironment task_environment_; - content::TestContentClientInitializer content_client_initializer_; -}; - -ContentFuzzerEnvironment& GetEnvironment() { - static base::NoDestructor<ContentFuzzerEnvironment> environment; +mojolpm::FuzzerEnvironment& GetEnvironment() { + static base::NoDestructor<mojolpm::FuzzerEnvironmentWithTaskEnvironment> + environment(1, kCmdline); return *environment; } @@ -314,12 +274,12 @@ break; case Action::kFileSystemManagerRemoteAction: - mojolpm::HandleRemoteAction( + ::mojolpm::HandleRemoteAction( action.file_system_manager_remote_action()); break; case Action::kFileSystemCancellableOperationRemoteAction: - mojolpm::HandleRemoteAction( + ::mojolpm::HandleRemoteAction( action.file_system_cancellable_operation_remote_action()); break; @@ -363,7 +323,7 @@ run_loop.QuitClosure()); run_loop.Run(); - mojolpm::GetContext()->AddInstance(id, std::move(remote)); + ::mojolpm::GetContext()->AddInstance(id, std::move(remote)); } } // namespace content @@ -391,7 +351,7 @@ testcase->SetUp(); - mojolpm::GetContext()->StartTestcase(); + ::mojolpm::GetContext()->StartTestcase(); base::RunLoop fuzzer_run_loop(base::RunLoop::Type::kNestableTasksAllowed); content::GetFuzzerTaskRunner()->PostTask( @@ -399,7 +359,7 @@ fuzzer_run_loop.QuitClosure())); fuzzer_run_loop.Run(); - mojolpm::GetContext()->EndTestcase(); + ::mojolpm::GetContext()->EndTestcase(); testcase->TearDown(); }
diff --git a/content/test/fuzzer/media_stream_dispatcher_host_mojolpm_fuzzer.cc b/content/test/fuzzer/media_stream_dispatcher_host_mojolpm_fuzzer.cc index 055f1d04..9339854 100644 --- a/content/test/fuzzer/media_stream_dispatcher_host_mojolpm_fuzzer.cc +++ b/content/test/fuzzer/media_stream_dispatcher_host_mojolpm_fuzzer.cc
@@ -5,35 +5,22 @@ #include <stdint.h> #include <utility> -#include "base/at_exit.h" -#include "base/base_switches.h" #include "base/bind.h" #include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/i18n/icu_util.h" #include "base/no_destructor.h" #include "base/run_loop.h" #include "base/test/simple_test_tick_clock.h" -#include "base/test/test_switches.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread.h" -#include "content/browser/network_service_instance_impl.h" #include "content/public/browser/audio_service.h" -#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/site_instance.h" #include "content/public/common/content_switches.h" -#include "content/public/test/browser_task_environment.h" #include "content/public/test/mock_navigation_handle.h" #include "content/public/test/test_browser_context.h" -#include "content/public/test/test_content_client_initializer.h" +#include "content/test/fuzzer/mojolpm_fuzzer_support.h" #include "content/test/test_render_frame_host.h" #include "content/test/test_web_contents.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_system.h" -#include "mojo/core/embedder/embedder.h" -#include "mojo/public/cpp/bindings/lib/validation_errors.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -46,39 +33,17 @@ #include "content/test/fuzzer/media_stream_dispatcher_host_mojolpm_fuzzer.pb.h" -#include "mojo/core/embedder/embedder.h" #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-mojolpm.h" #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h" #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h" -const char* const cmdline[] = {"media_stream_dispatcher_host_mojolpm_fuzzer", - nullptr}; -class ContentFuzzerEnvironment { - public: - ContentFuzzerEnvironment() - : fuzzer_thread_((base::CommandLine::Init(1, cmdline), "fuzzer_thread")) { - TestTimeouts::Initialize(); - logging::SetMinLogLevel(logging::LOG_FATAL); - mojo::core::Init(); - base::i18n::InitializeICU(); - fuzzer_thread_.StartAndWaitForTesting(); +const char* kCmdline[] = {"media_stream_dispatcher_host_mojolpm_fuzzer", + nullptr}; - content::ForceCreateNetworkServiceDirectlyForTesting(); - } - - scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() { - return fuzzer_thread_.task_runner(); - } - - private: - base::AtExitManager at_exit_manager_; - base::Thread fuzzer_thread_; - content::TestContentClientInitializer content_client_initializer_; -}; - -ContentFuzzerEnvironment& GetEnvironment() { - static base::NoDestructor<ContentFuzzerEnvironment> environment; +content::mojolpm::FuzzerEnvironment& GetEnvironment() { + static base::NoDestructor<content::mojolpm::FuzzerEnvironment> environment( + 1, kCmdline); return *environment; }
diff --git a/content/test/fuzzer/mojolpm_fuzzer_support.cc b/content/test/fuzzer/mojolpm_fuzzer_support.cc new file mode 100644 index 0000000..fcdc4ea --- /dev/null +++ b/content/test/fuzzer/mojolpm_fuzzer_support.cc
@@ -0,0 +1,45 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/test/fuzzer/mojolpm_fuzzer_support.h" + +#include "base/command_line.h" +#include "base/i18n/icu_util.h" +#include "base/test/test_timeouts.h" +#include "content/browser/network_service_instance_impl.h" // [nogncheck] +#include "content/browser/storage_partition_impl.h" // [nogncheck] +#include "content/browser/storage_partition_impl_map.h" // [nogncheck] + +namespace content { +namespace mojolpm { +FuzzerEnvironment::FuzzerEnvironment(int argc, const char** argv) + : command_line_initialized_(base::CommandLine::Init(argc, argv)), + fuzzer_thread_("fuzzer_thread") { + TestTimeouts::Initialize(); + + logging::SetMinLogLevel(logging::LOG_FATAL); + mojo::core::Init(); + base::i18n::InitializeICU(); + + ForceCreateNetworkServiceDirectlyForTesting(); + StoragePartitionImpl::ForceInProcessStorageServiceForTesting(); + + fuzzer_thread_.StartAndWaitForTesting(); +} + +FuzzerEnvironment::~FuzzerEnvironment() {} + +FuzzerEnvironmentWithTaskEnvironment::FuzzerEnvironmentWithTaskEnvironment( + int argc, + const char** argv) + : FuzzerEnvironment(argc, argv), + task_environment_( + base::test::TaskEnvironment::MainThreadType::DEFAULT, + base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC, + base::test::TaskEnvironment::ThreadingMode::MULTIPLE_THREADS, + BrowserTaskEnvironment::REAL_IO_THREAD) {} + +FuzzerEnvironmentWithTaskEnvironment::~FuzzerEnvironmentWithTaskEnvironment() {} +} // namespace mojolpm +} // namespace content \ No newline at end of file
diff --git a/content/test/fuzzer/mojolpm_fuzzer_support.h b/content/test/fuzzer/mojolpm_fuzzer_support.h new file mode 100644 index 0000000..9e55897 --- /dev/null +++ b/content/test/fuzzer/mojolpm_fuzzer_support.h
@@ -0,0 +1,67 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_TEST_FUZZER_MOJOLPM_FUZZER_SUPPORT_H_ +#define CONTENT_TEST_FUZZER_MOJOLPM_FUZZER_SUPPORT_H_ + +#include "base/at_exit.h" +#include "base/threading/thread.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/test_content_client_initializer.h" +#include "mojo/core/embedder/embedder.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" + +namespace content { +namespace mojolpm { +// Global environment needed to run the interface being tested. +// +// This will be created once, before fuzzing starts, and will be shared between +// all testcases. It is created on the main thread. +// +// This file contains several base types that divide MojoLPM fuzzer +// implementations within //content into two kinds - those that can reuse the +// BrowserTaskEnvironment between multiple testcases, and those that require a +// new BrowserTaskEnvironment for every testcase (usually this happens when +// reusing existing test harness code). + +// At a minimum, we should always be able to set up the command line, i18n and +// mojo, and create the thread on which the fuzzer will be run. We want to avoid +// (as much as is reasonable) any state being preserved between testcases. +class FuzzerEnvironment { + public: + FuzzerEnvironment(int argc, const char** argv); + virtual ~FuzzerEnvironment(); + + inline scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() { + return fuzzer_thread_.task_runner(); + } + + private: + bool command_line_initialized_; + base::AtExitManager at_exit_manager_; + base::Thread fuzzer_thread_; + + mojo::internal::ScopedSuppressValidationErrorLoggingForTests + validation_error_suppressor_; + mojo::internal::SerializationWarningObserverForTesting + serialization_error_suppressor_; + + TestContentClientInitializer content_client_initializer_; +}; + +// If we can also safely re-use a single BrowserTaskEnvironment and the +// TestContentClientInitializer between testcases, then prefer to use this case +// class instead. +class FuzzerEnvironmentWithTaskEnvironment : public FuzzerEnvironment { + public: + FuzzerEnvironmentWithTaskEnvironment(int argc, const char** argv); + ~FuzzerEnvironmentWithTaskEnvironment() override; + + private: + BrowserTaskEnvironment task_environment_; +}; +} // namespace mojolpm +} // namespace content + +#endif // CONTENT_TEST_FUZZER_MOJOLPM_FUZZER_SUPPORT_H_ \ No newline at end of file
diff --git a/content/test/fuzzer/presentation_service_mojolpm_fuzzer.cc b/content/test/fuzzer/presentation_service_mojolpm_fuzzer.cc index fb2befb..5f991bb 100644 --- a/content/test/fuzzer/presentation_service_mojolpm_fuzzer.cc +++ b/content/test/fuzzer/presentation_service_mojolpm_fuzzer.cc
@@ -7,40 +7,28 @@ #include <string> #include <utility> -#include "base/at_exit.h" -#include "base/base_switches.h" #include "base/bind.h" #include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/i18n/icu_util.h" #include "base/no_destructor.h" #include "base/run_loop.h" -#include "base/test/test_switches.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" #include "base/threading/thread.h" -#include "content/browser/gpu/gpu_data_manager_impl.h" // nogncheck -#include "content/browser/network_service_instance_impl.h" // nogncheck +#include "content/browser/gpu/gpu_data_manager_impl.h" // nogncheck #include "content/browser/presentation/presentation_service_impl.h" // nogncheck #include "content/browser/presentation/presentation_test_utils.h" #include "content/browser/site_instance_impl.h" // nogncheck -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" #include "content/public/browser/presentation_request.h" #include "content/public/browser/presentation_service_delegate.h" #include "content/public/browser/site_instance.h" -#include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/mock_navigation_handle.h" #include "content/public/test/test_browser_context.h" -#include "content/public/test/test_content_client_initializer.h" #include "content/public/test/test_renderer_host.h" #include "content/test/fuzzer/controller_presentation_service_delegate_for_fuzzing.h" +#include "content/test/fuzzer/mojolpm_fuzzer_support.h" #include "content/test/fuzzer/presentation_service_mojolpm_fuzzer.pb.h" #include "content/test/test_render_frame_host.h" #include "content/test/test_web_contents.h" -#include "mojo/core/embedder/embedder.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -50,44 +38,11 @@ #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h" #include "ui/events/devices/device_data_manager.h" -const char* cmdline[] = {"presentation_service_mojolpm_fuzzer", nullptr}; +const char* kCmdline[] = {"presentation_service_mojolpm_fuzzer", nullptr}; -// Global environment needed to run the interface being tested. -// -// This will be created once, before fuzzing starts, and will be shared between -// all testcases. It is created on the main thread. -// -// At a minimum, we should always be able to set up the command line, i18n and -// mojo, and create the thread on which the fuzzer will be run. We want to avoid -// (as much as is reasonable) any state being preserved between testcases. -// -// We try to create an environment that matches the real browser process as -// much as possible, so we use real platform threads in the task environment. -class ContentFuzzerEnvironment { - public: - ContentFuzzerEnvironment() - : fuzzer_thread_((base::CommandLine::Init(1, cmdline), "fuzzer_thread")) { - TestTimeouts::Initialize(); - logging::SetMinLogLevel(logging::LOG_FATAL); - mojo::core::Init(); - base::i18n::InitializeICU(); - fuzzer_thread_.StartAndWaitForTesting(); - - content::ForceCreateNetworkServiceDirectlyForTesting(); - } - - scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() { - return fuzzer_thread_.task_runner(); - } - - private: - base::AtExitManager at_exit_manager_; - base::Thread fuzzer_thread_; - content::TestContentClientInitializer content_client_initializer_; -}; - -ContentFuzzerEnvironment& GetEnvironment() { - static base::NoDestructor<ContentFuzzerEnvironment> environment; +content::mojolpm::FuzzerEnvironment& GetEnvironment() { + static base::NoDestructor<content::mojolpm::FuzzerEnvironment> environment( + 1, kCmdline); return *environment; }
diff --git a/content/test/fuzzer/video_capture_host_mojolpm_fuzzer.cc b/content/test/fuzzer/video_capture_host_mojolpm_fuzzer.cc index 61d1bea..3d02c82 100644 --- a/content/test/fuzzer/video_capture_host_mojolpm_fuzzer.cc +++ b/content/test/fuzzer/video_capture_host_mojolpm_fuzzer.cc
@@ -7,15 +7,11 @@ #include <string> #include <utility> -#include "base/at_exit.h" #include "base/bind.h" #include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/i18n/icu_util.h" #include "base/no_destructor.h" #include "base/run_loop.h" #include "base/task/task_traits.h" -#include "base/test/test_timeouts.h" #include "base/threading/thread.h" #include "content/browser/renderer_host/media/fake_video_capture_provider.h" #include "content/browser/renderer_host/media/in_process_video_capture_provider.h" // nogncheck @@ -27,7 +23,7 @@ #include "content/public/browser/browser_thread.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/test_browser_context.h" -#include "content/public/test/test_content_client_initializer.h" +#include "content/test/fuzzer/mojolpm_fuzzer_support.h" #include "content/test/fuzzer/video_capture_host_mojolpm_fuzzer.pb.h" #include "content/test/test_content_browser_client.h" #include "media/audio/audio_system_impl.h" @@ -40,11 +36,10 @@ #include "media/capture/video/linux/video_capture_device_factory_linux.h" #include "media/capture/video/video_capture_system_impl.h" #include "media/capture/video_capture_types.h" -#include "mojo/core/embedder/embedder.h" #include "third_party/blink/public/common/mediastream/media_devices.h" #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h" -const char* cmdline[] = {"video_capture_host_mojolpm_fuzzer", nullptr}; +const char* kCmdline[] = {"video_capture_host_mojolpm_fuzzer", nullptr}; // Describe all the devices (as descriptors). const uint32_t kNumDeviceDescriptors = 4; @@ -59,40 +54,9 @@ using blink::mojom::MediaDeviceType; -// Global environment needed to run the interface being tested. -// -// This will be created once, before fuzzing starts, and will be shared between -// all testcases. It is created on the main thread. -// -// At a minimum, we should always be able to set up the command line, i18n and -// mojo, and create the thread on which the fuzzer will be run. We want to avoid -// (as much as is reasonable) any state being preserved between testcases. -// -// We try to create an environment that matches the real browser process as much -// as possible, so we use real platform threads in the task environment. -class ContentFuzzerEnvironment { - public: - ContentFuzzerEnvironment() - : fuzzer_thread_((base::CommandLine::Init(1, cmdline), "fuzzer_thread")) { - TestTimeouts::Initialize(); - logging::SetMinLogLevel(logging::LOG_FATAL); - mojo::core::Init(); - base::i18n::InitializeICU(); - - fuzzer_thread_.StartAndWaitForTesting(); - } - - scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() { - return fuzzer_thread_.task_runner(); - } - - base::AtExitManager at_exit_manager_; - base::Thread fuzzer_thread_; - content::TestContentClientInitializer content_client_initializer_; -}; - -ContentFuzzerEnvironment& GetEnvironment() { - static base::NoDestructor<ContentFuzzerEnvironment> environment; +content::mojolpm::FuzzerEnvironment& GetEnvironment() { + static base::NoDestructor<content::mojolpm::FuzzerEnvironment> environment( + 1, kCmdline); return *environment; }
diff --git a/content/test/gpu/flake_suppressor/expectations.py b/content/test/gpu/flake_suppressor/expectations.py index d50e21f8..2eb2115 100644 --- a/content/test/gpu/flake_suppressor/expectations.py +++ b/content/test/gpu/flake_suppressor/expectations.py
@@ -448,7 +448,7 @@ files.append(line.split()[-1]) origin_file_contents = {} - for f in files: + for f in (f for f in files if f.endswith('.txt')): origin_filepath = posixpath.join(origin_dir, f) origin_filepath_url = posixpath.join(GITILES_URL, origin_filepath) + TEXT_FORMAT_ARG @@ -467,7 +467,8 @@ that are available from the local checkout. """ local_file_contents = {} - for f in os.listdir(ABSOLUTE_EXPECTATION_FILE_DIRECTORY): + for f in (f for f in os.listdir(ABSOLUTE_EXPECTATION_FILE_DIRECTORY) + if f.endswith('.txt')): with open(os.path.join(ABSOLUTE_EXPECTATION_FILE_DIRECTORY, f)) as infile: local_file_contents[f] = infile.read() return local_file_contents
diff --git a/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py b/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py index db34772..429bc5e 100644 --- a/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py +++ b/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py
@@ -127,6 +127,7 @@ def TearDownProcess(cls): super(SkiaGoldIntegrationTestBase, cls).TearDownProcess() shutil.rmtree(cls._skia_gold_temp_dir) + cls._skia_gold_session_manager = None @classmethod def AddCommandlineArgs(cls, parser):
diff --git a/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt index 26334c2..d1d193e 100644 --- a/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
@@ -79,6 +79,7 @@ crbug.com/1206463 [ android android-nexus-5x ] MediaPipe_mediapipe_face_mesh_test [ Failure ] crbug.com/1206463 [ android android-pixel-4 ] MediaPipe_mediapipe_face_mesh_test [ Failure ] crbug.com/1206463 [ chromeos chromeos-board-kevin ] MediaPipe_mediapipe_face_mesh_test [ Failure ] +crbug.com/1206463 [ fuchsia fuchsia-board-qemu-x64 ] MediaPipe_mediapipe_face_mesh_test [ Failure ] crbug.com/1206463 [ linux display-server-wayland intel ] MediaPipe_mediapipe_face_mesh_test [ Failure ] crbug.com/1206463 [ linux display-server-x intel ] MediaPipe_mediapipe_face_mesh_test [ Failure ] crbug.com/1206463 [ linux display-server-x nvidia ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt index c4e3e4e..d7aaf8c 100644 --- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -231,6 +231,9 @@ # Fails on Nexus 5 crbug.com/883500 [ android android-nexus-5 ] Pixel_BackgroundImage [ Failure ] +# Fails on Nexus 5X. +crbug.com/1307317 [ android android-chromium android-nexus-5x ] Pixel_PrecisionRoundedCorner [ Failure ] + # Flakes on Nexus 5X. crbug.com/883500 [ android android-chromium android-nexus-5x ] Pixel_BackgroundImage [ RetryOnFailure ] crbug.com/1065514 [ android android-chromium android-nexus-5x ] Pixel_WebGLReadPixelsTabSwitch [ RetryOnFailure ] @@ -280,12 +283,14 @@ # Flaky hang. crbug.com/1218288 [ fuchsia-board-astro ] Pixel_PrecisionRoundedCorner [ Failure ] -crbug.com/1218288 [ fuchsia-board-sherlock ] Pixel_PrecisionRoundedCorner [ Failure ] crbug.com/1136875 [ fuchsia-board-qemu-x64 ] Pixel_PrecisionRoundedCorner [ RetryOnFailure ] # Still fails on Nexus 5 after all other Pixel_CanvasLowLatency* pass. crbug.com/1097752 [ android android-nexus-5 ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ] +# Fails on Fuchsia emulators +crbug.com/1302427 [ fuchsia-board-qemu-x64 ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ] + # Produces blank images on gpu-fyi-try-chromeos-kevin, gpu-fyi-try-chromeos-amd64-generic. crbug.com/1086690 [ chromeos ] Pixel_WebGLSadCanvas [ Failure ] @@ -321,15 +326,6 @@ crbug.com/1268144 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_BackgroundImage [ Skip ] crbug.com/1268144 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_SolidColorBackground [ Skip ] -crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_AA_NoAlpha [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_AA_NoAlpha [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_WebGL2_ClearBufferfv_Result_Displayed [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_WebGL2_ClearBufferfv_Result_Displayed [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_NoAA_NoAlpha [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_NoAA_NoAlpha [ Failure ] - # Failures in fuchsia-chrome browser crbug.com/1295076 [ fuchsia fuchsia-chrome ] Pixel_CanvasDisplaySRGBAccelerated2D [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt index 9fa7453..906b491 100644 --- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -345,13 +345,8 @@ #################### # Astro specific issues -crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/context/context-no-alpha-fbo-with-alpha.html [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/context/context-attribute-preserve-drawing-buffer.html [ Failure ] -crbug.com/1145858 [ fuchsia fuchsia-board-astro ] conformance/extensions/ext-disjoint-timer-query.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/extensions/oes-texture-float-with-video.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/extensions/oes-texture-half-float-with-video.html [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/rendering/color-mask-preserved-during-implicit-clears.html [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/rendering/scissor-rect-repeated-rendering.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ] @@ -359,8 +354,6 @@ crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/copy-tex-image-2d-formats.html [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/tex-input-validation.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ] @@ -369,13 +362,10 @@ crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ] # OOM on hardware devices -crbug.com/1145951 [ fuchsia fuchsia-board-astro ] conformance/rendering/preservedrawingbuffer-leak.html [ Failure ] crbug.com/1146483 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/texture-video-transparent.html [ Failure ] # Flaky tests [ fuchsia fuchsia-board-astro ] WebglExtension_EXT_float_blend [ Failure ] [ fuchsia fuchsia-board-sherlock ] WebglExtension_EXT_float_blend [ Failure ] -[ fuchsia fuchsia-board-astro ] conformance/rendering/rendering-stencil-large-viewport.html [ Failure ] -[ fuchsia fuchsia-board-astro ] conformance/canvas/drawingbuffer-static-canvas-test.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/texture-npot-video.html [ Failure ] crbug.com/1210953 [ fuchsia fuchsia-board-astro ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ RetryOnFailure ] @@ -396,13 +386,8 @@ # [ fuchsia ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ] # Sherlock failures -crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/context/context-attribute-preserve-drawing-buffer.html [ Failure ] -crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/context/context-no-alpha-fbo-with-alpha.html [ Failure ] -crbug.com/1145858 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/extensions/ext-disjoint-timer-query.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/extensions/oes-texture-float-with-video.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/extensions/oes-texture-half-float-with-video.html [ Failure ] -crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/rendering/color-mask-preserved-during-implicit-clears.html [ Failure ] -crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/rendering/scissor-rect-repeated-rendering.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ] crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ] crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ] @@ -410,19 +395,19 @@ crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/copy-tex-image-2d-formats.html [ Failure ] crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/texture-npot-video.html [ Failure ] crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/texture-video-transparent.html [ Failure ] crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ] crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ] crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ] crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ] -crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/tex-input-validation.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ] crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ] +crbug.com/1145858 [ fuchsia ] conformance/extensions/ext-disjoint-timer-query.html [ Failure ] + #################### # Win failures # ####################
diff --git a/docs/security/document-domain.md b/docs/security/document-domain.md index 4f9d340..2046870 100644 --- a/docs/security/document-domain.md +++ b/docs/security/document-domain.md
@@ -58,6 +58,19 @@ current default or have not implemented the `Origin-Agent-Cluster` header at all. +## Where are the deprecation warnings found? + +The deprecation warnings are found in the [issues tab](https://developer.chrome.com/docs/devtools/issues/). + +## What does the deprecation warning tell me? + +There are two deprecation warnings: One for setting the `document.domain` +accessors, which modifies the security behaviour. And from M101 on, +a second warning when a cross-domain access is made that is facilitated by +the modified `document.domain` property. The first warning tells you where +the setup happens, and the second one tells you where it is being used (and +thus likely why this is being done in the first place). + ## How Can I Test This? In the DevTools console, for a page `www.example.test`: @@ -78,7 +91,7 @@ How to enable/disable the deprecation: -### Enable the Warning (Scheduled for M100) +### Enable the Warning (Before M100) * Start Chrome with `--enable-features=OriginAgentClusterDefaultWarning`
diff --git a/docs/security/web-mitigation-metrics.md b/docs/security/web-mitigation-metrics.md index cabfde43..00d30d68 100644 --- a/docs/security/web-mitigation-metrics.md +++ b/docs/security/web-mitigation-metrics.md
@@ -181,11 +181,12 @@ * Sanitizer creation: `kSanitizerAPICreated` and `kSanitizerAPIDefaultConfiguration` tell us how many Sanitizers are created and how many Sanitizers are created without custom configurations. -* Sanitizing process: `kSanitizerAPIToString` and - `kSanitizerAPIToFragment` counts the usage of two methods, - `Sanitizer::sanitizeToString` and `Sanitizer::sanitize`. -* `kSanitizerAPIActionTaken` shows how many times do the - actual sanitize action has been performed while calling the Sanitizer APIs. +* Sanitizer method: `kSanitizerAPIToFragment`, `kSanitizerAPISanitizeFor`, + and `kSanitizerAPIElementSetSanitized` measure which API entry point has been + called. +* `kSanitizerAPIActionTaken` shows how many times a sanitize action has been + performed while calling the Sanitizer APIs. (That is, on how many sanitizer + calls did the sanitizer remove nodes from the input sets.) * Input type: `kSanitizerAPIFromString`, `kSanitizerAPIFromDocument` and `kSanitizerAPIFromFragment` tell us what kind of input people are using.
diff --git a/docs/updater/design_doc.md b/docs/updater/design_doc.md index 93b4e40f..908396c 100644 --- a/docs/updater/design_doc.md +++ b/docs/updater/design_doc.md
@@ -8,3 +8,157 @@ The objective is to create an updater for desktop client software using Chromium code and tools. +## Dynamic Install Parameters + +### `installdataindex` + +`installdataindex` is one of the install parameters that can be specified for +first installs on the command line or via the +[metainstaller tag](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/tools/tag.py). + +For example, here is a typical command line for the Updater on Windows: +``` +UpdaterSetup.exe /install "appguid=YourAppID&appname=YourAppName&needsadmin=False&lang=en&installdataindex =verboselog" +``` + +In this case, the updater client sends the `installdataindex` of `verboselog` to +the update server. + +This involves: +* [Parsing the tag](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/tag.h) +into individual tag components, including install_data_index. +* Creating a CrxComponent with the install_data_index and providing it to the +[update_client](https://source.chromium.org/chromium/chromium/src/+/main:components/update_client/update_client.h). +* update_client sends the install_data_index to the update server. + +This is how a [JSON](https://www.json.org/) request from update_client may look +like: + +``` +{ + "request":{ + "@os":"win", + "@updater":"updater", + "acceptformat":"crx3", + "app":[ + { + "appid":"YourAppID", + "data":[ + { + "index":"verboselog", + "name":"install" + } + ], + "enabled":true, + "installsource":"ondemand", + "ping":{ + "r":-2 + }, + "updatecheck":{ + "sameversionupdate":true + }, + "version":"0.1" + } + ], + "arch":"x86", + "dedup":"cr", + "domainjoined":true, + "hw":{ + "avx":true, + "physmemory":32, + "sse":true, + "sse2":true, + "sse3":true, + "sse41":true, + "sse42":true, + "ssse3":true + }, + "ismachine":false, + "lang":"en-US", + "nacl_arch":"x86-64", + "os":{ + "arch":"x86_64", + "platform":"Windows", + "version":"10.0.19042.1586" + }, + "prodversion":"101.0.4949.0", + "protocol":"3.1", + "requestid":"{6b417770-1f68-4d52-8843-356760c84d33}", + "sessionid":"{37775211-4487-48d5-845d-35a1d71b03bc}", + "updaterversion":"101.0.4949.0", + "wow64":true + } +} +``` + +The server retrieves the data corresponding to `installdataindex=verboselog` +and returns it back to update_client. + +This is how a JSON response from the update server may look like: + +``` + "response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "data":[{ + "status":"ok", + "name":"install", + "index":"verboselog", + "#text":"{\"logging\":{\"verbose\":true}}" + }], + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://example.com/"}, + {"codebasediff":"http://diff.example.com/"}]}, + "manifest":{ + "version":"1.2.3.4", + "prodversionmin":"2.0.143.0", + "run":"UpdaterSetup.exe", + "arguments":"--arg1 --arg2", + "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}} + } + } + ] + } +``` + +update_client provides this response data back to the Updater. + +The updater client writes this data to a temporary file in the same directory as +the application installer. This is for security reasons, since writing the data +to the temp directory could potentially allow a man-in-the-middle attack. + +The updater client provides the temporary file as a parameter to the application +installer. + +Let's say, as shown above, that the update server responds with these example +file contents: +``` +{"logging":{"verbose":true}} +``` + +The updater client will now create a temporary file, say `c:\my +path\temporaryfile.dat` (assuming the application installer is running from +`c:\my path\YesExe.exe`), with the following file contents: +``` +\xEF\xBB\xBF{"logging":{"verbose":true}} +``` + +and then provide the file as a parameter to the application installer: +``` +"c:\my path\YesExe.exe" --installerdata="c:\my path\temporaryfile.dat" +``` + +* Notice above that the temp file contents are prefixed with an UTF-8 Byte Order +Mark of `EF BB BF`. +* For MSI installers, a property will passed to the installer: +`INSTALLERDATA="pathtofile"`. +* For exe-based installers, as shown above, a command line parameter will be +passed to the installer: `--installerdata="pathtofile"`. +* For Mac installers, an environment variable will be set: +`INSTALLERDATA="pathtofile"`. +* Ownership of the temp file is the responsibility of the application installer. +The updater will not delete this file. +* This installerdata is not persisted anywhere else, and it is not sent as a +part of pings to the update server.
diff --git a/docs/updater/functional_spec.md b/docs/updater/functional_spec.md index 0e75a3a..022b470 100644 --- a/docs/updater/functional_spec.md +++ b/docs/updater/functional_spec.md
@@ -5,4 +5,148 @@ [TOC] -## +## Dynamic Install Parameters + +### `installdataindex` + +`installdataindex` is one of the install parameters that can be specified for +first installs on the command line or via the +[metainstaller tag](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/tools/tag.py). + +For example, here is a typical command line for the Updater on Windows: +``` +UpdaterSetup.exe /install "appguid=YourAppID&appname=YourAppName&needsadmin=False&lang=en&installdataindex =verboselog" +``` + +In this case, the updater client sends the `installdataindex` of `verboselog` to +the update server. + +This is how a [JSON](https://www.json.org/) request from the updater client may +look like: + +``` +{ + "request":{ + "@os":"win", + "@updater":"updater", + "acceptformat":"crx3", + "app":[ + { + "appid":"YourAppID", + "data":[ + { + "index":"verboselog", + "name":"install" + } + ], + "enabled":true, + "installsource":"ondemand", + "ping":{ + "r":-2 + }, + "updatecheck":{ + "sameversionupdate":true + }, + "version":"0.1" + } + ], + "arch":"x86", + "dedup":"cr", + "domainjoined":true, + "hw":{ + "avx":true, + "physmemory":32, + "sse":true, + "sse2":true, + "sse3":true, + "sse41":true, + "sse42":true, + "ssse3":true + }, + "ismachine":false, + "lang":"en-US", + "nacl_arch":"x86-64", + "os":{ + "arch":"x86_64", + "platform":"Windows", + "version":"10.0.19042.1586" + }, + "prodversion":"101.0.4949.0", + "protocol":"3.1", + "requestid":"{6b417770-1f68-4d52-8843-356760c84d33}", + "sessionid":"{37775211-4487-48d5-845d-35a1d71b03bc}", + "updaterversion":"101.0.4949.0", + "wow64":true + } +} +``` + +The server retrieves the data corresponding to `installdataindex=verboselog` +and returns it back to the updater client. + +This is how a JSON response from the update server may look like: + +``` + "response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "data":[{ + "status":"ok", + "name":"install", + "index":"verboselog", + "#text":"{\"logging\":{\"verbose\":true}}" + }], + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://example.com/"}, + {"codebasediff":"http://diff.example.com/"}]}, + "manifest":{ + "version":"1.2.3.4", + "prodversionmin":"2.0.143.0", + "run":"UpdaterSetup.exe", + "arguments":"--arg1 --arg2", + "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}} + } + } + ] + } +``` + +The updater client writes this data to a temporary file in the same directory as +the application installer. This is for security reasons, since writing the data +to the temp directory could potentially allow a man-in-the-middle attack. + +The updater client provides the temporary file as a parameter to the application +installer. + +Let's say, as shown above, that the update server responds with these example +file contents: +``` +{"logging":{"verbose":true}} +``` + +The updater client will now create a temporary file, say `c:\my +path\temporaryfile.dat` (assuming the application installer is running from +`c:\my path\YesExe.exe`), with the following file contents: +``` +\xEF\xBB\xBF{"logging":{"verbose":true}} +``` + +and then provide the file as a parameter to the application installer: +``` +"c:\my path\YesExe.exe" --installerdata="c:\my path\temporaryfile.dat" +``` + +* Notice above that the temp file contents are prefixed with an UTF-8 Byte Order +Mark of `EF BB BF`. +* For MSI installers, a property will passed to the installer: +`INSTALLERDATA="pathtofile"`. +* For exe-based installers, as shown above, a command line parameter will be +passed to the installer: `--installerdata="pathtofile"`. +* For Mac installers, an environment variable will be set: +`INSTALLERDATA="pathtofile"`. +* Ownership of the temp file is the responsibility of the application installer. +The updater will not delete this file. +* This installerdata is not persisted anywhere else, and it is not sent as a +part of pings to the update server.
diff --git a/docs/updater/user_manual.md b/docs/updater/user_manual.md index 5f06126..5e9f43a 100644 --- a/docs/updater/user_manual.md +++ b/docs/updater/user_manual.md
@@ -1,7 +1,62 @@ # Chromium Updater User Manual -This is the user manual for [Chromium Updater](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/). +This is the user manual for +[Chromium Updater](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/). [TOC] -## +## Dynamic Install Parameters + +### `installdataindex` + +`installdataindex` is one of the install parameters that can be specified for +first installs on the command line or via the +[metainstaller tag](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/tools/tag.py). + +For example, here is a typical command line for the Updater on Windows: +``` +UpdaterSetup.exe /install "appguid=YourAppID&appname=YourAppName&needsadmin=False&lang=en&installdataindex =verboselog" +``` + +In this case, the updater client sends the `installdataindex` of `verboselog` to +the update server. + +The server retrieves the data corresponding to `installdataindex=verboselog` and +returns it back to the updater client. + +The updater client writes this data to a temporary file in the same directory as +the application installer. + +The updater client provides the temporary file as a parameter to the application +installer. + +Let's say, as shown above, that the update server responds with these example +file contents: +``` +{"logging":{"verbose":true}} +``` + +The updater client will now create a temporary file, say `c:\my +path\temporaryfile.dat` (assuming the application installer is running from +`c:\my path\YesExe.exe`), with the following file contents: +``` +\xEF\xBB\xBF{"logging":{"verbose":true}} +``` + +and then provide the file as a parameter to the application installer: +``` +"c:\my path\YesExe.exe" --installerdata="c:\my path\temporaryfile.dat" +``` + +* Notice above that the temp file contents are prefixed with an UTF-8 Byte Order +Mark of `EF BB BF`. +* For MSI installers, a property will passed to the installer: +`INSTALLERDATA="pathtofile"`. +* For exe-based installers, as shown above, a command line parameter will be +passed to the installer: `--installerdata="pathtofile"`. +* For Mac installers, an environment variable will be set: +`INSTALLERDATA="pathtofile"`. +* Ownership of the temp file is the responsibility of the application installer. +The updater will not delete this file. +* This installerdata is not persisted anywhere else, and it is not sent as a +part of pings to the update server.
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn index 44b1d66..96de962 100644 --- a/extensions/browser/BUILD.gn +++ b/extensions/browser/BUILD.gn
@@ -164,6 +164,7 @@ "api/scripting/scripting_utils.h", "api/storage/settings_namespace.cc", "api/storage/settings_namespace.h", + "api/storage/settings_observer.h", "api/system_display/display_info_provider.cc", "api/system_display/display_info_provider.h", "api/system_display/system_display_api.cc",
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn index 042c82b..61e9cc8 100644 --- a/extensions/browser/api/BUILD.gn +++ b/extensions/browser/api/BUILD.gn
@@ -23,7 +23,6 @@ "storage/local_value_store_cache.h", "storage/session_storage_manager.cc", "storage/session_storage_manager.h", - "storage/settings_observer.h", "storage/settings_storage_quota_enforcer.cc", "storage/settings_storage_quota_enforcer.h", "storage/storage_api.cc",
diff --git a/extensions/browser/api/extensions_api_client.cc b/extensions/browser/api/extensions_api_client.cc index cf7b1ac..500006b 100644 --- a/extensions/browser/api/extensions_api_client.cc +++ b/extensions/browser/api/extensions_api_client.cc
@@ -30,8 +30,7 @@ void ExtensionsAPIClient::AddAdditionalValueStoreCaches( content::BrowserContext* context, const scoped_refptr<value_store::ValueStoreFactory>& factory, - const scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>>& - observers, + SettingsChangedCallback observer, std::map<settings_namespace::Namespace, ValueStoreCache*>* caches) {} void ExtensionsAPIClient::AttachWebContentsHelpers(
diff --git a/extensions/browser/api/extensions_api_client.h b/extensions/browser/api/extensions_api_client.h index 20ddb020..d5f0ceb 100644 --- a/extensions/browser/api/extensions_api_client.h +++ b/extensions/browser/api/extensions_api_client.h
@@ -16,17 +16,13 @@ #include "extensions/browser/api/clipboard/clipboard_api.h" #include "extensions/browser/api/declarative_content/content_rules_registry.h" #include "extensions/browser/api/storage/settings_namespace.h" +#include "extensions/browser/api/storage/settings_observer.h" #include "extensions/common/api/clipboard.h" #include "extensions/common/extension.h" #include "extensions/common/extension_id.h" class GURL; -namespace base { -template <class T> -class ObserverListThreadSafe; -} - namespace content { class BrowserContext; class WebContents; @@ -59,7 +55,6 @@ class MimeHandlerViewGuestDelegate; class NonNativeFileSystemDelegate; class RulesCacheDelegate; -class SettingsObserver; class SupervisedUserExtensionsDelegate; class ValueStoreCache; class VirtualKeyboardDelegate; @@ -90,8 +85,7 @@ virtual void AddAdditionalValueStoreCaches( content::BrowserContext* context, const scoped_refptr<value_store::ValueStoreFactory>& factory, - const scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>>& - observers, + SettingsChangedCallback observer, std::map<settings_namespace::Namespace, ValueStoreCache*>* caches); // Attaches any extra web contents helpers (like ExtensionWebContentsObserver)
diff --git a/extensions/browser/api/storage/settings_observer.h b/extensions/browser/api/storage/settings_observer.h index 968ef0b..a4abf1fd 100644 --- a/extensions/browser/api/storage/settings_observer.h +++ b/extensions/browser/api/storage/settings_observer.h
@@ -5,25 +5,33 @@ #ifndef EXTENSIONS_BROWSER_API_STORAGE_SETTINGS_OBSERVER_H_ #define EXTENSIONS_BROWSER_API_STORAGE_SETTINGS_OBSERVER_H_ -#include "base/observer_list_threadsafe.h" +#include "base/callback.h" +#include "base/task/bind_post_task.h" +#include "base/task/sequenced_task_runner.h" +#include "base/types/strong_alias.h" #include "base/values.h" namespace extensions { enum class StorageAreaNamespace; -// Interface for classes that listen to changes to extension settings. -class SettingsObserver { - public: - // Called when a list of settings have changed for an extension. - virtual void OnSettingsChanged(const std::string& extension_id, - StorageAreaNamespace storage_area, - const base::Value& changes) = 0; +using SettingsChangedCallback = base::RepeatingCallback< + void(const std::string&, StorageAreaNamespace, base::Value)>; - virtual ~SettingsObserver() {} -}; +using SequenceBoundSettingsChangedCallback = + base::StrongAlias<class SequenceBoundSettingsChangedCallbackTag, + SettingsChangedCallback>; -typedef base::ObserverListThreadSafe<SettingsObserver> SettingsObserverList; +// Returns a callback that is guaranteed to run on |task_runner|. This should be +// used when the callback is invoked from other sequences. +inline SequenceBoundSettingsChangedCallback +GetSequenceBoundSettingsChangedCallback( + scoped_refptr<base::SequencedTaskRunner> task_runner, + SettingsChangedCallback callback, + const base::Location& location = FROM_HERE) { + return SequenceBoundSettingsChangedCallback(base::BindPostTask( + std::move(task_runner), std::move(callback), location)); +} } // namespace extensions
diff --git a/extensions/browser/api/storage/storage_api.cc b/extensions/browser/api/storage/storage_api.cc index 00f0f15..765fd338 100644 --- a/extensions/browser/api/storage/storage_api.cc +++ b/extensions/browser/api/storage/storage_api.cc
@@ -167,7 +167,9 @@ storage_area_string.c_str()))); } - observers_ = frontend->GetObservers(); + observer_ = GetSequenceBoundSettingsChangedCallback( + base::SequencedTaskRunnerHandle::Get(), frontend->GetObserver()); + frontend->RunWithStorage( extension(), settings_namespace_, base::BindOnce(&SettingsFunction::AsyncRunWithStorage, this)); @@ -201,9 +203,8 @@ return Error(result.status().message); if (!result.changes().empty()) { - observers_->Notify( - FROM_HERE, &SettingsObserver::OnSettingsChanged, extension_id(), - storage_area_, + observer_->Run( + extension_id(), storage_area_, value_store::ValueStoreChange::ToValue(result.PassChanges())); } @@ -213,11 +214,14 @@ void SettingsFunction::OnSessionSettingsChanged( std::vector<SessionStorageManager::ValueChange> changes) { if (!changes.empty()) { - scoped_refptr<SettingsObserverList> observers = - StorageFrontend::Get(browser_context())->GetObservers(); - observers->Notify(FROM_HERE, &SettingsObserver::OnSettingsChanged, - extension_id(), storage_area_, - ValueChangeToValue(std::move(changes))); + SettingsChangedCallback observer = + StorageFrontend::Get(browser_context())->GetObserver(); + // This used to dispatch asynchronously as a result of a + // ObserverListThreadSafe. Ideally, we'd just run this synchronously, but it + // appears at least some tests rely on the asynchronous behavior. + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(observer, extension_id(), storage_area_, + ValueChangeToValue(std::move(changes)))); } }
diff --git a/extensions/browser/api/storage/storage_api.h b/extensions/browser/api/storage/storage_api.h index fe30fad..5e1c525 100644 --- a/extensions/browser/api/storage/storage_api.h +++ b/extensions/browser/api/storage/storage_api.h
@@ -67,7 +67,7 @@ settings_namespace::INVALID; // Observers, cached so that it's only grabbed from the UI thread. - scoped_refptr<SettingsObserverList> observers_; + SequenceBoundSettingsChangedCallback observer_; }; class StorageStorageAreaGetFunction : public SettingsFunction {
diff --git a/extensions/browser/api/storage/storage_frontend.cc b/extensions/browser/api/storage/storage_frontend.cc index 1ebd2137..a250b18 100644 --- a/extensions/browser/api/storage/storage_frontend.cc +++ b/extensions/browser/api/storage/storage_frontend.cc
@@ -57,64 +57,6 @@ } } -// Settings change Observer which forwards changes on to the extension -// processes for |context| and its incognito partner if it exists. -class DefaultObserver : public SettingsObserver { - public: - explicit DefaultObserver(BrowserContext* context) - : browser_context_(context) {} - - // SettingsObserver implementation. - void OnSettingsChanged(const std::string& extension_id, - StorageAreaNamespace storage_area, - const base::Value& changes) override { - TRACE_EVENT1("browser", "SettingsObserver:OnSettingsChanged", - "extension_id", extension_id); - - // Alias extension_id for investigation of shutdown hangs. crbug.com/1154997 - // Extension IDs are exactly 32 characters in length. - constexpr size_t kExtensionsIdLength = 32; - char extension_id_str[kExtensionsIdLength + 1]; - base::strlcpy(extension_id_str, extension_id.c_str(), - std::size(extension_id_str)); - base::debug::Alias(extension_id_str); - - const std::string namespace_string = StorageAreaToString(storage_area); - EventRouter* event_router = EventRouter::Get(browser_context_); - - // We only dispatch the events if there's a valid listener (even though - // EventRouter would handle the no-listener case) since copying `changes` - // can be expensive. - // Event for each storage(sync, local, managed). - if (event_router->ExtensionHasEventListener( - extension_id, api::storage::OnChanged::kEventName)) { - std::vector<base::Value> args; - args.push_back(changes.Clone()); - args.push_back(base::Value(namespace_string)); - std::unique_ptr<Event> event( - new Event(events::STORAGE_ON_CHANGED, - api::storage::OnChanged::kEventName, std::move(args))); - event_router->DispatchEventToExtension(extension_id, std::move(event)); - } - - // Event for StorageArea. - auto area_event_name = - base::StringPrintf("storage.%s.onChanged", namespace_string.c_str()); - if (event_router->ExtensionHasEventListener(extension_id, - area_event_name)) { - std::vector<base::Value> args; - args.push_back(changes.Clone()); - auto event = - std::make_unique<Event>(StorageAreaToEventHistogram(storage_area), - area_event_name, std::move(args)); - event_router->DispatchEventToExtension(extension_id, std::move(event)); - } - } - - private: - const raw_ptr<BrowserContext> browser_context_; -}; - } // namespace // static @@ -145,25 +87,19 @@ scoped_refptr<value_store::ValueStoreFactory> factory) { TRACE_EVENT0("browser,startup", "StorageFrontend::Init"); - observers_ = new SettingsObserverList(); - browser_context_observer_ = - std::make_unique<DefaultObserver>(browser_context_); DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!browser_context_->IsOffTheRecord()); - observers_->AddObserver(browser_context_observer_.get()); - caches_[settings_namespace::LOCAL] = new LocalValueStoreCache(factory); // Add any additional caches the embedder supports (for example, caches // for chrome.storage.managed and chrome.storage.sync). ExtensionsAPIClient::Get()->AddAdditionalValueStoreCaches( - browser_context_, factory, observers_, &caches_); + browser_context_, factory, GetObserver(), &caches_); } StorageFrontend::~StorageFrontend() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - observers_->RemoveObserver(browser_context_observer_.get()); for (auto it = caches_.begin(); it != caches_.end(); ++it) { ValueStoreCache* cache = it->second; cache->ShutdownOnUI(); @@ -216,9 +152,10 @@ } } -scoped_refptr<SettingsObserverList> StorageFrontend::GetObservers() { +SettingsChangedCallback StorageFrontend::GetObserver() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - return observers_; + return base::BindRepeating(&StorageFrontend::OnSettingsChanged, + weak_factory_.GetWeakPtr()); } void StorageFrontend::DisableStorageForTesting( @@ -232,6 +169,67 @@ } } +// Forwards changes on to the extension processes for |browser_context_| and its +// incognito partner if it exists. +void StorageFrontend::OnSettingsChanged(const std::string& extension_id, + StorageAreaNamespace storage_area, + base::Value changes) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + TRACE_EVENT1("browser", "SettingsObserver:OnSettingsChanged", "extension_id", + extension_id); + + // Alias extension_id for investigation of shutdown hangs. crbug.com/1154997 + // Extension IDs are exactly 32 characters in length. + constexpr size_t kExtensionsIdLength = 32; + char extension_id_str[kExtensionsIdLength + 1]; + base::strlcpy(extension_id_str, extension_id.c_str(), + std::size(extension_id_str)); + base::debug::Alias(extension_id_str); + + const std::string namespace_string = StorageAreaToString(storage_area); + EventRouter* event_router = EventRouter::Get(browser_context_); + + bool has_event_changed_listener = event_router->ExtensionHasEventListener( + extension_id, api::storage::OnChanged::kEventName); + + // Event for StorageArea. + auto area_event_name = + base::StringPrintf("storage.%s.onChanged", namespace_string.c_str()); + bool has_area_changed_event_listener = + event_router->ExtensionHasEventListener(extension_id, area_event_name); + + auto make_changed_event = [&namespace_string](base::Value changes) { + std::vector<base::Value> args; + args.emplace_back(std::move(changes)); + args.emplace_back(base::Value(namespace_string)); + return std::make_unique<Event>(events::STORAGE_ON_CHANGED, + api::storage::OnChanged::kEventName, + std::move(args)); + }; + auto make_area_changed_event = [&storage_area, + &area_event_name](base::Value changes) { + std::vector<base::Value> args; + args.push_back(std::move(changes)); + return std::make_unique<Event>(StorageAreaToEventHistogram(storage_area), + area_event_name, std::move(args)); + }; + // We only dispatch the events if there's a valid listener (even though + // EventRouter would handle the no-listener case) since copying `changes` + // can be expensive. + // Event for each storage(sync, local, managed). + if (has_event_changed_listener && has_area_changed_event_listener) { + event_router->DispatchEventToExtension( + extension_id, make_area_changed_event(changes.Clone())); + } + if (has_event_changed_listener) { + event_router->DispatchEventToExtension( + extension_id, make_changed_event(std::move(changes))); + } else if (has_area_changed_event_listener) { + event_router->DispatchEventToExtension( + extension_id, make_area_changed_event(std::move(changes))); + } +} + // BrowserContextKeyedAPI implementation. // static
diff --git a/extensions/browser/api/storage/storage_frontend.h b/extensions/browser/api/storage/storage_frontend.h index 9bdfa37..474d137 100644 --- a/extensions/browser/api/storage/storage_frontend.h +++ b/extensions/browser/api/storage/storage_frontend.h
@@ -11,6 +11,8 @@ #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/values.h" #include "extensions/browser/api/storage/settings_namespace.h" #include "extensions/browser/api/storage/settings_observer.h" #include "extensions/browser/api/storage/value_store_cache.h" @@ -61,8 +63,8 @@ void DeleteStorageSoon(const std::string& extension_id, base::OnceClosure done_callback); - // Gets the thread-safe observer list. - scoped_refptr<SettingsObserverList> GetObservers(); + // Gets the Settings change callback. + SettingsChangedCallback GetObserver(); void DisableStorageForTesting( settings_namespace::Namespace settings_namespace); @@ -87,18 +89,18 @@ void Init(scoped_refptr<value_store::ValueStoreFactory> storage_factory); + void OnSettingsChanged(const std::string& extension_id, + StorageAreaNamespace storage_area, + base::Value changes); + // The (non-incognito) browser context this Frontend belongs to. const raw_ptr<content::BrowserContext> browser_context_; - // List of observers to settings changes. - scoped_refptr<SettingsObserverList> observers_; - - // Observer for |browser_context_|. - std::unique_ptr<SettingsObserver> browser_context_observer_; - // Maps a known namespace to its corresponding ValueStoreCache. The caches // are owned by this object. CacheMap caches_; + + base::WeakPtrFactory<StorageFrontend> weak_factory_{this}; }; } // namespace extensions
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc index b20f5fc..73beeb8 100644 --- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc +++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -5,18 +5,27 @@ #include "extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h" #include <memory> +#include <ostream> +#include <string> #include <utility> +#include <vector> #include "base/bind.h" +#include "base/callback.h" #include "base/callback_helpers.h" +#include "base/check.h" +#include "base/check_op.h" #include "base/feature_list.h" +#include "base/memory/scoped_refptr.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" +#include "base/notreached.h" #include "base/strings/stringprintf.h" -#include "base/task/post_task.h" +#include "base/task/sequenced_task_runner.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h" -#include "components/ukm/content/source_url_recorder.h" +#include "components/keyed_service/core/keyed_service_shutdown_notifier.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -24,16 +33,21 @@ #include "content/public/browser/global_request_id.h" #include "content/public/browser/network_service_instance.h" #include "content/public/browser/render_frame_host.h" -#include "content/public/browser/render_process_host.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/url_utils.h" #include "extensions/browser/api/web_request/permission_helper.h" +#include "extensions/browser/api/web_request/web_request_api.h" #include "extensions/browser/extension_navigation_ui_data.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/extension_features.h" #include "extensions/common/extensions_client.h" #include "extensions/common/manifest_handlers/web_accessible_resources_info.h" -#include "net/base/completion_repeating_callback.h" +#include "net/base/auth.h" +#include "net/base/ip_endpoint.h" +#include "net/base/isolation_info.h" +#include "net/base/net_errors.h" +#include "net/base/request_priority.h" +#include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" #include "net/http/http_util.h" @@ -42,12 +56,16 @@ #include "services/metrics/public/cpp/ukm_builders.h" #include "services/metrics/public/cpp/ukm_recorder.h" #include "services/metrics/public/cpp/ukm_source_id.h" -#include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/url_loader_completion_status.h" #include "services/network/public/mojom/early_hints.mojom.h" #include "services/network/public/mojom/network_service.mojom.h" #include "services/network/public/mojom/parsed_headers.mojom-forward.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/common/loader/throttling_url_loader.h" +#include "url/gurl.h" #include "url/origin.h" +#include "url/url_constants.h" namespace extensions { namespace {
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h index afb814d..10a7ca6 100644 --- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h +++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -7,24 +7,27 @@ #include <cstdint> #include <map> +#include <memory> +#include <set> +#include <string> +#include <vector> -#include "base/callback.h" +#include "base/callback_list.h" #include "base/memory/raw_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/memory/ref_counted_delete_on_sequence.h" +#include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" -#include "components/keyed_service/core/keyed_service_shutdown_notifier.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_frame_host.h" #include "extensions/browser/api/web_request/web_request_api.h" #include "extensions/browser/api/web_request/web_request_info.h" -#include "extensions/common/extension_id.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/remote.h" +#include "net/base/auth.h" #include "net/base/completion_once_callback.h" +#include "net/base/request_priority.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "services/network/public/cpp/resource_request.h" @@ -34,6 +37,22 @@ #include "services/network/public/mojom/url_response_head.mojom.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "url/gurl.h" +#include "url/origin.h" + +namespace { +class BrowserContext; +} + +namespace net { +class HttpRequestHeaders; +class HttpResponseHeaders; +class IPEndPoint; +struct RedirectInfo; +} // namespace net + +namespace network { +struct URLLoaderCompletionStatus; +} namespace extensions {
diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc index fed3a765..a843c1f 100644 --- a/extensions/browser/event_router.cc +++ b/extensions/browser/event_router.cc
@@ -34,7 +34,6 @@ #include "extensions/browser/process_manager.h" #include "extensions/browser/process_map.h" #include "extensions/common/constants.h" -#include "extensions/common/event_filtering_info_type_converters.h" #include "extensions/common/extension.h" #include "extensions/common/extension_api.h" #include "extensions/common/extension_messages.h" @@ -163,26 +162,22 @@ UserGestureState user_gesture, mojom::EventFilteringInfoPtr info) { NotifyEventDispatched(browser_context, extension_id, event_name, *event_args); - mojom::DispatchEventParams params; - params.worker_thread_id = worker_thread_id; - params.extension_id = extension_id; - params.event_name = event_name; - params.event_id = event_id; - params.is_user_gesture = user_gesture == USER_GESTURE_ENABLED; - params.filtering_info = info->To<EventFilteringInfo>(); + auto params = mojom::DispatchEventParams::New(); + params->worker_thread_id = worker_thread_id; + params->extension_id = extension_id; + params->event_name = event_name; + params->event_id = event_id; + params->is_user_gesture = user_gesture == USER_GESTURE_ENABLED; + params->filtering_info = std::move(info); - // TODO(crbug/1222550): Remove IPC->Send call after worker_thread_dispatcher - // is also mojofied. - if (worker_thread_id == kMainThreadId) { - Get(browser_context)->RouteDispatchEvent(rph, params.Clone(), *event_args); - } else { - rph->Send(new ExtensionMsg_DispatchEvent(params, *event_args)); - } + Get(browser_context)->RouteDispatchEvent(rph, std::move(params), *event_args); } void EventRouter::RouteDispatchEvent(content::RenderProcessHost* rph, - const mojom::DispatchEventParamsPtr params, - const ListValue& event_args) { + mojom::DispatchEventParamsPtr params, + ListValue& event_args) { + // TODO(crbug.com/1302000) Add bindings for worker threads to be directly + // channel-associated. mojo::AssociatedRemote<mojom::EventDispatcher>& dispatcher = rph_dispatcher_map_[rph]; if (!dispatcher.is_bound()) { @@ -193,7 +188,7 @@ channel->GetRemoteAssociatedInterface( dispatcher.BindNewEndpointAndPassReceiver()); } - dispatcher->DispatchEvent(params.Clone(), event_args.Clone()); + dispatcher->DispatchEvent(std::move(params), event_args.Clone()); } // static
diff --git a/extensions/browser/event_router.h b/extensions/browser/event_router.h index 62def670..82adadd 100644 --- a/extensions/browser/event_router.h +++ b/extensions/browser/event_router.h
@@ -449,8 +449,8 @@ int64_t service_worker_version_id); void RouteDispatchEvent(content::RenderProcessHost* rph, - const mojom::DispatchEventParamsPtr params, - const base::ListValue& event_args); + mojom::DispatchEventParamsPtr params, + base::ListValue& event_args); // static static void DoDispatchEventToSenderBookkeeping(
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc index 3c60b32..56b6d5d 100644 --- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc +++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
@@ -7,6 +7,7 @@ #include <utility> #include "base/bind.h" +#include "base/check_op.h" #include "components/guest_view/common/guest_view_constants.h" #include "content/public/browser/host_zoom_map.h" #include "content/public/browser/navigation_handle.h" @@ -14,6 +15,7 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/site_instance.h" #include "content/public/common/child_process_host.h" #include "content/public/common/url_constants.h" #include "extensions/browser/api/extensions_api_client.h" @@ -188,11 +190,28 @@ return; } - // Use the mime handler extension's SiteInstance to create the guest so it - // goes under the same process as the extension. - ProcessManager* process_manager = ProcessManager::Get(browser_context()); - scoped_refptr<content::SiteInstance> guest_site_instance = - process_manager->GetSiteInstanceForURL(stream_->handler_url()); + // Compute the mime handler extension's `SiteInstance`. This must match the + // `SiteInstance` for the navigation in `DidAttachToEmbedder()`, otherwise the + // wrong `HostZoomMap` will be used, and the `RenderFrameHost` for the guest + // `WebContents` will need to be swapped. + scoped_refptr<content::SiteInstance> guest_site_instance; +#if BUILDFLAG(ENABLE_PDF) + // TODO(crbug.com/1300730): Using `SiteInstance::CreateForURL()` creates a new + // `BrowsingInstance`, which causes problems for features like background + // pages. Remove one of these branches either when `ProcessManager` correctly + // handles the multiple `StoragePartitionConfig` case, or when no + // `MimeHandlerView` extension depends on background pages. + if (mime_handler_extension->id() == extension_misc::kPdfExtensionId) { + guest_site_instance = content::SiteInstance::CreateForURL( + browser_context(), stream_->handler_url()); + } else { +#endif // BUILDFLAG(ENABLE_PDF) + ProcessManager* process_manager = ProcessManager::Get(browser_context()); + guest_site_instance = + process_manager->GetSiteInstanceForURL(stream_->handler_url()); +#if BUILDFLAG(ENABLE_PDF) + } +#endif // BUILDFLAG_ENABLE_PDF) // Clear the zoom level for the mime handler extension. The extension is // responsible for managing its own zoom. This is necessary for OOP PDF, as @@ -466,6 +485,13 @@ const GURL& new_url = navigation_handle->GetURL(); CHECK(url::IsSameOriginWith(new_url, stream_->handler_url()) || new_url.IsAboutBlank()); + +#if BUILDFLAG(ENABLE_PDF) + if (stream_->extension_id() == extension_misc::kPdfExtensionId) { + // Host zoom level should match the override set in `CreateWebContents()`. + DCHECK_EQ(0, content::HostZoomMap::GetZoomLevel(web_contents())); + } +#endif // BUILDFLAG(ENABLE_PDF) } }
diff --git a/extensions/browser/script_executor.cc b/extensions/browser/script_executor.cc index c0c456fe..7361208 100644 --- a/extensions/browser/script_executor.cc +++ b/extensions/browser/script_executor.cc
@@ -66,22 +66,28 @@ frame_id); if (!frame) { AddWillNotInjectResult( - frame_id, base::StringPrintf("No frame with ID: %d", frame_id)); + frame_id, ExtensionApiFrameIdMap::DocumentId(), + base::StringPrintf("No frame with ID: %d", frame_id)); continue; } DCHECK(!base::Contains(pending_render_frames_, frame)); if (!frame->IsRenderFrameLive()) { + ExtensionApiFrameIdMap::DocumentId document_id = + ExtensionApiFrameIdMap::GetDocumentId(frame); AddWillNotInjectResult( - frame_id, + frame_id, document_id, base::StringPrintf("Frame with ID %d is not ready", frame_id)); continue; } if (frame->IsErrorDocument()) { + ExtensionApiFrameIdMap::DocumentId document_id = + ExtensionApiFrameIdMap::GetDocumentId(frame); AddWillNotInjectResult( - frame_id, base::StringPrintf( - "Frame with ID %d is showing error page", frame_id)); + frame_id, document_id, + base::StringPrintf("Frame with ID %d is showing error page", + frame_id)); continue; } @@ -146,8 +152,10 @@ void WebContentsDestroyed() override { for (content::RenderFrameHost* frame : pending_render_frames_) { int frame_id = ExtensionApiFrameIdMap::GetFrameId(frame); + ExtensionApiFrameIdMap::DocumentId document_id = + ExtensionApiFrameIdMap::GetDocumentId(frame); AddWillNotInjectResult( - frame_id, + frame_id, document_id, base::StringPrintf("Tab containing frame with ID %d was removed.", frame_id)); } @@ -163,16 +171,22 @@ return; int frame_id = ExtensionApiFrameIdMap::GetFrameId(render_frame_host); + ExtensionApiFrameIdMap::DocumentId document_id = + ExtensionApiFrameIdMap::GetDocumentId(render_frame_host); AddWillNotInjectResult( - frame_id, + frame_id, document_id, base::StringPrintf("Frame with ID %d was removed.", frame_id)); if (pending_render_frames_.empty()) Finish(); } - void AddWillNotInjectResult(int frame_id, std::string error) { + void AddWillNotInjectResult( + int frame_id, + const ExtensionApiFrameIdMap::DocumentId& document_id, + std::string error) { ScriptExecutor::FrameResult result; result.frame_id = frame_id; + result.document_id = document_id; result.error = std::move(error); results_.push_back(std::move(result)); } @@ -213,6 +227,8 @@ frame_result.frame_responded = true; frame_result.frame_id = ExtensionApiFrameIdMap::GetFrameId(render_frame_host); + frame_result.document_id = + ExtensionApiFrameIdMap::GetDocumentId(render_frame_host); frame_result.error = error; // TODO(devlin): Do we need to trust the renderer for the URL here? Is there // a risk of the frame having navigated since the injection happened?
diff --git a/extensions/browser/script_executor.h b/extensions/browser/script_executor.h index 12fe8fc..d762422 100644 --- a/extensions/browser/script_executor.h +++ b/extensions/browser/script_executor.h
@@ -13,6 +13,7 @@ #include "base/callback.h" #include "base/memory/raw_ptr.h" #include "base/values.h" +#include "extensions/browser/extension_api_frame_id_map.h" #include "extensions/common/constants.h" #include "extensions/common/mojom/code_injection.mojom.h" #include "extensions/common/mojom/css_origin.mojom-shared.h" @@ -77,6 +78,8 @@ // The ID of the frame of the injection. int frame_id = -1; + // The document ID of the frame of the injection. + ExtensionApiFrameIdMap::DocumentId document_id; // The error associated with the injection, if any. Empty if the injection // succeeded. std::string error;
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn index a2a26ee..21575366 100644 --- a/extensions/common/BUILD.gn +++ b/extensions/common/BUILD.gn
@@ -173,18 +173,6 @@ traits_sources = [ "//extensions/common/mojom/activation_sequence_mojom_traits.cc" ] }, - { - types = [ - { - mojom = "extensions.mojom.EventFilteringInfo" - cpp = "::extensions::EventFilteringInfo" - }, - ] - traits_headers = - [ "//extensions/common/mojom/event_dispatcher_mojom_traits.h" ] - traits_sources = - [ "//extensions/common/mojom/event_dispatcher_mojom_traits.cc" ] - }, ] overridden_deps = [ "//content/public/common:interfaces" ] @@ -251,10 +239,6 @@ "error_utils.h", "event_filter.cc", "event_filter.h", - "event_filtering_info.cc", - "event_filtering_info.h", - "event_filtering_info_type_converters.cc", - "event_filtering_info_type_converters.h", "event_matcher.cc", "event_matcher.h", "extension.cc",
diff --git a/extensions/common/event_filtering_info.cc b/extensions/common/event_filtering_info.cc deleted file mode 100644 index 865b2b85..0000000 --- a/extensions/common/event_filtering_info.cc +++ /dev/null
@@ -1,14 +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 "extensions/common/event_filtering_info.h" - -namespace extensions { - -EventFilteringInfo::EventFilteringInfo() {} -EventFilteringInfo::EventFilteringInfo(const EventFilteringInfo& other) = - default; -EventFilteringInfo::~EventFilteringInfo() {} - -} // namespace extensions
diff --git a/extensions/common/event_filtering_info.h b/extensions/common/event_filtering_info.h deleted file mode 100644 index 04af133..0000000 --- a/extensions/common/event_filtering_info.h +++ /dev/null
@@ -1,53 +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 EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_ -#define EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_ - -#include <memory> -#include <string> - -#include "third_party/abseil-cpp/absl/types/optional.h" -#include "url/gurl.h" - -namespace extensions { - -// Extra information about an event that is used in event filtering. -// -// This is the information that is matched against criteria specified in JS -// extension event listeners. Eg: -// -// chrome.someApi.onSomeEvent.addListener(cb, -// {url: [{hostSuffix: 'google.com'}], -// tabId: 1}); -struct EventFilteringInfo { - public: - EventFilteringInfo(); - EventFilteringInfo(const EventFilteringInfo& other); - ~EventFilteringInfo(); - - absl::optional<GURL> url; - absl::optional<std::string> service_type; - absl::optional<int> instance_id; - - // Note: window type & visible are Chrome concepts, so arguably - // doesn't belong in the extensions module. If the number of Chrome - // concept grows, consider a delegation model with a - // ChromeEventFilteringInfo class. - absl::optional<std::string> window_type; - - // By default events related to windows are filtered based on the - // listener's extension. This parameter will be set if the listener - // didn't set any filter on window types. - absl::optional<bool> window_exposed_by_default; - - bool is_empty() const { - return !url && !service_type && !instance_id && !window_type && - !window_exposed_by_default; - } -}; - -} // namespace extensions - -#endif // EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_
diff --git a/extensions/common/event_filtering_info_type_converters.cc b/extensions/common/event_filtering_info_type_converters.cc deleted file mode 100644 index c6df0a0..0000000 --- a/extensions/common/event_filtering_info_type_converters.cc +++ /dev/null
@@ -1,45 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "extensions/common/event_filtering_info_type_converters.h" - -namespace mojo { - -// static -extensions::mojom::EventFilteringInfoPtr -TypeConverter<extensions::mojom::EventFilteringInfoPtr, - extensions::EventFilteringInfo>:: - Convert(const extensions::EventFilteringInfo& input) { - extensions::mojom::EventFilteringInfoPtr output = - extensions::mojom::EventFilteringInfo::New(); - output->url = input.url; - output->service_type = input.service_type; - output->has_instance_id = input.instance_id.has_value(); - if (output->has_instance_id) - output->instance_id = input.instance_id.value(); - output->window_type = input.window_type; - output->has_window_exposed_by_default = - input.window_exposed_by_default.has_value(); - if (output->has_window_exposed_by_default) - output->window_exposed_by_default = input.window_exposed_by_default.value(); - return output; -} - -// static -extensions::EventFilteringInfo -TypeConverter<extensions::EventFilteringInfo, - extensions::mojom::EventFilteringInfo>:: - Convert(const extensions::mojom::EventFilteringInfo& input) { - extensions::EventFilteringInfo output; - output.url = input.url; - output.service_type = input.service_type; - if (input.has_instance_id) - output.instance_id = input.instance_id; - output.window_type = input.window_type; - if (input.has_window_exposed_by_default) - output.window_exposed_by_default = input.window_exposed_by_default; - return output; -} - -} // namespace mojo \ No newline at end of file
diff --git a/extensions/common/event_filtering_info_type_converters.h b/extensions/common/event_filtering_info_type_converters.h deleted file mode 100644 index 5617457..0000000 --- a/extensions/common/event_filtering_info_type_converters.h +++ /dev/null
@@ -1,31 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef EXTENSIONS_COMMON_EVENT_FILTERING_INFO_TYPE_CONVERTERS_H_ -#define EXTENSIONS_COMMON_EVENT_FILTERING_INFO_TYPE_CONVERTERS_H_ - -#include "extensions/common/event_filtering_info.h" -#include "extensions/common/mojom/event_dispatcher.mojom.h" -#include "mojo/public/cpp/bindings/type_converter.h" - -namespace mojo { -// TODO(crbug.com/1222550): Remove these converters once -// extensions::EventFilteringInfo is removed. -template <> -struct TypeConverter<extensions::mojom::EventFilteringInfoPtr, - extensions::EventFilteringInfo> { - static extensions::mojom::EventFilteringInfoPtr Convert( - const extensions::EventFilteringInfo& input); -}; - -template <> -struct TypeConverter<extensions::EventFilteringInfo, - extensions::mojom::EventFilteringInfo> { - static extensions::EventFilteringInfo Convert( - const extensions::mojom::EventFilteringInfo& input); -}; - -} // namespace mojo - -#endif // EXTENSIONS_COMMON_EVENT_FILTERING_INFO_TYPE_CONVERTERS_H_ \ No newline at end of file
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h index 339ce4b..73683a9b 100644 --- a/extensions/common/extension_messages.h +++ b/extensions/common/extension_messages.h
@@ -28,7 +28,6 @@ #include "extensions/common/common_param_traits.h" #include "extensions/common/constants.h" #include "extensions/common/draggable_region.h" -#include "extensions/common/event_filtering_info.h" #include "extensions/common/extension.h" #include "extensions/common/extension_guid.h" #include "extensions/common/extensions_client.h" @@ -138,27 +137,6 @@ IPC_STRUCT_TRAITS_MEMBER(service_worker_version_id) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(extensions::mojom::DispatchEventParams) - // If this event is for a service worker, then this is the worker thread - // id. Otherwise, this is 0. - IPC_STRUCT_TRAITS_MEMBER(worker_thread_id) - - // The id of the extension to dispatch the event to. - IPC_STRUCT_TRAITS_MEMBER(extension_id) - - // The name of the event to dispatch. - IPC_STRUCT_TRAITS_MEMBER(event_name) - - // The id of the event for use in the EventAck response message. - IPC_STRUCT_TRAITS_MEMBER(event_id) - - // Whether or not the event is part of a user gesture. - IPC_STRUCT_TRAITS_MEMBER(is_user_gesture) - - // Additional filtering info for the event. - IPC_STRUCT_TRAITS_MEMBER(filtering_info) -IPC_STRUCT_TRAITS_END() - // Struct containing information about the sender of connect() calls that // originate from a tab. IPC_STRUCT_BEGIN(ExtensionMsg_TabConnectionInfo) @@ -275,14 +253,6 @@ IPC_STRUCT_TRAITS_MEMBER(serialization_format) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(extensions::EventFilteringInfo) - IPC_STRUCT_TRAITS_MEMBER(url) - IPC_STRUCT_TRAITS_MEMBER(service_type) - IPC_STRUCT_TRAITS_MEMBER(instance_id) - IPC_STRUCT_TRAITS_MEMBER(window_type) - IPC_STRUCT_TRAITS_MEMBER(window_exposed_by_default) -IPC_STRUCT_TRAITS_END() - // Singly-included section for custom IPC traits. #ifndef INTERNAL_EXTENSIONS_COMMON_EXTENSION_MESSAGES_H_ #define INTERNAL_EXTENSIONS_COMMON_EXTENSION_MESSAGES_H_ @@ -310,13 +280,6 @@ // Messages sent from the browser to the renderer: -// Sent to the renderer to dispatch an event to an extension. -// Note: |event_args| is separate from the params to avoid having the message -// take ownership. -IPC_MESSAGE_CONTROL2(ExtensionMsg_DispatchEvent, - extensions::mojom::DispatchEventParams /* params */, - base::ListValue /* event_args */) - // The browser's response to the ExtensionMsg_WakeEventPage IPC. IPC_MESSAGE_CONTROL2(ExtensionMsg_WakeEventPageResponse, int /* request_id */,
diff --git a/extensions/common/mojom/event_dispatcher.mojom b/extensions/common/mojom/event_dispatcher.mojom index 90f9677e..611d762 100644 --- a/extensions/common/mojom/event_dispatcher.mojom +++ b/extensions/common/mojom/event_dispatcher.mojom
@@ -18,9 +18,6 @@ mojo_base.mojom.DeprecatedListValue event_args); }; -// Typemapped to extensions::EventFilteringInfo. -// TODO(yochio): Convert extensions::EventFilteringInfo usage to -// extensions::mojom::EventFilteringInfo (https://crbug.com/1222550) struct EventFilteringInfo { url.mojom.Url? url;
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc index 22bf85b..0ae7b02 100644 --- a/extensions/renderer/dispatcher.cc +++ b/extensions/renderer/dispatcher.cc
@@ -32,7 +32,6 @@ #include "extensions/common/api/messaging/message.h" #include "extensions/common/constants.h" #include "extensions/common/cors_util.h" -#include "extensions/common/event_filtering_info_type_converters.h" #include "extensions/common/extension.h" #include "extensions/common/extension_api.h" #include "extensions/common/extension_features.h" @@ -1302,6 +1301,11 @@ void Dispatcher::DispatchEvent(mojom::DispatchEventParamsPtr params, base::Value event_args) { + if (params->worker_thread_id != kMainThreadId) { + WorkerThreadDispatcher::Get()->DispatchEvent(std::move(params), + std::move(event_args)); + return; + } content::RenderFrame* background_frame = ExtensionFrameHelper::GetBackgroundPageFrame(params->extension_id); @@ -1326,7 +1330,7 @@ DispatchEventHelper(params->extension_id, params->event_name, base::Value::AsListValue(event_args), - mojom::EventFilteringInfo::From(params->filtering_info)); + std::move(params->filtering_info)); if (background_frame) { // Tell the browser process when an event has been dispatched with a lazy
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h index e2cb9769..6b03612 100644 --- a/extensions/renderer/dispatcher.h +++ b/extensions/renderer/dispatcher.h
@@ -284,6 +284,8 @@ void OnDispatchOnDisconnect(int worker_thread_id, const PortId& port_id, const std::string& error_message); + + // mojom::EventDispatcher implementation. void DispatchEvent(mojom::DispatchEventParamsPtr params, base::Value event_args) override; @@ -381,8 +383,9 @@ // it is dependent on other messages sent on other associated channels. mojo::AssociatedReceiver<mojom::Renderer> receiver_; - // Extensions Dipsatch receiver. This is an associated receiver because - // it is dependent on other messages sent on other associated channels. + // Extensions mojom::EventDispatcher receiver. This is an associated receiver + // because it is dependent on other messages sent on other associated + // channels. mojo::AssociatedReceiver<mojom::EventDispatcher> dispatcher_; // Used to hold a service worker information which is ready to execute but the
diff --git a/extensions/renderer/worker_thread_dispatcher.cc b/extensions/renderer/worker_thread_dispatcher.cc index 3486329..b55177d1 100644 --- a/extensions/renderer/worker_thread_dispatcher.cc +++ b/extensions/renderer/worker_thread_dispatcher.cc
@@ -16,7 +16,6 @@ #include "content/public/renderer/render_thread.h" #include "content/public/renderer/worker_thread.h" #include "extensions/common/constants.h" -#include "extensions/common/event_filtering_info_type_converters.h" #include "extensions/common/extension_features.h" #include "extensions/common/extension_messages.h" #include "extensions/common/mojom/event_dispatcher.mojom.h" @@ -33,8 +32,6 @@ namespace { -base::LazyInstance<WorkerThreadDispatcher>::DestructorAtExit - g_worker_thread_dispatcher_instance = LAZY_INSTANCE_INITIALIZER; base::LazyInstance<base::ThreadLocalPointer<extensions::ServiceWorkerData>>:: DestructorAtExit g_data_tls = LAZY_INSTANCE_INITIALIZER; @@ -122,11 +119,12 @@ } // namespace -WorkerThreadDispatcher::WorkerThreadDispatcher() {} -WorkerThreadDispatcher::~WorkerThreadDispatcher() {} +WorkerThreadDispatcher::WorkerThreadDispatcher() = default; +WorkerThreadDispatcher::~WorkerThreadDispatcher() = default; WorkerThreadDispatcher* WorkerThreadDispatcher::Get() { - return g_worker_thread_dispatcher_instance.Pointer(); + static base::NoDestructor<WorkerThreadDispatcher> dispatcher; + return dispatcher.get(); } void WorkerThreadDispatcher::Init(content::RenderThread* render_thread) { @@ -162,7 +160,6 @@ bool WorkerThreadDispatcher::HandlesMessageOnWorkerThread( const IPC::Message& message) { return message.type() == ExtensionMsg_ResponseWorker::ID || - message.type() == ExtensionMsg_DispatchEvent::ID || message.type() == ExtensionMsg_DispatchOnConnect::ID || message.type() == ExtensionMsg_DeliverMessage::ID || message.type() == ExtensionMsg_DispatchOnDisconnect::ID || @@ -186,6 +183,14 @@ Dispatcher::GetWorkerScriptContextSet()); } +// static +void WorkerThreadDispatcher::DispatchEventOnWorkerThread( + mojom::DispatchEventParamsPtr params, + base::Value event_args) { + auto* dispatcher = WorkerThreadDispatcher::Get(); + dispatcher->DispatchEventHelper(std::move(params), std::move(event_args)); +} + bool WorkerThreadDispatcher::OnControlMessageReceived( const IPC::Message& message) { if (HandlesMessageOnWorkerThread(message)) { @@ -305,7 +310,6 @@ bool handled = true; IPC_BEGIN_MESSAGE_MAP(WorkerThreadDispatcher, message) IPC_MESSAGE_HANDLER(ExtensionMsg_ResponseWorker, OnResponseWorker) - IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchEvent, OnDispatchEvent) IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnConnect, OnDispatchOnConnect) IPC_MESSAGE_HANDLER(ExtensionMsg_DeliverMessage, OnDeliverMessage) IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnDisconnect, @@ -353,32 +357,48 @@ error); } -void WorkerThreadDispatcher::OnDispatchEvent( - const mojom::DispatchEventParams& params, - const base::ListValue& event_args) { +void WorkerThreadDispatcher::DispatchEventHelper( + mojom::DispatchEventParamsPtr params, + base::Value event_args) { + DCHECK_EQ(params->worker_thread_id, content::WorkerThread::GetCurrentId()); + ServiceWorkerData* data = g_data_tls.Pointer()->Get(); - DCHECK(data); + + // If the worker state was already destroyed via + // Dispatcher::WillDestroyServiceWorkerContextOnWorkerThread, then + // drop this mojo event. See https://crbug.com/1008143 for details. + if (!data) + return; ScriptContext* script_context = data->context(); // Note |scoped_extension_interaction| requires a HandleScope. v8::Isolate* isolate = script_context->isolate(); v8::HandleScope handle_scope(isolate); std::unique_ptr<InteractionProvider::Scope> scoped_extension_interaction; - if (params.is_user_gesture) { + if (params->is_user_gesture) { scoped_extension_interaction = ExtensionInteractionProvider::Scope::ForWorker( script_context->v8_context()); } - mojom::EventFilteringInfoPtr filtering_info = - mojom::EventFilteringInfo::From(params.filtering_info); + data->bindings_system()->DispatchEventInContext( - params.event_name, &event_args, filtering_info, data->context()); + params->event_name, &base::Value::AsListValue(event_args), + std::move(params->filtering_info), data->context()); const int worker_thread_id = content::WorkerThread::GetCurrentId(); Send(new ExtensionHostMsg_EventAckWorker(data->context()->GetExtensionID(), data->service_worker_version_id(), - worker_thread_id, params.event_id)); + worker_thread_id, params->event_id)); } +void WorkerThreadDispatcher::DispatchEvent(mojom::DispatchEventParamsPtr params, + base::Value event_args) { + DCHECK(!worker_thread_util::IsWorkerThread()); + const int worker_thread_id = params->worker_thread_id; + PostTaskToWorkerThread( + worker_thread_id, + base::BindOnce(&WorkerThreadDispatcher::DispatchEventOnWorkerThread, + std::move(params), std::move(event_args))); +} void WorkerThreadDispatcher::OnDispatchOnConnect( int worker_thread_id, const PortId& target_port_id,
diff --git a/extensions/renderer/worker_thread_dispatcher.h b/extensions/renderer/worker_thread_dispatcher.h index 6e42ee9b..9110c7b7 100644 --- a/extensions/renderer/worker_thread_dispatcher.h +++ b/extensions/renderer/worker_thread_dispatcher.h
@@ -50,7 +50,8 @@ // worker thread (this TODO formerly referred to content::ThreadSafeSender // which no longer exists). class WorkerThreadDispatcher : public content::RenderThreadObserver, - public IPC::Sender { + public IPC::Sender, + public mojom::EventDispatcher { public: WorkerThreadDispatcher(); @@ -150,10 +151,16 @@ // the IO thread. mojom::EventRouter* GetEventRouterOnIO(); + // Mojo interface implementation, called from the main thread. + void DispatchEvent(mojom::DispatchEventParamsPtr params, + base::Value event_args) override; + private: static bool HandlesMessageOnWorkerThread(const IPC::Message& message); static void ForwardIPC(int worker_thread_id, const IPC::Message& message); static void UpdateBindingsOnWorkerThread(const ExtensionId& extension_id); + static void DispatchEventOnWorkerThread(mojom::DispatchEventParamsPtr params, + base::Value event_args); void OnMessageReceivedOnWorkerThread(int worker_thread_id, const IPC::Message& message); @@ -166,8 +173,6 @@ bool succeeded, const base::ListValue& response, const std::string& error); - void OnDispatchEvent(const mojom::DispatchEventParams& params, - const base::ListValue& event_args); void OnValidateMessagePort(int worker_thread_id, const PortId& id); void OnDispatchOnConnect(int worker_thread_id, const PortId& target_port_id, @@ -181,6 +186,9 @@ const PortId& port_id, const std::string& error_message); + void DispatchEventHelper(mojom::DispatchEventParamsPtr params, + base::Value event_args); + // IPC sender. Belongs to the render thread, but thread safe. scoped_refptr<IPC::SyncMessageFilter> message_filter_;
diff --git a/gpu/command_buffer/service/common_decoder.cc b/gpu/command_buffer/service/common_decoder.cc index 1dc657ca..6edc324 100644 --- a/gpu/command_buffer/service/common_decoder.cc +++ b/gpu/command_buffer/service/common_decoder.cc
@@ -128,6 +128,13 @@ return true; } +bool CommonDecoder::Bucket::OffsetSizeValid(size_t offset, size_t size) const { + size_t end = 0; + if (!base::CheckAdd<size_t>(offset, size).AssignIfValid(&end)) + return false; + return end <= size_; +} + CommonDecoder::CommonDecoder(DecoderClient* client, CommandBufferServiceBase* command_buffer_service) : command_buffer_service_(command_buffer_service),
diff --git a/gpu/command_buffer/service/common_decoder.h b/gpu/command_buffer/service/common_decoder.h index 8096224..3f96002 100644 --- a/gpu/command_buffer/service/common_decoder.h +++ b/gpu/command_buffer/service/common_decoder.h
@@ -11,16 +11,17 @@ #include <map> #include <memory> #include <string> +#include <vector> #include "base/memory/raw_ptr.h" #include "gpu/command_buffer/common/buffer.h" #include "gpu/command_buffer/common/cmd_buffer_common.h" -#include "gpu/command_buffer/service/async_api_interface.h" +#include "gpu/command_buffer/common/constants.h" #include "gpu/gpu_export.h" // Forwardly declare a few GL types to avoid including GL header files. -typedef int GLsizei; -typedef int GLint; +using GLsizei = int; +using GLint = int; namespace gfx { class ColorSpace; @@ -35,9 +36,9 @@ // o3d/gl2 command buffer decoder. class GPU_EXPORT CommonDecoder { public: - typedef error::Error Error; + using Error = error::Error; - static const unsigned int kMaxStackDepth = 32; + static constexpr unsigned int kMaxStackDepth = 32; // A bucket is a buffer to help collect memory across a command buffer. When // creating a command buffer implementation of an existing API, sometimes that @@ -107,12 +108,7 @@ std::vector<GLint>* _length); private: - bool OffsetSizeValid(size_t offset, size_t size) const { - size_t end = 0; - if (!base::CheckAdd<size_t>(offset, size).AssignIfValid(&end)) - return false; - return end <= size_; - } + bool OffsetSizeValid(size_t offset, size_t size) const; size_t size_; ::std::unique_ptr<int8_t[]> data_; @@ -225,11 +221,11 @@ raw_ptr<DecoderClient> client_; size_t max_bucket_size_; - typedef std::map<uint32_t, std::unique_ptr<Bucket>> BucketMap; + using BucketMap = std::map<uint32_t, std::unique_ptr<Bucket>>; BucketMap buckets_; - typedef Error (CommonDecoder::*CmdHandler)(uint32_t immediate_data_size, - const volatile void* data); + using CmdHandler = Error (CommonDecoder::*)(uint32_t immediate_data_size, + const volatile void* data); // A struct to hold info about each command. struct CommandInfo {
diff --git a/gpu/command_buffer/service/shared_image_backing_d3d.cc b/gpu/command_buffer/service/shared_image_backing_d3d.cc index 2e2c33e..5050ba75 100644 --- a/gpu/command_buffer/service/shared_image_backing_d3d.cc +++ b/gpu/command_buffer/service/shared_image_backing_d3d.cc
@@ -594,6 +594,13 @@ WGPUDevice device, WGPUBackendType backend_type) { #if BUILDFLAG(USE_DAWN) +#if BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES) + if (backend_type == WGPUBackendType_OpenGLES) { + return std::make_unique<SharedImageRepresentationDawnEGLImage>( + ProduceGLTexturePassthrough(manager, tracker), manager, this, tracker, + device); + } +#endif const viz::ResourceFormat viz_resource_format = format(); const WGPUTextureFormat wgpu_format = viz::ToWGPUFormat(viz_resource_format); if (wgpu_format == WGPUTextureFormat_Undefined) { @@ -610,16 +617,6 @@ texture_descriptor.mipLevelCount = 1; texture_descriptor.sampleCount = 1; -#if BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES) - if (backend_type == WGPUBackendType_OpenGLES) { - // EGLImage textures do not support sampling, at the moment. - texture_descriptor.usage &= ~WGPUTextureUsage_TextureBinding; - return std::make_unique<SharedImageRepresentationDawnEGLImage>( - ProduceGLTexturePassthrough(manager, tracker), manager, this, tracker, - device, texture_descriptor); - } -#endif - // We need to have internal usages of CopySrc for copies and // RenderAttachment for clears. WGPUDawnTextureInternalUsageDescriptor internalDesc = {};
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_common.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_common.cc index e6915d9..b57aa8b 100644 --- a/gpu/command_buffer/service/shared_image_backing_factory_gl_common.cc +++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_common.cc
@@ -29,6 +29,7 @@ : use_passthrough_(gpu_preferences.use_passthrough_cmd_decoder && gles2::PassthroughCommandDecoderSupported()), workarounds_(workarounds), + use_webgpu_adapter_(gpu_preferences.use_webgpu_adapter), progress_reporter_(progress_reporter) { gl::GLApi* api = gl::g_current_gl_context; api->glGetIntegervFn(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_common.h b/gpu/command_buffer/service/shared_image_backing_factory_gl_common.h index f7546a70..de9f28f 100644 --- a/gpu/command_buffer/service/shared_image_backing_factory_gl_common.h +++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_common.h
@@ -77,6 +77,7 @@ bool texture_usage_angle_ = false; SharedImageBackingGLCommon::UnpackStateAttribs attribs_; GpuDriverBugWorkarounds workarounds_; + WebGPUAdapterName use_webgpu_adapter_ = WebGPUAdapterName::kDefault; // Used to notify the watchdog before a buffer allocation in case it takes // long.
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc index 89f22cf..b5cb4fe3 100644 --- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc +++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
@@ -142,7 +142,8 @@ return false; } // Needs interop factory - if ((usage & SHARED_IMAGE_USAGE_WEBGPU) || + if (((usage & SHARED_IMAGE_USAGE_WEBGPU) && + use_webgpu_adapter_ != WebGPUAdapterName::kCompat) || (usage & SHARED_IMAGE_USAGE_VIDEO_DECODE) || (usage & SHARED_IMAGE_USAGE_SCANOUT)) { return false;
diff --git a/gpu/command_buffer/service/shared_image_backing_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_gl_texture.cc index a58dd2af..58f69f0 100644 --- a/gpu/command_buffer/service/shared_image_backing_gl_texture.cc +++ b/gpu/command_buffer/service/shared_image_backing_gl_texture.cc
@@ -13,7 +13,6 @@ #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" -#include "components/viz/common/resources/resource_format_utils.h" #include "components/viz/common/resources/resource_sizes.h" #include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "gpu/command_buffer/common/shared_image_trace_utils.h" @@ -46,6 +45,7 @@ #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_version_info.h" #include "ui/gl/scoped_binders.h" +#include "ui/gl/scoped_make_current.h" #include "ui/gl/shared_gl_fence_egl.h" #include "ui/gl/trace_util.h" @@ -58,6 +58,10 @@ #include "gpu/command_buffer/service/shared_image_backing_factory_iosurface.h" #endif +#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES) +#include "gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h" +#endif + namespace gpu { namespace { @@ -186,6 +190,22 @@ MemoryTypeTracker* tracker, WGPUDevice device, WGPUBackendType backend_type) { +#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES) + if (backend_type == WGPUBackendType_OpenGLES) { + if (!image_egl_) { + CreateEGLImage(); + } + std::unique_ptr<SharedImageRepresentationGLTextureBase> texture; + if (IsPassthrough()) { + texture = ProduceGLTexturePassthrough(manager, tracker); + } else { + texture = ProduceGLTexture(manager, tracker); + } + return std::make_unique<SharedImageRepresentationDawnEGLImage>( + std::move(texture), manager, this, tracker, device); + } +#endif + if (!factory()) { DLOG(ERROR) << "No SharedImageFactory to create a dawn representation."; return nullptr; @@ -236,6 +256,25 @@ } } +void SharedImageBackingGLTexture::CreateEGLImage() { +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(USE_OZONE) + SharedContextState* shared_context_state = factory()->GetSharedContextState(); + ui::ScopedMakeCurrent smc(shared_context_state->context(), + shared_context_state->surface()); + auto image_np = base::MakeRefCounted<gl::GLImageNativePixmap>( + size(), viz::BufferFormat(format())); + image_np->InitializeFromTexture(GetGLServiceId()); + image_egl_ = image_np; + if (passthrough_texture_) { + passthrough_texture_->SetLevelImage(passthrough_texture_->target(), 0, + image_egl_.get()); + } else if (texture_) { + texture_->SetLevelImage(texture_->target(), 0, image_egl_.get(), + gles2::Texture::ImageState::BOUND); + } +#endif +} + void SharedImageBackingGLTexture::SetCompatibilitySwizzle( const gles2::Texture::CompatibilitySwizzle* swizzle) { if (!IsPassthrough())
diff --git a/gpu/command_buffer/service/shared_image_backing_gl_texture.h b/gpu/command_buffer/service/shared_image_backing_gl_texture.h index 0475952f..70a0371 100644 --- a/gpu/command_buffer/service/shared_image_backing_gl_texture.h +++ b/gpu/command_buffer/service/shared_image_backing_gl_texture.h
@@ -7,6 +7,10 @@ #include "gpu/command_buffer/service/shared_image_backing_gl_common.h" +namespace gl { +class GLImageEGL; +} + namespace gpu { // Implementation of SharedImageBacking that creates a GL Texture that is not @@ -34,6 +38,7 @@ GLenum GetGLTarget() const; GLuint GetGLServiceId() const; + void CreateEGLImage(); private: // SharedImageBacking: @@ -68,6 +73,7 @@ scoped_refptr<gles2::TexturePassthrough> passthrough_texture_; sk_sp<SkPromiseImageTexture> cached_promise_texture_; + scoped_refptr<gl::GLImageEGL> image_egl_; }; } // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_representation.h b/gpu/command_buffer/service/shared_image_representation.h index e1f2e81..b4972844 100644 --- a/gpu/command_buffer/service/shared_image_representation.h +++ b/gpu/command_buffer/service/shared_image_representation.h
@@ -201,6 +201,7 @@ protected: friend class SharedImageRepresentationSkiaGL; + friend class SharedImageRepresentationDawnEGLImage; friend class SharedImageRepresentationGLTextureImpl; // Can be overridden to handle clear state tracking when GL access begins or @@ -247,9 +248,6 @@ GetTexturePassthrough() = 0; gpu::TextureBase* GetTextureBase() override; - - protected: - friend class SharedImageRepresentationDawnEGLImage; }; class GPU_GLES2_EXPORT SharedImageRepresentationSkia
diff --git a/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.cc b/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.cc index 3c9bd0b..698543ee 100644 --- a/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.cc +++ b/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.cc
@@ -5,6 +5,7 @@ #include "gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h" #include "build/build_config.h" +#include "components/viz/common/resources/resource_format_utils.h" #include "gpu/command_buffer/service/texture_manager.h" #include <dawn/native/OpenGLBackend.h> @@ -23,17 +24,14 @@ namespace gpu { SharedImageRepresentationDawnEGLImage::SharedImageRepresentationDawnEGLImage( - std::unique_ptr<SharedImageRepresentationGLTexturePassthrough> - gl_representation, + std::unique_ptr<SharedImageRepresentationGLTextureBase> gl_representation, SharedImageManager* manager, SharedImageBacking* backing, MemoryTypeTracker* tracker, - WGPUDevice device, - const WGPUTextureDescriptor& texture_descriptor) + WGPUDevice device) : SharedImageRepresentationDawn(manager, backing, tracker), gl_representation_(std::move(gl_representation)), device_(device), - texture_descriptor_(texture_descriptor), dawn_procs_(dawn::native::GetProcs()) { DCHECK(device_); @@ -51,11 +49,29 @@ WGPUTexture SharedImageRepresentationDawnEGLImage::BeginAccess( WGPUTextureUsage usage) { gl_representation_->BeginAccess(ToSharedImageAccessGLMode(usage)); + WGPUTextureDescriptor texture_descriptor = {}; + texture_descriptor.nextInChain = nullptr; + texture_descriptor.format = viz::ToWGPUFormat(format()); + texture_descriptor.usage = WGPUTextureUsage_CopySrc | + WGPUTextureUsage_CopyDst | + WGPUTextureUsage_RenderAttachment; + texture_descriptor.dimension = WGPUTextureDimension_2D; + texture_descriptor.size = {static_cast<uint32_t>(size().width()), + static_cast<uint32_t>(size().height()), 1}; + texture_descriptor.mipLevelCount = 1; + texture_descriptor.sampleCount = 1; dawn::native::opengl::ExternalImageDescriptorEGLImage externalImageDesc; - externalImageDesc.cTextureDescriptor = &texture_descriptor_; - const auto& texture = gl_representation_->GetTexturePassthrough(); - externalImageDesc.image = - texture->GetLevelImage(texture->target(), 0u)->GetEGLImage(); + externalImageDesc.cTextureDescriptor = &texture_descriptor; + const gl::GLImage* image; + gpu::TextureBase* texture = gl_representation_->GetTextureBase(); + if (texture->GetType() == gpu::TextureBase::Type::kPassthrough) { + image = static_cast<gles2::TexturePassthrough*>(texture)->GetLevelImage( + texture->target(), 0u); + } else { + image = static_cast<gles2::Texture*>(texture)->GetLevelImage( + texture->target(), 0u); + } + externalImageDesc.image = image->GetEGLImage(); DCHECK(externalImageDesc.image); externalImageDesc.isInitialized = true; texture_ =
diff --git a/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h b/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h index e256f71..4fda76a5 100644 --- a/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h +++ b/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h
@@ -15,13 +15,11 @@ : public SharedImageRepresentationDawn { public: SharedImageRepresentationDawnEGLImage( - std::unique_ptr<SharedImageRepresentationGLTexturePassthrough> - gl_representation, + std::unique_ptr<SharedImageRepresentationGLTextureBase> gl_representation, SharedImageManager* manager, SharedImageBacking* backing, MemoryTypeTracker* tracker, - WGPUDevice device, - const WGPUTextureDescriptor& texture_descriptor); + WGPUDevice device); ~SharedImageRepresentationDawnEGLImage() override; private: @@ -29,10 +27,8 @@ void EndAccess() override; private: - std::unique_ptr<SharedImageRepresentationGLTexturePassthrough> - gl_representation_; + std::unique_ptr<SharedImageRepresentationGLTextureBase> gl_representation_; WGPUDevice device_; - WGPUTextureDescriptor texture_descriptor_; DawnProcTable dawn_procs_; WGPUTexture texture_ = nullptr; };
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc index bd85889f..772ce5f 100644 --- a/gpu/command_buffer/service/webgpu_decoder_impl.cc +++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -1504,7 +1504,8 @@ force_fallback_adapter = true; } - if (gr_context_type_ != GrContextType::kVulkan) { + if (gr_context_type_ != GrContextType::kVulkan && + use_webgpu_adapter_ != WebGPUAdapterName::kCompat) { #if BUILDFLAG(IS_LINUX) SendAdapterProperties(request_adapter_serial, -1, nullptr, "WebGPU on Linux requires command-line flag "
diff --git a/gpu/config/gpu_control_list.cc b/gpu/config/gpu_control_list.cc index 12b263e..b15351f 100644 --- a/gpu/config/gpu_control_list.cc +++ b/gpu/config/gpu_control_list.cc
@@ -146,21 +146,6 @@ if (op == kBetween) ref_version2.erase(ref_version2.begin()); } - - // No comparison should be run if two being-compared versions do not match - // Intel driver version schema. - if (Version::Compare({version[0]}, {"100"}, style) >= 0) { - if ((Version::Compare({ref_version1[0]}, {"100"}, style) < 0) || - (op == kBetween - ? Version::Compare({ref_version2[0]}, {"100"}, style) < 0 - : false)) - return false; - } else if ((Version::Compare({ref_version1[0]}, {"100"}, style) >= 0) || - (op == kBetween - ? Version::Compare({ref_version2[0]}, {"100"}, style) >= 0 - : false)) { - return false; - } } else if (schema == kVersionSchemaNvidiaDriver) { // The driver version we get from the os is "XX.XX.XXXA.BBCC", while the // workaround is of the form "ABB.CC". Drop the first two stanzas from the
diff --git a/gpu/config/gpu_control_list_entry_unittest.cc b/gpu/config/gpu_control_list_entry_unittest.cc index cdd3006..f68656f 100644 --- a/gpu/config/gpu_control_list_entry_unittest.cc +++ b/gpu/config/gpu_control_list_entry_unittest.cc
@@ -1129,7 +1129,7 @@ gpu_info.gpu.driver_version = "25.20.100.6000"; EXPECT_TRUE(entry.Contains(kOsWin, "", gpu_info)); gpu_info.gpu.driver_version = "24.20.99.6000"; - EXPECT_FALSE(entry.Contains(kOsWin, "", gpu_info)); + EXPECT_TRUE(entry.Contains(kOsWin, "", gpu_info)); gpu_info.gpu.driver_version = "24.20.101.6000"; EXPECT_FALSE(entry.Contains(kOsWin, "", gpu_info)); gpu_info.gpu.driver_version = "25.20.100.7000";
diff --git a/gpu/config/gpu_control_list_format.txt b/gpu/config/gpu_control_list_format.txt index 45b3854..5d8b1195 100644 --- a/gpu/config/gpu_control_list_format.txt +++ b/gpu/config/gpu_control_list_format.txt
@@ -108,11 +108,10 @@ // in that case, major is still numerical, but minor is lexical. // Only "driver_version" supports "(intel|nvidia)_driver" schema. // -// intel_driver schema versions have two forms: AA.BB.CC.DDDD (legacy) and -// AA.BB.CCC.DDDD (new). Of these, the last two fields are most relevant, and -// the first two can be ignored by setting them to 0. The two forms must not be -// mixed e.g. < 0.0.100.0 does not include legacy driver versions - the correct -// constraint is <= 0.0.99.9999. +// intel_driver schema versions have the form like "AA.BB.(CC|CCC).DDDD". The +// last two fields are most relevant, and the first two will be ignored by +// setting them to 0. Thus "0.0.(CC|CCC).DDDD" will be used in comparison +// following the general rule. // // FLOAT includes "op" "value", and "value2". "op" can be any of the // following values: "=", "<", "<=", ">", ">=", "any", "between". "value2" is
diff --git a/gpu/config/gpu_control_list_version_unittest.cc b/gpu/config/gpu_control_list_version_unittest.cc index e3d8675..0c633c4 100644 --- a/gpu/config/gpu_control_list_version_unittest.cc +++ b/gpu/config/gpu_control_list_version_unittest.cc
@@ -194,33 +194,22 @@ TEST_F(VersionTest, IntelDriverSchema) { { - // New drivers, AA.BB.CCC.DDDD Version info = {kLT, kNumerical, kIntelDriver, "25.20.100.6952", nullptr}; EXPECT_TRUE(info.Contains("0.0.100.6000")); EXPECT_FALSE(info.Contains("0.0.100.7000")); EXPECT_FALSE(info.Contains("0.0.200.6000")); EXPECT_TRUE(info.Contains("26.20.100.6000")); EXPECT_FALSE(info.Contains("24.20.100.7000")); + EXPECT_TRUE(info.Contains("23.20.16.5037")); } { - // Old drivers, AA.BB.CC.DDDD Version info = {kGT, kNumerical, kIntelDriver, "10.18.15.4256", nullptr}; EXPECT_TRUE(info.Contains("0.0.15.6000")); EXPECT_FALSE(info.Contains("0.0.15.4000")); EXPECT_TRUE(info.Contains("10.18.15.4279")); EXPECT_FALSE(info.Contains("15.40.15.4058")); - } - { - // Old driver versions cannot be compared against new driver versions. - Version info = {kLT, kNumerical, kIntelDriver, "0.0.100.0", nullptr}; - EXPECT_FALSE(info.Contains("23.20.16.4973")); - EXPECT_FALSE(info.Contains("20.19.15.4364")); - } - { - // Old driver versions can only be compared against old driver versions. - Version info = {kLE, kNumerical, kIntelDriver, "0.0.99.9999", nullptr}; - EXPECT_TRUE(info.Contains("23.20.16.4973")); - EXPECT_TRUE(info.Contains("20.19.15.4364")); + EXPECT_TRUE(info.Contains("26.20.100.6000")); + EXPECT_TRUE(info.Contains("26.20.100.4000")); } }
diff --git "a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json" index 234b920..904f1556 100644 --- "a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json" +++ "b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@ "project": "chromium" }, "builder_spec": { - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", "execution_mode": "COMPILE_AND_TEST", "legacy_chromium_config": { "apply_configs": [ @@ -53,6 +53,7 @@ "v.test_suite" ] }, - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", + "perf_dashboard_machine_group": "ChromiumGPUFYI", "recipe": "chromium" } \ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050reclient shadow\051/properties.json" index 93d6f64..2848ac3 100644 --- "a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050reclient shadow\051/properties.json" +++ "b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@ "project": "chromium" }, "builder_spec": { - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", "execution_mode": "COMPILE_AND_TEST", "legacy_chromium_config": { "apply_configs": [ @@ -53,6 +53,7 @@ "v.test_suite" ] }, - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", + "perf_dashboard_machine_group": "ChromiumGPUFYI", "recipe": "chromium" } \ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050dbg\051 \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050dbg\051 \050reclient shadow\051/properties.json" index a7db12b..35595f32 100644 --- "a/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050dbg\051 \050reclient shadow\051/properties.json" +++ "b/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050dbg\051 \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@ "project": "chromium" }, "builder_spec": { - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", "execution_mode": "COMPILE_AND_TEST", "legacy_chromium_config": { "apply_configs": [ @@ -53,6 +53,7 @@ "v.test_suite" ] }, - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", + "perf_dashboard_machine_group": "ChromiumGPUFYI", "recipe": "chromium" } \ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050reclient shadow\051/properties.json" index bbddbdce..9e97238 100644 --- "a/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050reclient shadow\051/properties.json" +++ "b/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@ "project": "chromium" }, "builder_spec": { - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", "execution_mode": "COMPILE_AND_TEST", "legacy_chromium_config": { "apply_configs": [ @@ -53,6 +53,7 @@ "v.test_suite" ] }, - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", + "perf_dashboard_machine_group": "ChromiumGPUFYI", "recipe": "chromium" } \ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/GPU FYI XR Win x64 Builder \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU FYI XR Win x64 Builder \050reclient shadow\051/properties.json" index 5735468180..1394828 100644 --- "a/infra/config/generated/builders/ci/GPU FYI XR Win x64 Builder \050reclient shadow\051/properties.json" +++ "b/infra/config/generated/builders/ci/GPU FYI XR Win x64 Builder \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@ "project": "chromium" }, "builder_spec": { - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", "execution_mode": "COMPILE_AND_TEST", "legacy_chromium_config": { "apply_configs": [ @@ -54,6 +54,7 @@ "v.test_suite" ] }, - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", + "perf_dashboard_machine_group": "ChromiumGPUFYI", "recipe": "chromium" } \ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/GPU Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json" index 39ae36fb..c9fda0f 100644 --- "a/infra/config/generated/builders/ci/GPU Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json" +++ "b/infra/config/generated/builders/ci/GPU Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@ "project": "chromium" }, "builder_spec": { - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", "execution_mode": "COMPILE_AND_TEST", "legacy_chromium_config": { "apply_configs": [ @@ -51,6 +51,7 @@ "v.test_suite" ] }, - "builder_group": "chromium.fyi", + "builder_group": "chromium.gpu.fyi", + "perf_dashboard_machine_group": "ChromiumGPUFYI", "recipe": "chromium" } \ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-11-x86-rel-compilator/properties.json b/infra/config/generated/builders/try/android-11-x86-rel-compilator/properties.json deleted file mode 100644 index e4f5494..0000000 --- a/infra/config/generated/builders/try/android-11-x86-rel-compilator/properties.json +++ /dev/null
@@ -1,22 +0,0 @@ -{ - "$build/goma": { - "enable_ats": true, - "jobs": 300, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org", - "use_luci_auth": true - }, - "$recipe_engine/resultdb/test_presentation": { - "column_keys": [], - "grouping_keys": [ - "status", - "v.test_suite" - ] - }, - "builder_group": "tryserver.chromium.android", - "orchestrator": { - "builder_group": "tryserver.chromium.android", - "builder_name": "android-11-x86-rel" - }, - "recipe": "chromium/compilator" -} \ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-11-x86-rel/properties.json b/infra/config/generated/builders/try/android-11-x86-rel/properties.json index f46f338d..d90599c 100644 --- a/infra/config/generated/builders/try/android-11-x86-rel/properties.json +++ b/infra/config/generated/builders/try/android-11-x86-rel/properties.json
@@ -1,7 +1,9 @@ { - "$build/chromium_orchestrator": { - "compilator": "android-11-x86-rel-compilator", - "compilator_watcher_git_revision": "7809a690bbd935bcb3b4d922e24cabe168aaabc8" + "$build/goma": { + "enable_ats": true, + "rpc_extra_params": "?prod", + "server_host": "goma.chromium.org", + "use_luci_auth": true }, "$recipe_engine/resultdb/test_presentation": { "column_keys": [], @@ -11,5 +13,5 @@ ] }, "builder_group": "tryserver.chromium.android", - "recipe": "chromium/orchestrator" + "recipe": "chromium_trybot" } \ No newline at end of file
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md index 140386f..f11c6ce 100644 --- a/infra/config/generated/cq-builders.md +++ b/infra/config/generated/cq-builders.md
@@ -453,9 +453,6 @@ by CQ. These are often used to test new configurations before they are added as required builders. -* [android-11-x86-rel](https://ci.chromium.org/p/chromium/builders/try/android-11-x86-rel) ([definition](https://cs.chromium.org/search?q=+file:/try.star$+""android-11-x86-rel"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""android-11-x86-rel"")) - * Experiment percentage: 10.0 - * [android-12-x64-rel](https://ci.chromium.org/p/chromium/builders/try/android-12-x64-rel) ([definition](https://cs.chromium.org/search?q=+file:/try.star$+""android-12-x64-rel"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""android-12-x64-rel"")) * Experiment percentage: 20.0
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg index d079cdd..2f32d0f2 100644 --- a/infra/config/generated/luci/commit-queue.cfg +++ b/infra/config/generated/luci/commit-queue.cfg
@@ -271,13 +271,6 @@ } builders { name: "chromium/try/android-11-x86-rel" - experiment_percentage: 10 - location_regexp: ".*" - location_regexp_exclude: ".+/[+]/docs/.+" - location_regexp_exclude: ".+/[+]/infra/config/.+" - } - builders { - name: "chromium/try/android-11-x86-rel-compilator" includable_only: true } builders {
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index 7d16238a..4190f75 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -7168,11 +7168,11 @@ ' }' ' }' ' },' - ' "builder_group": "chromium.fyi",' + ' "builder_group": "chromium.gpu.fyi",' ' "led_builder_is_bootstrapped": true,' ' "recipe": "chromium"' '}' - execution_timeout_secs: 10800 + execution_timeout_secs: 18000 build_numbers: YES service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com" experiments { @@ -7247,11 +7247,11 @@ ' }' ' }' ' },' - ' "builder_group": "chromium.fyi",' + ' "builder_group": "chromium.gpu.fyi",' ' "led_builder_is_bootstrapped": true,' ' "recipe": "chromium"' '}' - execution_timeout_secs: 10800 + execution_timeout_secs: 18000 build_numbers: YES service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com" experiments { @@ -7490,11 +7490,11 @@ ' }' ' }' ' },' - ' "builder_group": "chromium.fyi",' + ' "builder_group": "chromium.gpu.fyi",' ' "led_builder_is_bootstrapped": true,' ' "recipe": "chromium"' '}' - execution_timeout_secs: 10800 + execution_timeout_secs: 18000 build_numbers: YES service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com" experiments { @@ -7569,11 +7569,11 @@ ' }' ' }' ' },' - ' "builder_group": "chromium.fyi",' + ' "builder_group": "chromium.gpu.fyi",' ' "led_builder_is_bootstrapped": true,' ' "recipe": "chromium"' '}' - execution_timeout_secs: 10800 + execution_timeout_secs: 18000 build_numbers: YES service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com" experiments { @@ -7730,11 +7730,11 @@ ' }' ' }' ' },' - ' "builder_group": "chromium.fyi",' + ' "builder_group": "chromium.gpu.fyi",' ' "led_builder_is_bootstrapped": true,' ' "recipe": "chromium"' '}' - execution_timeout_secs: 10800 + execution_timeout_secs: 18000 build_numbers: YES service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com" experiments { @@ -8288,11 +8288,11 @@ ' }' ' }' ' },' - ' "builder_group": "chromium.fyi",' + ' "builder_group": "chromium.gpu.fyi",' ' "led_builder_is_bootstrapped": true,' ' "recipe": "chromium"' '}' - execution_timeout_secs: 10800 + execution_timeout_secs: 18000 build_numbers: YES service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com" experiments { @@ -24006,6 +24006,8 @@ ' "tools/android/avd/proto/creation/generic_android19.textpb",' ' "tools/android/avd/proto/creation/generic_android22.textpb",' ' "tools/android/avd/proto/creation/generic_android23.textpb",' + ' "tools/android/avd/proto/creation/generic_android24.textpb",' + ' "tools/android/avd/proto/creation/generic_playstore_android24.textpb",' ' "tools/android/avd/proto/creation/generic_android25.textpb",' ' "tools/android/avd/proto/creation/generic_playstore_android25.textpb",' ' "tools/android/avd/proto/creation/generic_android27.textpb",' @@ -26268,7 +26270,7 @@ ' "led_builder_is_bootstrapped": true,' ' "recipe": "chromium"' '}' - execution_timeout_secs: 10800 + execution_timeout_secs: 14400 build_numbers: YES service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" experiments { @@ -27119,6 +27121,14 @@ ' "sdk_package_name": "system-images;android-23;google_apis;x86"' ' },' ' {' + ' "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-24/google_apis/x86.yaml",' + ' "sdk_package_name": "system-images;android-24;google_apis;x86"' + ' },' + ' {' + ' "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-24/google_apis_playstore/x86.yaml",' + ' "sdk_package_name": "system-images;android-24;google_apis_playstore;x86"' + ' },' + ' {' ' "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-25/google_apis/x86.yaml",' ' "sdk_package_name": "system-images;android-25;google_apis;x86"' ' },' @@ -48053,11 +48063,12 @@ builders { name: "android-11-x86-rel" swarming_host: "chromium-swarm.appspot.com" - dimensions: "builder:android-11-x86-rel" - dimensions: "cores:4" + dimensions: "builderless:1" + dimensions: "cores:8" dimensions: "cpu:x86-64" dimensions: "os:Ubuntu-18.04" dimensions: "pool:luci.chromium.try" + dimensions: "ssd:0" exe { cipd_package: "infra/chromium/bootstrapper/${platform}" cipd_version: "latest" @@ -48086,98 +48097,7 @@ ' },' ' "builder_group": "tryserver.chromium.android",' ' "led_builder_is_bootstrapped": true,' - ' "recipe": "chromium/orchestrator"' - '}' - execution_timeout_secs: 14400 - expiration_secs: 7200 - grace_period { - seconds: 120 - } - caches { - name: "win_toolchain" - path: "win_toolchain" - } - build_numbers: YES - service_account: "chromium-orchestrator@chops-service-accounts.iam.gserviceaccount.com" - task_template_canary_percentage { - value: 5 - } - experiments { - key: "luci.recipes.use_python3" - value: 100 - } - resultdb { - enable: true - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "try_test_results" - test_results {} - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "gpu_try_test_results" - test_results { - predicate { - test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" - } - } - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "blink_web_tests_try_test_results" - test_results { - predicate { - test_id_regexp: "ninja://[^/]*blink_web_tests/.+" - } - } - } - history_options { - use_invocation_timestamp: true - } - } - description_html: "This is the orchestrator half of an orchestrator + compilator pair of builders. The compilator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/android-11-x86-rel-compilator\">android-11-x86-rel-compilator</a>." - } - builders { - name: "android-11-x86-rel-compilator" - swarming_host: "chromium-swarm.appspot.com" - dimensions: "builder:android-11-x86-rel-compilator" - dimensions: "cores:32" - dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" - dimensions: "pool:luci.chromium.try" - dimensions: "ssd:1" - exe { - cipd_package: "infra/chromium/bootstrapper/${platform}" - cipd_version: "latest" - cmd: "bootstrapper" - } - properties: - '{' - ' "$bootstrap/exe": {' - ' "exe": {' - ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' - ' "cipd_version": "refs/heads/main",' - ' "cmd": [' - ' "luciexe"' - ' ]' - ' }' - ' },' - ' "$bootstrap/properties": {' - ' "properties_file": "infra/config/generated/builders/try/android-11-x86-rel-compilator/properties.json",' - ' "top_level_project": {' - ' "ref": "refs/heads/main",' - ' "repo": {' - ' "host": "chromium.googlesource.com",' - ' "project": "chromium/src"' - ' }' - ' }' - ' },' - ' "builder_group": "tryserver.chromium.android",' - ' "led_builder_is_bootstrapped": true,' - ' "recipe": "chromium/compilator"' + ' "recipe": "chromium_trybot"' '}' execution_timeout_secs: 14400 expiration_secs: 7200 @@ -48229,7 +48149,6 @@ use_invocation_timestamp: true } } - description_html: "This is the compilator half of an orchestrator + compilator pair of builders. The orchestrator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/android-11-x86-rel\">android-11-x86-rel</a>." } builders { name: "android-12-x64-dbg"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index 2538cbfb..c023520c 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -2448,12 +2448,6 @@ id: "try" name: "Chromium CQ Console" builders { - name: "buildbucket/luci.chromium.try/android-11-x86-rel" - } - builders { - name: "buildbucket/luci.chromium.try/android-11-x86-rel-compilator" - } - builders { name: "buildbucket/luci.chromium.try/android-12-x64-rel" } builders { @@ -6720,35 +6714,6 @@ short_name: "64rel" } builders { - name: "buildbucket/luci.chromium.ci/GPU Win x64 Builder (dbg) (reclient shadow)" - category: "Windows" - } - builders { - name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 Builder (dbg) (reclient shadow)" - category: "Windows|Builder|Debug" - short_name: "x64" - } - builders { - name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 Builder (reclient shadow)" - category: "Windows|Builder|Release" - short_name: "x64" - } - builders { - name: "buildbucket/luci.chromium.ci/GPU FYI XR Win x64 Builder (reclient shadow)" - category: "Windows|Builder|XR" - short_name: "x64" - } - builders { - name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)" - category: "Windows|Builder|dx12vk" - short_name: "dbg" - } - builders { - name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)" - category: "Windows|Builder|dx12vk" - short_name: "rel" - } - builders { name: "buildbucket/luci.chromium.ci/Comparison Android (reclient)" category: "android" short_name: "cmp" @@ -8437,6 +8402,36 @@ short_name: "x64" } builders { + name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 Builder (dbg) (reclient shadow)" + category: "Windows|Builder|reclient|Debug" + short_name: "x64" + } + builders { + name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 Builder (reclient shadow)" + category: "Windows|Builder|reclient|Release" + short_name: "x64" + } + builders { + name: "buildbucket/luci.chromium.ci/GPU FYI XR Win x64 Builder (reclient shadow)" + category: "Windows|Builder|reclient|XR" + short_name: "x64" + } + builders { + name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)" + category: "Windows|Builder|reclient|dx12vk" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)" + category: "Windows|Builder|reclient|dx12vk" + short_name: "rel" + } + builders { + name: "buildbucket/luci.chromium.ci/GPU Win x64 Builder (dbg) (reclient shadow)" + category: "Windows|Builder|reclient|nonFYI|Debug" + short_name: "x64" + } + builders { name: "buildbucket/luci.chromium.ci/Win10 FYI x64 Release (AMD RX 5500 XT)" category: "Windows|10|x64|AMD" short_name: "rel" @@ -14535,9 +14530,6 @@ name: "buildbucket/luci.chromium.try/android-11-x86-rel" } builders { - name: "buildbucket/luci.chromium.try/android-11-x86-rel-compilator" - } - builders { name: "buildbucket/luci.chromium.try/android-12-x64-dbg" } builders { @@ -15664,9 +15656,6 @@ name: "buildbucket/luci.chromium.try/android-11-x86-rel" } builders { - name: "buildbucket/luci.chromium.try/android-11-x86-rel-compilator" - } - builders { name: "buildbucket/luci.chromium.try/android-12-x64-dbg" } builders {
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star index 2465c36b..17c5672b 100644 --- a/infra/config/lib/builders.star +++ b/infra/config/lib/builders.star
@@ -206,15 +206,6 @@ # setting ssd:0 dimension _EXCLUDE_BUILDERLESS_SSD_OS_CATEGORIES = [os_category.MAC] -def _chromium_tests_property(*, project_trigger_overrides): - chromium_tests = {} - - project_trigger_overrides = defaults.get_value("project_trigger_overrides", project_trigger_overrides) - if project_trigger_overrides: - chromium_tests["project_trigger_overrides"] = project_trigger_overrides - - return chromium_tests or None - def _goma_property(*, goma_backend, goma_debug, goma_enable_ats, goma_jobs): goma_properties = {} @@ -339,7 +330,6 @@ goma_jobs = None, list_view = args.COMPUTE, os = None, - project_trigger_overrides = None, pool = None, sheriff_rotations = None, xcode = None, @@ -396,7 +386,6 @@ xcode = args.DEFAULT, console_view_entry = None, list_view = args.DEFAULT, - project_trigger_overrides = args.DEFAULT, goma_backend = args.DEFAULT, goma_debug = args.DEFAULT, goma_enable_ats = args.DEFAULT, @@ -513,11 +502,6 @@ list_view: A string or a list of strings identifying the ID(s) of the list view(s) to add an entry to. Supports a module-level default that defaults to no list views. - project_trigger_overrides: a dict mapping the LUCI projects declared in - recipe BotSpecs to the LUCI project to use when triggering builders. - When this builder triggers another builder, if the BotSpec for that - builder has a LUCI project that is a key in this mapping, the - corresponding value will be used instead. goma_backend: a member of the `goma.backend` enum indicating the goma backend the builder should use. Will be incorporated into the '$build/goma' property. By default, considered None. @@ -684,12 +668,6 @@ if ssd != None: dimensions["ssd"] = str(int(ssd)) - chromium_tests = _chromium_tests_property( - project_trigger_overrides = project_trigger_overrides, - ) - if chromium_tests != None: - properties["$build/chromium_tests"] = chromium_tests - goma_enable_ats = defaults.get_value("goma_enable_ats", goma_enable_ats) # Enable ATS on linux by default.
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star index 40c02bb..24502a1 100644 --- a/infra/config/subprojects/chromium/ci.star +++ b/infra/config/subprojects/chromium/ci.star
@@ -13,9 +13,6 @@ bucket = "ci", build_numbers = True, cpu = cpu.X86_64, - project_trigger_overrides = branches.value({ - branches.NOT_MAIN: {"chromium": settings.project}, - }), triggered_by = ["chromium-gitiles-trigger"], )
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star index 891eb52..1f57629 100644 --- a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
@@ -138,6 +138,7 @@ category = "emulator|x86|rel", short_name = "N", ), + execution_timeout = 4 * time.hour, goma_backend = None, reclient_instance = rbe_instance.DEFAULT, reclient_jobs = rbe_jobs.DEFAULT,
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star index fd404edd..dcdb2ed 100644 --- a/infra/config/subprojects/chromium/ci/chromium.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -82,18 +82,6 @@ kwargs.setdefault("os", os.MAC_DEFAULT) return ci.builder(name = name, **kwargs) -def _fyi_gpu_windows_builder(*, name, **kwargs): - kwargs.setdefault("execution_timeout", ci.DEFAULT_EXECUTION_TIMEOUT) - kwargs.setdefault("builderless", True) - kwargs.setdefault("cores", 8) - kwargs.setdefault("os", os.WINDOWS_ANY) - kwargs.setdefault("service_account", ci.gpu.SERVICE_ACCOUNT) - kwargs.setdefault("pool", ci.gpu.POOL) - kwargs.setdefault("goma_backend", None) - kwargs.setdefault("reclient_jobs", rbe_jobs.LOW_JOBS_FOR_CI) - kwargs.setdefault("reclient_instance", rbe_instance.DEFAULT) - return ci.builder(name = name, **kwargs) - ci.builder( name = "Linux Viz", console_view_entry = consoles.console_view_entry( @@ -814,154 +802,6 @@ ) # End - Reclient migration, phase 2, block 1 shadow builders -_fyi_gpu_windows_builder( - name = "GPU FYI Win x64 Builder (reclient shadow)", - builder_spec = builder_config.builder_spec( - chromium_config = builder_config.chromium_config( - config = "chromium", - apply_configs = ["mb"], - build_config = builder_config.build_config.RELEASE, - target_bits = 64, - ), - gclient_config = builder_config.gclient_config( - config = "chromium", - apply_configs = [ - "chrome_internal", - "no_kaleidoscope", - "enable_reclient", - ], - ), - ), - console_view_entry = consoles.console_view_entry( - category = "Windows|Builder|Release", - short_name = "x64", - ), -) - -_fyi_gpu_windows_builder( - name = "GPU FYI Win x64 Builder (dbg) (reclient shadow)", - builder_spec = builder_config.builder_spec( - chromium_config = builder_config.chromium_config( - config = "chromium", - apply_configs = ["mb"], - build_config = builder_config.build_config.DEBUG, - target_bits = 64, - ), - gclient_config = builder_config.gclient_config( - config = "chromium", - apply_configs = [ - "chrome_internal", - "no_kaleidoscope", - "enable_reclient", - ], - ), - ), - console_view_entry = consoles.console_view_entry( - category = "Windows|Builder|Debug", - short_name = "x64", - ), -) - -_fyi_gpu_windows_builder( - name = "GPU FYI XR Win x64 Builder (reclient shadow)", - builder_spec = builder_config.builder_spec( - chromium_config = builder_config.chromium_config( - config = "chromium", - apply_configs = ["mb"], - build_config = builder_config.build_config.RELEASE, - target_bits = 64, - ), - gclient_config = builder_config.gclient_config( - config = "chromium", - apply_configs = [ - "chrome_internal", - "no_kaleidoscope", - "enable_reclient", - ], - ), - # This causes the builder to upload isolates to a location where - # Pinpoint can access them in addition to the usual isolate - # server. This is necessary because "Win10 FYI x64 Release XR - # perf (NVIDIA)", which is a child of this builder, uploads perf - # results, and Pinpoint may trigger additional builds on this - # builder during a bisect. - perf_isolate_upload = True, - ), - console_view_entry = consoles.console_view_entry( - category = "Windows|Builder|XR", - short_name = "x64", - ), -) - -_fyi_gpu_windows_builder( - name = "GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)", - builder_spec = builder_config.builder_spec( - chromium_config = builder_config.chromium_config( - config = "chromium", - apply_configs = ["mb"], - build_config = builder_config.build_config.RELEASE, - target_bits = 64, - ), - gclient_config = builder_config.gclient_config( - config = "chromium", - apply_configs = [ - "chrome_internal", - "no_kaleidoscope", - "enable_reclient", - ], - ), - ), - console_view_entry = consoles.console_view_entry( - category = "Windows|Builder|dx12vk", - short_name = "rel", - ), -) - -_fyi_gpu_windows_builder( - name = "GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)", - builder_spec = builder_config.builder_spec( - chromium_config = builder_config.chromium_config( - config = "chromium", - apply_configs = ["mb"], - build_config = builder_config.build_config.DEBUG, - target_bits = 64, - ), - gclient_config = builder_config.gclient_config( - config = "chromium", - apply_configs = [ - "chrome_internal", - "no_kaleidoscope", - "enable_reclient", - ], - ), - ), - console_view_entry = consoles.console_view_entry( - category = "Windows|Builder|dx12vk", - short_name = "dbg", - ), -) - -_fyi_gpu_windows_builder( - name = "GPU Win x64 Builder (dbg) (reclient shadow)", - builder_spec = builder_config.builder_spec( - chromium_config = builder_config.chromium_config( - config = "chromium", - apply_configs = ["mb"], - build_config = builder_config.build_config.DEBUG, - target_bits = 64, - ), - gclient_config = builder_config.gclient_config( - config = "chromium", - apply_configs = [ - "enable_reclient", - ], - ), - ), - console_view_entry = consoles.console_view_entry( - category = "Windows", - ), -) - ci.builder( name = "Win ASan Release (reclient shadow)", builderless = True,
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star index b0f06d5..1152256 100644 --- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -3,6 +3,8 @@ # found in the LICENSE file. """Definitions of builders in the chromium.gpu.fyi builder group.""" +load("//lib/args.star", "args") +load("//lib/builder_config.star", "builder_config") load("//lib/builders.star", "goma", "sheriff_rotations") load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs") load("//lib/consoles.star", "consoles") @@ -55,6 +57,14 @@ kwargs.setdefault("execution_timeout", ci.DEFAULT_EXECUTION_TIMEOUT) return ci.gpu.windows_builder(name = name, **kwargs) +def _gpu_fyi_windows_builder_shadow(*, name, **kwargs): + kwargs.setdefault("execution_timeout", 5 * time.hour) + kwargs.setdefault("goma_backend", None) + kwargs.setdefault("reclient_jobs", rbe_jobs.LOW_JOBS_FOR_CI) + kwargs.setdefault("reclient_instance", rbe_instance.DEFAULT) + kwargs.setdefault("sheriff_rotations", args.ignore_default(None)) + return gpu_fyi_windows_builder(name = name, **kwargs) + ci.thin_tester( name = "Android FYI Release (NVIDIA Shield TV)", console_view_entry = consoles.console_view_entry( @@ -626,3 +636,152 @@ short_name = "x64", ), ) + +_gpu_fyi_windows_builder_shadow( + name = "GPU FYI Win x64 Builder (reclient shadow)", + builder_spec = builder_config.builder_spec( + chromium_config = builder_config.chromium_config( + config = "chromium", + apply_configs = ["mb"], + build_config = builder_config.build_config.RELEASE, + target_bits = 64, + ), + gclient_config = builder_config.gclient_config( + config = "chromium", + apply_configs = [ + "chrome_internal", + "no_kaleidoscope", + "enable_reclient", + ], + ), + ), + console_view_entry = consoles.console_view_entry( + category = "Windows|Builder|reclient|Release", + short_name = "x64", + ), +) + +_gpu_fyi_windows_builder_shadow( + name = "GPU FYI Win x64 Builder (dbg) (reclient shadow)", + builder_spec = builder_config.builder_spec( + chromium_config = builder_config.chromium_config( + config = "chromium", + apply_configs = ["mb"], + build_config = builder_config.build_config.DEBUG, + target_bits = 64, + ), + gclient_config = builder_config.gclient_config( + config = "chromium", + apply_configs = [ + "chrome_internal", + "no_kaleidoscope", + "enable_reclient", + ], + ), + ), + console_view_entry = consoles.console_view_entry( + category = "Windows|Builder|reclient|Debug", + short_name = "x64", + ), +) + +_gpu_fyi_windows_builder_shadow( + name = "GPU FYI XR Win x64 Builder (reclient shadow)", + builder_spec = builder_config.builder_spec( + chromium_config = builder_config.chromium_config( + config = "chromium", + apply_configs = ["mb"], + build_config = builder_config.build_config.RELEASE, + target_bits = 64, + ), + gclient_config = builder_config.gclient_config( + config = "chromium", + apply_configs = [ + "chrome_internal", + "no_kaleidoscope", + "enable_reclient", + ], + ), + # This causes the builder to upload isolates to a location where + # Pinpoint can access them in addition to the usual isolate + # server. This is necessary because "Win10 FYI x64 Release XR + # perf (NVIDIA)", which is a child of this builder, uploads perf + # results, and Pinpoint may trigger additional builds on this + # builder during a bisect. + perf_isolate_upload = True, + ), + console_view_entry = consoles.console_view_entry( + category = "Windows|Builder|reclient|XR", + short_name = "x64", + ), +) + +_gpu_fyi_windows_builder_shadow( + name = "GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)", + builder_spec = builder_config.builder_spec( + chromium_config = builder_config.chromium_config( + config = "chromium", + apply_configs = ["mb"], + build_config = builder_config.build_config.RELEASE, + target_bits = 64, + ), + gclient_config = builder_config.gclient_config( + config = "chromium", + apply_configs = [ + "chrome_internal", + "no_kaleidoscope", + "enable_reclient", + ], + ), + ), + console_view_entry = consoles.console_view_entry( + category = "Windows|Builder|reclient|dx12vk", + short_name = "rel", + ), +) + +_gpu_fyi_windows_builder_shadow( + name = "GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)", + builder_spec = builder_config.builder_spec( + chromium_config = builder_config.chromium_config( + config = "chromium", + apply_configs = ["mb"], + build_config = builder_config.build_config.DEBUG, + target_bits = 64, + ), + gclient_config = builder_config.gclient_config( + config = "chromium", + apply_configs = [ + "chrome_internal", + "no_kaleidoscope", + "enable_reclient", + ], + ), + ), + console_view_entry = consoles.console_view_entry( + category = "Windows|Builder|reclient|dx12vk", + short_name = "dbg", + ), +) + +_gpu_fyi_windows_builder_shadow( + name = "GPU Win x64 Builder (dbg) (reclient shadow)", + builder_spec = builder_config.builder_spec( + chromium_config = builder_config.chromium_config( + config = "chromium", + apply_configs = ["mb"], + build_config = builder_config.build_config.DEBUG, + target_bits = 64, + ), + gclient_config = builder_config.gclient_config( + config = "chromium", + apply_configs = [ + "enable_reclient", + ], + ), + ), + console_view_entry = consoles.console_view_entry( + category = "Windows|Builder|reclient|nonFYI|Debug", + short_name = "x64", + ), +)
diff --git a/infra/config/subprojects/chromium/ci/chromium.packager.star b/infra/config/subprojects/chromium/ci/chromium.packager.star index bc612b0..f8d622d 100644 --- a/infra/config/subprojects/chromium/ci/chromium.packager.star +++ b/infra/config/subprojects/chromium/ci/chromium.packager.star
@@ -95,6 +95,8 @@ "tools/android/avd/proto/creation/generic_android19.textpb", "tools/android/avd/proto/creation/generic_android22.textpb", "tools/android/avd/proto/creation/generic_android23.textpb", + "tools/android/avd/proto/creation/generic_android24.textpb", + "tools/android/avd/proto/creation/generic_playstore_android24.textpb", "tools/android/avd/proto/creation/generic_android25.textpb", "tools/android/avd/proto/creation/generic_playstore_android25.textpb", "tools/android/avd/proto/creation/generic_android27.textpb", @@ -195,6 +197,14 @@ "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-23/google_apis/x86.yaml", }, { + "sdk_package_name": "system-images;android-24;google_apis;x86", + "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-24/google_apis/x86.yaml", + }, + { + "sdk_package_name": "system-images;android-24;google_apis_playstore;x86", + "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-24/google_apis_playstore/x86.yaml", + }, + { "sdk_package_name": "system-images;android-25;google_apis;x86", "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-25/google_apis/x86.yaml", },
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star index 069ebaf..fc7607a 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -32,23 +32,8 @@ name = "android-10-arm64-rel", ) -try_.orchestrator_builder( +try_.builder( name = "android-11-x86-rel", - compilator = "android-11-x86-rel-compilator", - # TODO(crbug.com/1137474): Enable it on branch after running on CQ - # branch_selector = branches.STANDARD_MILESTONE, - main_list_view = "try", - # TODO(crbug.com/1137474): Fully enable once it works fine - tryjob = try_.job( - experiment_percentage = 10, - ), -) - -try_.compilator_builder( - name = "android-11-x86-rel-compilator", - # TODO(crbug.com/1137474): Enable it on branch after running on CQ - # branch_selector = branches.STANDARD_MILESTONE, - main_list_view = "try", ) try_.builder(
diff --git a/infra/inclusive_language_presubmit_exempt_dirs.txt b/infra/inclusive_language_presubmit_exempt_dirs.txt index 411f0e5..ae20a2e 100644 --- a/infra/inclusive_language_presubmit_exempt_dirs.txt +++ b/infra/inclusive_language_presubmit_exempt_dirs.txt
@@ -604,8 +604,8 @@ third_party/rust/aquamarine/v0_1/crate/doc/js 1 1 third_party/rust/atty/v0_2/crate 3 2 third_party/rust/autocxx_bindgen/v0_59/crate/src 10 2 -third_party/rust/autocxx/v0_16/crate/.github/workflows 1 1 -third_party/rust/autocxx/v0_16/crate/src 1 1 +third_party/rust/autocxx/v0_17/crate/.github/workflows 1 1 +third_party/rust/autocxx/v0_17/crate/src 1 1 third_party/rust/bindgen/v0_59/crate/src 10 2 third_party/rust/cexpr/v0_6/crate/.github/workflows 2 1 third_party/rust/cfg_if/v1/crate/.github/workflows 4 1
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_provider.swift b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_provider.swift index 48049a5..82e5780b 100644 --- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_provider.swift +++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_provider.swift
@@ -7,12 +7,14 @@ /// A provider to provide the SwiftUI PopupView to Objective-C. This is /// necessary because Objective-C can't see SwiftUI types. -/// -/// - Parameters: -/// - model: The popup model to be used by the popup view. -/// - popupShouldSelfSize: Whether the popup should resize itself to fit its content. -/// - Returns: The hosting controller which embeds the popup view. @objcMembers public class OmniboxPopupViewProvider: NSObject { + + /// Returns a hosting controller embedding the popup view using the given model and settings. + /// + /// - Parameters: + /// - model: The popup model to be used by the popup view. + /// - popupShouldSelfSize: Whether the popup should resize itself to fit its content. + /// - Returns: The hosting controller which embeds the popup view. public static func makeViewController(withModel model: PopupModel, popupShouldSelfSize: Bool) -> UIViewController & ContentProviding {
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift b/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift index 556937b3e..0273f200 100644 --- a/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift +++ b/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift
@@ -120,13 +120,13 @@ ZStack { if self.isPressed || self.isHighlighted { Color.cr_tableRowViewHighlight } - Button(action: selectionHandler) { Rectangle().fill(.clear).contentShape(Rectangle()) } + Button(action: selectionHandler) { Color.clear.contentShape(Rectangle()) } .buttonStyle(PressedPreferenceKeyButtonStyle()) .onPreferenceChange(PressedPreferenceKey.self) { isPressed in self.isPressed = isPressed } - /// The content is in front of the button, for proper hit testing. + // The content is in front of the button, for proper hit testing. HStack(alignment: .center, spacing: 0) { HStack(alignment: .center, spacing: 0) { Spacer()
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/popup_view.swift b/ios/chrome/browser/ui/omnibox/popup/shared/popup_view.swift index 14e8b18..252d52f 100644 --- a/ios/chrome/browser/ui/omnibox/popup/shared/popup_view.swift +++ b/ios/chrome/browser/ui/omnibox/popup/shared/popup_view.swift
@@ -4,6 +4,34 @@ import SwiftUI +/// Utility which provides a way to treat the `simultaneousGesture` view modifier as a value. +struct SimultaneousGestureModifier<T: Gesture>: ViewModifier { + let gesture: T + let mask: GestureMask + + init(_ gesture: T, including mask: GestureMask = .all) { + self.gesture = gesture + self.mask = mask + } + + func body(content: Content) -> some View { + content.simultaneousGesture(gesture, including: mask) + } +} + +/// A view modifier which embeds the content in a `ScrollViewReader` and calls `action` +/// when the provided `value` changes. It is similar to the `onChange` view modifier, but provides +/// a `ScrollViewProxy` object in addition to the new state of `value` when calling `action`. +struct ScrollOnChangeModifier<V: Equatable>: ViewModifier { + @Binding var value: V + let action: (V, ScrollViewProxy) -> Void + func body(content: Content) -> some View { + ScrollViewReader { scrollProxy in + content.onChange(of: value) { newState in action(newState, scrollProxy) } + } + } +} + struct PopupView: View { enum Dimensions { static let matchListRowInsets = EdgeInsets(.zero) @@ -23,7 +51,7 @@ self.appearanceContainerType = appearanceContainerType } - var content: some View { + var listContent: some View { ForEach(Array(zip(model.sections.indices, model.sections)), id: \.0) { sectionIndex, section in @@ -34,7 +62,6 @@ match: match, isHighlighted: IndexPath(row: matchIndex, section: sectionIndex) == self.model.highlightedMatchIndexPath, - selectionHandler: { model.delegate?.autocompleteResultConsumer( model, didSelectRow: UInt(matchIndex), inSection: UInt(sectionIndex)) @@ -69,18 +96,24 @@ } var body: some View { + let listModifier = SimultaneousGestureModifier(DragGesture().onChanged { onDrag($0) }) + .concat(ScrollOnChangeModifier(value: $model.sections, action: onNewMatches)) + if shouldSelfSize { - SelfSizingList(bottomMargin: Dimensions.selfSizingListBottomMargin) { - content + SelfSizingList( + bottomMargin: Dimensions.selfSizingListBottomMargin, + listModifier: listModifier + ) { + listContent } emptySpace: { PopupEmptySpaceView() } .onAppear(perform: onAppear) } else { - List { - content - } - .onAppear(perform: onAppear) + List { listContent } + .modifier(listModifier) + .onAppear(perform: onAppear) + .ignoresSafeArea(.keyboard) } } @@ -90,9 +123,21 @@ appearanceContainerType ]) - listAppearance.bounces = false + if shouldSelfSize { + listAppearance.bounces = false + } } } + + func onDrag(_ dragValue: DragGesture.Value) { + model.highlightedMatchIndexPath = nil + model.delegate?.autocompleteResultConsumerDidScroll(model) + } + + func onNewMatches(matches: [PopupMatchSection], scrollProxy: ScrollViewProxy) { + // Scroll to the very top of the list. + scrollProxy.scrollTo(0, anchor: UnitPoint(x: 0, y: -.infinity)) + } } struct PopupView_Previews: PreviewProvider {
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/self_sizing_list.swift b/ios/chrome/browser/ui/omnibox/popup/shared/self_sizing_list.swift index 8b01425..0f482ec 100644 --- a/ios/chrome/browser/ui/omnibox/popup/shared/self_sizing_list.swift +++ b/ios/chrome/browser/ui/omnibox/popup/shared/self_sizing_list.swift
@@ -20,17 +20,25 @@ /// View which acts like a `List` but which also clips whatever empty space /// is available below the actual content to replace it with an arbitrary view. -struct SelfSizingList<Content: View, EmptySpace: View>: View { +struct SelfSizingList<Content: View, EmptySpace: View, ListModifier: ViewModifier>: View { let bottomMargin: CGFloat + var listModifier: ListModifier var content: () -> Content var emptySpace: () -> EmptySpace + /// - Parameters: + /// - bottomMargin: The bottom margin below the end of the list. + /// - listModifier: A `ViewModifier` to apply to the internal list. + /// - content: The content of the list. + /// - emptySpace: The view used to replace the area below the list. init( bottomMargin: CGFloat = 0, + listModifier: ListModifier, @ViewBuilder content: @escaping () -> Content, @ViewBuilder emptySpace: @escaping () -> EmptySpace ) { self.bottomMargin = bottomMargin + self.listModifier = listModifier self.content = content self.emptySpace = emptySpace } @@ -47,6 +55,7 @@ content() .anchorPreference(key: MaxYPreferenceKey.self, value: .bounds) { geometry[$0].maxY } } + .modifier(listModifier) .onPreferenceChange(MaxYPreferenceKey.self) { newMaxY in let newContentHeightEstimate = newMaxY.map { min($0 + bottomMargin, geometry.size.height)
diff --git a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm index 6c3c137..999f182 100644 --- a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm +++ b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
@@ -67,9 +67,12 @@ ItemTypeSearchHistorySuggestedItem, ItemTypeTextAccessoryNoImage, ItemTypeTextEditItem, + ItemTypeURLWithActivityIndicator, + ItemTypeURLWithActivityIndicatorStopped, ItemTypeURLWithTimestamp, ItemTypeURLWithSize, ItemTypeURLWithSupplementalText, + ItemTypeURLWithThirdRowText, ItemTypeURLWithBadgeImage, ItemTypeTextSettingsDetail, ItemTypeLinkFooter, @@ -598,6 +601,33 @@ item.URL = [[CrURL alloc] initWithGURL:GURL("https://photos.google.com/")]; item.badgeImage = [UIImage imageNamed:@"table_view_cell_check_mark"]; [model addItem:item toSectionWithIdentifier:SectionIdentifierURL]; + + item = + [[TableViewURLItem alloc] initWithType:ItemTypeURLWithActivityIndicator]; + item.title = @"Sent Request to Server"; + item.URL = [[CrURL alloc] initWithGURL:GURL("https://started.spinner.com/")]; + [model addItem:item toSectionWithIdentifier:SectionIdentifierURL]; + + item = [[TableViewURLItem alloc] + initWithType:ItemTypeURLWithActivityIndicatorStopped]; + item.title = @"Received Response from Server"; + item.URL = [[CrURL alloc] initWithGURL:GURL("https://stopped.spinner.com/")]; + [model addItem:item toSectionWithIdentifier:SectionIdentifierURL]; + + item = [[TableViewURLItem alloc] initWithType:ItemTypeURLWithThirdRowText]; + item.title = @"Web Channel with 3rd Row Text"; + item.URL = + [[CrURL alloc] initWithGURL:GURL("https://blog.google/products/chrome/")]; + item.thirdRowText = @"Unavailable"; + [model addItem:item toSectionWithIdentifier:SectionIdentifierURL]; + + item = [[TableViewURLItem alloc] initWithType:ItemTypeURLWithThirdRowText]; + item.title = @"Web Channel with 3rd Row Red Text"; + item.URL = + [[CrURL alloc] initWithGURL:GURL("https://blog.google/products/chrome/")]; + item.thirdRowText = @"Unavailable"; + item.thirdRowTextColor = UIColor.redColor; + [model addItem:item toSectionWithIdentifier:SectionIdentifierURL]; } #pragma mark - Actions @@ -667,6 +697,18 @@ base::mac::ObjCCastStrict<TableViewTabsSearchSuggestedHistoryCell>( cell); [searchHistoryCell updateHistoryResultsCount:7]; + } else if (itemType == ItemTypeURLWithActivityIndicator) { + TableViewURLCell* URLCell = + base::mac::ObjCCastStrict<TableViewURLCell>(cell); + [URLCell startAnimatingActivityIndicator]; + } else if (itemType == ItemTypeURLWithActivityIndicatorStopped) { + TableViewURLCell* URLCell = + base::mac::ObjCCastStrict<TableViewURLCell>(cell); + [URLCell startAnimatingActivityIndicator]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 4 * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + [URLCell stopAnimatingActivityIndicator]; + }); } return cell; }
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h index 9039fb8..e9ead03 100644 --- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h +++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h
@@ -25,6 +25,12 @@ @property(nonatomic, readwrite, copy) NSString* supplementalURLText; // Delimiter used to separate the URL hostname and the supplemental text. @property(nonatomic, readwrite, copy) NSString* supplementalURLTextDelimiter; +// Custom third row text. This is not shown if it is empty or if the second row +// is empty. +@property(nonatomic, readwrite, copy) NSString* thirdRowText; +// Third row text color, if it is shown. If nil, ChromeTableViewStyler's +// |detailTextColor| is used, otherwise a default color is used. +@property(nonatomic, strong) UIColor* thirdRowTextColor; // Detail text to be displayed instead of the URL. @property(nonatomic, strong) NSString* detailText; // Metadata text displayed at the trailing edge of the cell. @@ -59,6 +65,10 @@ // Optional metadata that is displayed at the trailing edge of the cell. @property(nonatomic, readonly, strong) UILabel* metadataLabel; +// Optional third row label. This is never used in place of the second row of +// text. +@property(nonatomic, readonly, strong) UILabel* thirdRowLabel; + // Unique identifier that matches with one URLItem. @property(nonatomic, strong) NSString* cellUniqueIdentifier; @@ -67,6 +77,14 @@ // that use TableViewURLCell. - (void)configureUILayout; +// Starts the animation of the activity indicator replacing the favicon. NO-OP +// if it is already running. +- (void)startAnimatingActivityIndicator; + +// Stops the animation of the activity indicator and puts favicon back in place. +// NO-OP if it is already stopped. +- (void)stopAnimatingActivityIndicator; + @end #endif // IOS_CHROME_BROWSER_UI_TABLE_VIEW_CELLS_TABLE_VIEW_URL_ITEM_H_
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm index eb35a03..d7eb985 100644 --- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm +++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
@@ -56,6 +56,7 @@ base::mac::ObjCCastStrict<TableViewURLCell>(tableCell); cell.titleLabel.text = [self titleLabelText]; cell.URLLabel.text = [self URLLabelText]; + cell.thirdRowLabel.text = self.thirdRowText; cell.faviconBadgeView.image = self.badgeImage; cell.metadataLabel.text = self.metadata; cell.cellUniqueIdentifier = self.uniqueIdentifier; @@ -65,8 +66,11 @@ cell.titleLabel.textColor = styler.cellTitleColor; if (styler.cellDetailColor) { cell.URLLabel.textColor = styler.cellDetailColor; + cell.thirdRowLabel.textColor = styler.cellDetailColor; cell.metadataLabel.textColor = styler.cellDetailColor; } + if (self.thirdRowTextColor) + cell.thirdRowLabel.textColor = self.thirdRowTextColor; [cell configureUILayout]; } @@ -132,6 +136,9 @@ @property(nonatomic, strong) UIStackView* horizontalStack; // Container View for the faviconView. @property(nonatomic, strong) FaviconContainerView* faviconContainerView; +// Activity indicator (spinner) used for indicating an in-flight request related +// to the item represented by this cell. +@property(nonatomic, strong) UIActivityIndicatorView* activityIndicatorView; @end @implementation TableViewURLCell @@ -146,6 +153,7 @@ _faviconBadgeView = [[TableViewURLCellFaviconBadgeView alloc] init]; _titleLabel = [[UILabel alloc] init]; _URLLabel = [[UILabel alloc] init]; + _thirdRowLabel = [[UILabel alloc] init]; _metadataLabel = [[UILabel alloc] init]; // Set font sizes using dynamic type. @@ -156,6 +164,11 @@ _URLLabel.adjustsFontForContentSizeCategory = YES; _URLLabel.textColor = [UIColor colorNamed:kTextSecondaryColor]; _URLLabel.hidden = YES; + _thirdRowLabel.font = + [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle]; + _thirdRowLabel.adjustsFontForContentSizeCategory = YES; + _thirdRowLabel.textColor = [UIColor colorNamed:kTextSecondaryColor]; + _thirdRowLabel.hidden = YES; _metadataLabel.font = [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle]; _metadataLabel.textColor = [UIColor colorNamed:kTextSecondaryColor]; @@ -164,7 +177,7 @@ // Use stack views to layout the subviews except for the favicon. UIStackView* verticalStack = [[UIStackView alloc] - initWithArrangedSubviews:@[ _titleLabel, _URLLabel ]]; + initWithArrangedSubviews:@[ _titleLabel, _URLLabel, _thirdRowLabel ]]; verticalStack.axis = UILayoutConstraintAxisVertical; [_metadataLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal]; @@ -256,6 +269,12 @@ } else { self.URLLabel.hidden = YES; } + if ([self.thirdRowLabel.text length] && !self.URLLabel.hidden) { + self.thirdRowLabel.hidden = NO; + } else { + // There shouldn't be a third row if the second row isn't even shown. + self.thirdRowLabel.hidden = YES; + } } - (void)prepareForReuse { @@ -265,6 +284,7 @@ self.horizontalStack.alignment = UIStackViewAlignmentFill; self.metadataLabel.hidden = YES; self.URLLabel.hidden = YES; + self.thirdRowLabel.hidden = YES; } - (void)setAccessibilityLabel:(NSString*)accessibilityLabel { @@ -279,6 +299,11 @@ accessibilityLabel = [NSString stringWithFormat:@"%@, %@", accessibilityLabel, self.URLLabel.text]; } + if (self.thirdRowLabel.text.length > 0) { + accessibilityLabel = + [NSString stringWithFormat:@"%@, %@", accessibilityLabel, + self.thirdRowLabel.text]; + } if (self.metadataLabel.text.length > 0) { accessibilityLabel = [NSString stringWithFormat:@"%@, %@", accessibilityLabel, @@ -307,4 +332,31 @@ return YES; } +- (void)startAnimatingActivityIndicator { + // It may be an edge case if the activity indicator is spinning when we don't + // expect it. But it's okay to leave indicator spinning instead of crashing. + if (self.activityIndicatorView != nil) { + return; + } + + self.activityIndicatorView = [[UIActivityIndicatorView alloc] init]; + UIActivityIndicatorView* activityView = self.activityIndicatorView; + activityView.translatesAutoresizingMaskIntoConstraints = NO; + [self.faviconContainerView addSubview:activityView]; + [NSLayoutConstraint activateConstraints:@[ + [activityView.centerXAnchor + constraintEqualToAnchor:self.faviconContainerView.centerXAnchor], + [activityView.centerYAnchor + constraintEqualToAnchor:self.faviconContainerView.centerYAnchor], + ]]; + [activityView startAnimating]; + activityView.backgroundColor = [UIColor whiteColor]; +} + +- (void)stopAnimatingActivityIndicator { + [self.activityIndicatorView stopAnimating]; + [self.activityIndicatorView removeFromSuperview]; + self.activityIndicatorView = nil; +} + @end
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm index fe9056e..100d3ec1 100644 --- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm +++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
@@ -122,3 +122,88 @@ EXPECT_NSEQ(base::SysUTF8ToNSString(kURL.host()), url_cell.titleLabel.text); EXPECT_NSEQ(kSupplementalURLText, url_cell.URLLabel.text); } + +// Tests that the third row text is shown when the other two rows are shown. +TEST_F(TableViewURLItemTest, ThirdRowText) { + NSString* const kTitle = @"Title"; + const GURL kURL("https://www.google.com"); + NSString* const kThirdRowText = @"third-row"; + + TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0]; + item.title = kTitle; + item.URL = [[CrURL alloc] initWithGURL:kURL]; + item.thirdRowText = kThirdRowText; + + id cell = [[[item cellClass] alloc] init]; + ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init]; + [item configureCell:cell withStyler:styler]; + ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]); + EXPECT_NSEQ(kThirdRowText, + base::mac::ObjCCast<TableViewURLCell>(cell).thirdRowLabel.text); + EXPECT_FALSE( + base::mac::ObjCCast<TableViewURLCell>(cell).thirdRowLabel.hidden); +} + +// Tests that the third row text is not shown when the second row is not shown. +TEST_F(TableViewURLItemTest, ThirdRowTextNotShown) { + NSString* const kTitle = @"Title"; + NSString* const kThirdRowText = @"third-row"; + + TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0]; + item.title = kTitle; + item.thirdRowText = kThirdRowText; + + id cell = [[[item cellClass] alloc] init]; + ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init]; + [item configureCell:cell withStyler:styler]; + ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]); + EXPECT_TRUE(base::mac::ObjCCast<TableViewURLCell>(cell).thirdRowLabel.hidden); +} + +// Tests that the third row text is shown in chosen color. +TEST_F(TableViewURLItemTest, ThirdRowTextColor) { + NSString* const kTitle = @"Title"; + const GURL kURL("https://www.google.com"); + NSString* const kThirdRowText = @"third-row"; + UIColor* const kExpectedColor = UIColor.greenColor; + + TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0]; + item.title = kTitle; + item.URL = [[CrURL alloc] initWithGURL:kURL]; + item.thirdRowText = kThirdRowText; + item.thirdRowTextColor = kExpectedColor; + + id cell = [[[item cellClass] alloc] init]; + ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init]; + styler.cellDetailColor = UIColor.redColor; + [item configureCell:cell withStyler:styler]; + ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]); + EXPECT_NSEQ( + kExpectedColor, + base::mac::ObjCCast<TableViewURLCell>(cell).thirdRowLabel.textColor); +} + +// Tests that the third row text is included in the accessibility label. +TEST_F(TableViewURLItemTest, ThirdRowTextAccessibilityLabel) { + NSString* const kTitle = @"Title"; + NSString* const kDetail = @"Detail"; + NSString* const kThirdRowText = @"third-row"; + NSString* const kMetadataText = @"Metadata"; + NSString* const kExpectedAccessibilityText = + @"Title, Detail, third-row, Metadata"; + + TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0]; + item.title = kTitle; + item.detailText = kDetail; + item.thirdRowText = kThirdRowText; + item.metadata = kMetadataText; + + id cell = [[[item cellClass] alloc] init]; + ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init]; + [item configureCell:cell withStyler:styler]; + ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]); + + UITableViewCell* tableViewCell = base::mac::ObjCCast<UITableViewCell>(cell); + tableViewCell.accessibilityLabel = nil; + EXPECT_NSEQ(kExpectedAccessibilityText, tableViewCell.accessibilityLabel); +}
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 index 2db5539c0..3ad24f0 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -e25eb1a60148ccd084207d96e564273e706aa661 \ No newline at end of file +7f608e9492078a1f22ed12561f1982e8113f960c \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 index 40f2141..9262cee 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -0ac249240a4f8f20d63d790aa2531fab0243e9ea \ No newline at end of file +690aeab35e6013694c0dd4a6666ee2ea7ff1c04a \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 index f4cb630..bd3fa1c 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -b721ea9d14be2f46ea4865fbcee2690728067611 \ No newline at end of file +fc8cfc68cafa4c9522ec5e018588567c1b4d38ac \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 index 1849f4ec..567c083 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -f5d910ab439f5bf4f465e43a1593c76db4178117 \ No newline at end of file +f2b0a90a2cb66e339a35669a7a026ce67d35e145 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 index cc6dca3..9b4ef34 100644 --- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -b4466c779fbff53fbd6c7f998faa9626cc2f2f13 \ No newline at end of file +eace1fc4900032677b24b3356ec525fef5c608c6 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 index 95fe566..cfa4f2b1 100644 --- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -c05399c014dfbfa186d200c0cfcbaa490e72bcaa \ No newline at end of file +6c21d5a51c8f31492e15766ad0e01a0f196263da \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 index ce673d0..2cf8eafb 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -d161c21584624770e57fe17f7bdb6028b484c05c \ No newline at end of file +82028e6abe7880a544f84c0652dc8980d02f8821 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 index 3e88de2..40243f78 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -7640e5dd1d544b5d66db7b6cd7b458ab7e5c4ba7 \ No newline at end of file +bbafa8e495b91bdc8a7766befd5895a8a222f0df \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 index 66eb0d12..36e1f5a 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -5bc5a9c070289fe262401290055b3203425fbc0f \ No newline at end of file +4bb09874b98ff7d953a469a5a04c5237cea048bf \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 index f63974f..ba6c018 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -de0a803543bd8e50eb264e25ea4287cd58bdaf1d \ No newline at end of file +4585849047939207e3fee1a12be51a2ee0626728 \ No newline at end of file
diff --git a/media/audio/cras/audio_manager_cras.cc b/media/audio/cras/audio_manager_cras.cc index c5e6d26c..6490f6e 100644 --- a/media/audio/cras/audio_manager_cras.cc +++ b/media/audio/cras/audio_manager_cras.cc
@@ -79,42 +79,178 @@ } } +// Checks if a system AEC with a specific group ID is flagged to be deactivated +// by the field trial. +bool IsSystemAecDeactivated(int aec_group_id) { + return base::GetFieldTrialParamByFeatureAsBool( + features::kCrOSSystemAECDeactivatedGroups, + base::NumberToString(aec_group_id), false); +} + +// Checks if the board with `aec_group_id` is flagged by the field trial to not +// allow using DSP-based AEC effect. +bool IsDspBasedAecDeactivated(int aec_group_id) { + return base::GetFieldTrialParamByFeatureAsBool( + features::kCrOSDspBasedAecDeactivatedGroups, + base::NumberToString(aec_group_id), false) || + !base::FeatureList::IsEnabled(features::kCrOSDspBasedAecAllowed); +} + +// Checks if the board with `aec_group_id` is flagged by the field trial to not +// allow using DSP-based NS effect. +bool IsDspBasedNsDeactivated(int aec_group_id) { + return base::GetFieldTrialParamByFeatureAsBool( + features::kCrOSDspBasedNsDeactivatedGroups, + base::NumberToString(aec_group_id), false) || + !base::FeatureList::IsEnabled(features::kCrOSDspBasedNsAllowed); +} + +// Checks if the board with `aec_group_id` is flagged by the field trial to not +// allow using DSP-based AGC effect. +bool IsDspBasedAgcDeactivated(int aec_group_id) { + return base::GetFieldTrialParamByFeatureAsBool( + features::kCrOSDspBasedAgcDeactivatedGroups, + base::NumberToString(aec_group_id), false) || + !base::FeatureList::IsEnabled(features::kCrOSDspBasedAgcAllowed); +} + +// Specifies which DSP-based effects are allowed based on media constraints and +// any finch field trials. +void SetAllowedDspBasedEffects(int aec_group_id, AudioParameters& params) { + int effects = params.effects(); + + // Allow AEC to be applied by CRAS on DSP if the AEC is active in CRAS and if + // using the AEC on DSP has not been deactivated by any field trials. + if ((effects & AudioParameters::ECHO_CANCELLER) && + !IsDspBasedAecDeactivated(aec_group_id)) { + effects = effects | AudioParameters::ALLOW_DSP_ECHO_CANCELLER; + } else { + effects = effects & ~AudioParameters::ALLOW_DSP_ECHO_CANCELLER; + } + + // Allow NS to be applied by CRAS on DSP if the NS is active in CRAS and if + // using the NS on DSP has not been deactivated by any field trials. + if ((effects & AudioParameters::NOISE_SUPPRESSION) && + !IsDspBasedNsDeactivated(aec_group_id)) { + effects = effects | AudioParameters::ALLOW_DSP_NOISE_SUPPRESSION; + } else { + effects = effects & ~AudioParameters::ALLOW_DSP_NOISE_SUPPRESSION; + } + + // Allow AGC to be applied by CRAS on DSP if the AGC is active in CRAS and if + // using the AGC on DSP has not been deactivated by any field trials. + if ((effects & AudioParameters::AUTOMATIC_GAIN_CONTROL) && + !IsDspBasedAgcDeactivated(aec_group_id)) { + effects = effects | AudioParameters::ALLOW_DSP_AUTOMATIC_GAIN_CONTROL; + } else { + effects = effects & ~AudioParameters::ALLOW_DSP_AUTOMATIC_GAIN_CONTROL; + } + + params.set_effects(effects); +} + + +// Collects flags values for whether, and in what way, the AEC, NS or AGC +// effects should be enforced in spite of them not being flagged as supported by +// the board. +void RetrieveSystemEffectFeatures(bool& enforce_system_aec, + bool& enforce_system_ns, + bool& enforce_system_agc, + bool& tuned_system_aec_allowed) { + const bool enforce_system_aec_ns_agc_feature = + base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecNsAgc); + const bool enforce_system_aec_ns_feature = + base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecNs); + const bool enforce_system_aec_agc_feature = + base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecAgc); + const bool enforce_system_aec_feature = + base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAec); + + enforce_system_aec = + enforce_system_aec_feature || enforce_system_aec_ns_agc_feature || + enforce_system_aec_ns_feature || enforce_system_aec_agc_feature; + enforce_system_ns = + enforce_system_aec_ns_agc_feature || enforce_system_aec_ns_feature; + enforce_system_agc = + enforce_system_aec_ns_agc_feature || enforce_system_aec_agc_feature; + + tuned_system_aec_allowed = + base::FeatureList::IsEnabled(features::kCrOSSystemAEC); +} + +AudioParameters AudioManagerCras::GetStreamParametersForSystem( + int user_buffer_size) { + AudioParameters params( + AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, + kDefaultSampleRate, user_buffer_size, + AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize, + limits::kMaxAudioBufferSize)); + + bool enforce_system_aec; + bool enforce_system_ns; + bool enforce_system_agc; + bool tuned_system_aec_allowed; + RetrieveSystemEffectFeatures(enforce_system_aec, enforce_system_ns, + enforce_system_agc, tuned_system_aec_allowed); + + // Activation of the system AEC. Allow experimentation with system AEC with + // all devices, but enable it by default on devices that actually support it. + params.set_effects(params.effects() | + AudioParameters::EXPERIMENTAL_ECHO_CANCELLER); + + // Rephrase the field aec_supported to properly reflect its meaning in this + // context (since it currently signals whether an CrAS APM with tuned settings + // is available). + // TODO(crbug.com/1307680): add unit tests and caching cras_util_ results. + const bool tuned_system_apm_available = cras_util_->CrasGetAecSupported(); + + // Don't use the system AEC if it is deactivated for this group ID. Also never + // activate NS nor AGC for this board if the AEC is not activated, since this + // will cause issues for the Browser AEC. + bool use_system_aec = + (tuned_system_apm_available && tuned_system_aec_allowed) || + enforce_system_aec; + + // TODO(hychao): query from CRAS + bool system_ns_supported = true; + bool system_agc_supported = true; + + int aec_group_id = cras_util_->CrasGetAecGroupId(); + if (!use_system_aec || IsSystemAecDeactivated(aec_group_id)) { + SetAllowedDspBasedEffects(aec_group_id, params); + return params; + } + + // Activation of the system AEC. + params.set_effects(params.effects() | AudioParameters::ECHO_CANCELLER); + + // Don't use system NS or AGC if the AEC has board-specific tunings. + if (!tuned_system_apm_available) { + // Activation of the system NS. + if (system_ns_supported || enforce_system_ns) { + params.set_effects(params.effects() | AudioParameters::NOISE_SUPPRESSION); + } + + // Activation of the system AGC. + if (system_agc_supported || enforce_system_agc) { + params.set_effects(params.effects() | + AudioParameters::AUTOMATIC_GAIN_CONTROL); + } + } + + SetAllowedDspBasedEffects(aec_group_id, params); + return params; +} + AudioParameters AudioManagerCras::GetInputStreamParameters( const std::string& device_id) { DCHECK(GetTaskRunner()->BelongsToCurrentThread()); int user_buffer_size = GetUserBufferSize(); - int buffer_size = + user_buffer_size = user_buffer_size ? user_buffer_size : kDefaultInputBufferSize; - AudioParameters params( - AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, - kDefaultSampleRate, buffer_size, - AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize, - limits::kMaxAudioBufferSize)); - - // Allow experimentation with system echo cancellation with all devices, - // but enable it by default on devices that actually support it. - params.set_effects(params.effects() | - AudioParameters::EXPERIMENTAL_ECHO_CANCELLER); - if (base::FeatureList::IsEnabled(features::kCrOSSystemAEC)) { - if (cras_util_->CrasGetAecSupported()) { - const int32_t aec_group_id = cras_util_->CrasGetAecGroupId(); - - // Check if the system AEC has a group ID which is flagged to be - // deactivated by the field trial. - const bool system_aec_deactivated = - base::GetFieldTrialParamByFeatureAsBool( - features::kCrOSSystemAECDeactivatedGroups, - base::NumberToString(aec_group_id), false); - - if (!system_aec_deactivated) { - params.set_effects(params.effects() | AudioParameters::ECHO_CANCELLER); - } - } - } - - return params; + return GetStreamParametersForSystem(user_buffer_size); } std::string AudioManagerCras::GetDefaultInputDeviceID() {
diff --git a/media/audio/cras/audio_manager_cras.h b/media/audio/cras/audio_manager_cras.h index da2302f..db042649 100644 --- a/media/audio/cras/audio_manager_cras.h +++ b/media/audio/cras/audio_manager_cras.h
@@ -46,6 +46,11 @@ bool IsDefault(const std::string& device_id, bool is_input) override; enum CRAS_CLIENT_TYPE GetClientType() override; + // Produces AudioParameters for the system, including audio processing + // capabilities tailored for the system, + AudioParameters GetStreamParametersForSystem( + int user_buffer_size); + protected: AudioParameters GetPreferredOutputStreamParameters( const std::string& output_device_id,
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn index e74c63a..5bab781 100644 --- a/media/base/BUILD.gn +++ b/media/base/BUILD.gn
@@ -73,6 +73,7 @@ "audio_power_monitor.h", "audio_processing.cc", "audio_processing.h", + "audio_processor_controls.h", "audio_pull_fifo.cc", "audio_pull_fifo.h", "audio_push_fifo.cc",
diff --git a/media/base/audio_processing.h b/media/base/audio_processing.h index 95fda4b1..ca80199 100644 --- a/media/base/audio_processing.h +++ b/media/base/audio_processing.h
@@ -9,7 +9,6 @@ #include "build/build_config.h" #include "media/base/media_export.h" -#include "third_party/abseil-cpp/absl/types/optional.h" namespace media { @@ -90,13 +89,6 @@ std::string ToString() const; }; -// This struct contains audio processing metrics that are reported by the audio -// service. -struct MEDIA_EXPORT AudioProcessingStats { - absl::optional<double> echo_return_loss; - absl::optional<double> echo_return_loss_enhancement; -}; - } // namespace media #endif // MEDIA_BASE_AUDIO_PROCESSING_H_
diff --git a/media/base/audio_processor_controls.h b/media/base/audio_processor_controls.h new file mode 100644 index 0000000..b429f803 --- /dev/null +++ b/media/base/audio_processor_controls.h
@@ -0,0 +1,40 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_AUDIO_PROCESSOR_CONTROLS_H_ +#define MEDIA_BASE_AUDIO_PROCESSOR_CONTROLS_H_ + +#include "base/callback.h" +#include "media/base/media_export.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace media { + +// Audio processing metrics that are reported by the audio service. +struct MEDIA_EXPORT AudioProcessingStats { + absl::optional<double> echo_return_loss; + absl::optional<double> echo_return_loss_enhancement; +}; + +// Interactions with the audio service. +class MEDIA_EXPORT AudioProcessorControls { + public: + using GetStatsCB = + base::OnceCallback<void(const media::AudioProcessingStats& stats)>; + + // Request the latest stats from the audio processor. Stats are returned + // asynchronously through |callback|. + virtual void GetStats(GetStatsCB callback) = 0; + + // Set preferred number of microphone channels. + virtual void SetPreferredNumCaptureChannels( + int32_t num_preferred_channels) = 0; + + protected: + virtual ~AudioProcessorControls() = default; +}; + +} // namespace media + +#endif // MEDIA_BASE_AUDIO_PROCESSOR_CONTROLS_H_
diff --git a/media/base/key_system_properties.h b/media/base/key_system_properties.h index 6eeb877..f80ba87 100644 --- a/media/base/key_system_properties.h +++ b/media/base/key_system_properties.h
@@ -82,7 +82,7 @@ std::vector<std::unique_ptr<KeySystemProperties>>; using GetSupportedKeySystemsCB = - base::OnceCallback<void(KeySystemPropertiesVector)>; + base::RepeatingCallback<void(KeySystemPropertiesVector)>; } // namespace media
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc index adf33338..d2a1916 100644 --- a/media/base/key_systems.cc +++ b/media/base/key_systems.cc
@@ -276,7 +276,6 @@ // Implementation of KeySystems interface. void UpdateIfNeeded(base::OnceClosure done_cb) override; - bool IsUpToDate() override; std::string GetBaseKeySystemName( const std::string& key_system) const override; bool IsSupportedKeySystem(const std::string& key_system) const override; @@ -323,7 +322,6 @@ KeySystemsImpl(); ~KeySystemsImpl() override; - bool IsUpdateNeeded(); void UpdateSupportedKeySystems(); void OnSupportedKeySystemsUpdated(KeySystemPropertiesVector key_systems); void ProcessSupportedKeySystems(KeySystemPropertiesVector key_systems); @@ -387,10 +385,6 @@ update_callbacks_.Notify(); } -bool KeySystemsImpl::IsUpdateNeeded() { - return GetMediaClient() && GetMediaClient()->IsKeySystemsUpdateNeeded(); -} - void KeySystemsImpl::UpdateSupportedKeySystems() { DCHECK(!is_updating_); is_updating_ = true; @@ -401,28 +395,18 @@ } GetMediaClient()->GetSupportedKeySystems( - base::BindOnce(&KeySystemsImpl::OnSupportedKeySystemsUpdated, - weak_factory_.GetWeakPtr())); + base::BindRepeating(&KeySystemsImpl::OnSupportedKeySystemsUpdated, + weak_factory_.GetWeakPtr())); } void KeySystemsImpl::UpdateIfNeeded(base::OnceClosure done_cb) { if (is_updating_) { + // The callback will be resolved in OnSupportedKeySystemsUpdated(). update_callbacks_.AddUnsafe(std::move(done_cb)); return; } - DCHECK(update_callbacks_.empty()); - if (!IsUpdateNeeded()) { - std::move(done_cb).Run(); - return; - } - - update_callbacks_.AddUnsafe(std::move(done_cb)); - UpdateSupportedKeySystems(); -} - -bool KeySystemsImpl::IsUpToDate() { - return !is_updating_ && !IsUpdateNeeded(); + std::move(done_cb).Run(); } SupportedCodecs KeySystemsImpl::GetCodecMaskForMimeType( @@ -483,7 +467,6 @@ KeySystemPropertiesVector key_systems) { DVLOG(1) << __func__; - DCHECK(is_updating_); is_updating_ = false; // Clear Key is always supported. @@ -550,6 +533,7 @@ const KeySystemProperties* KeySystemsImpl::GetKeySystemProperties( const std::string& key_system) const { + DCHECK(!is_updating_); for (const auto& entry : key_system_properties_map_) { const auto& base_key_system = entry.first; const auto* properties = entry.second.get();
diff --git a/media/base/key_systems.h b/media/base/key_systems.h index b13bf6d..7165a17 100644 --- a/media/base/key_systems.h +++ b/media/base/key_systems.h
@@ -35,9 +35,6 @@ // out of date. Calls the `done_cb` when done. virtual void UpdateIfNeeded(base::OnceClosure done_cb) = 0; - // Whether the list of available key systems is up to date. - virtual bool IsUpToDate() = 0; - // Gets the base key system name, e.g. "org.chromium.foo". virtual std::string GetBaseKeySystemName( const std::string& key_system) const = 0;
diff --git a/media/base/key_systems_unittest.cc b/media/base/key_systems_unittest.cc index cd4b792..7a9b977 100644 --- a/media/base/key_systems_unittest.cc +++ b/media/base/key_systems_unittest.cc
@@ -247,16 +247,11 @@ ~TestMediaClient() override; // MediaClient implementation. - bool IsKeySystemsUpdateNeeded() final; void GetSupportedKeySystems(GetSupportedKeySystemsCB cb) final; bool IsSupportedAudioType(const media::AudioType& type) final; bool IsSupportedVideoType(const media::VideoType& type) final; bool IsSupportedBitstreamAudioCodec(AudioCodec codec) final; - // Helper function to test the case where IsKeySystemsUpdateNeeded() is true - // after GetSupportedKeySystems() is called. - void SetKeySystemsUpdateNeeded(); - // Helper function to disable "kExternal" key system support so that we can // test the key system update case. void DisableExternalKeySystemSupport(); @@ -265,28 +260,21 @@ GetAudioRendererAlgorithmParameters(AudioParameters audio_parameters) final; private: - bool is_update_needed_ = true; + KeySystemPropertiesVector GetSupportedKeySystemsInternal(); + + GetSupportedKeySystemsCB get_supported_key_systems_cb_; bool supports_external_key_system_ = true; }; TestMediaClient::TestMediaClient() = default; TestMediaClient::~TestMediaClient() = default; -bool TestMediaClient::IsKeySystemsUpdateNeeded() { - return is_update_needed_; -} - void TestMediaClient::GetSupportedKeySystems(GetSupportedKeySystemsCB cb) { - DCHECK(is_update_needed_); - KeySystemPropertiesVector key_systems; + // Save the callback for future updates. + DCHECK(!get_supported_key_systems_cb_); + get_supported_key_systems_cb_ = cb; - key_systems.emplace_back(new AesKeySystemProperties(kUsesAes)); - - if (supports_external_key_system_) - key_systems.emplace_back(new ExternalKeySystemProperties()); - - is_update_needed_ = false; - std::move(cb).Run(std::move(key_systems)); + get_supported_key_systems_cb_.Run(GetSupportedKeySystemsInternal()); } bool TestMediaClient::IsSupportedAudioType(const media::AudioType& type) { @@ -301,12 +289,9 @@ return false; } -void TestMediaClient::SetKeySystemsUpdateNeeded() { - is_update_needed_ = true; -} - void TestMediaClient::DisableExternalKeySystemSupport() { supports_external_key_system_ = false; + get_supported_key_systems_cb_.Run(GetSupportedKeySystemsInternal()); } absl::optional<::media::AudioRendererAlgorithmParameters> @@ -315,6 +300,17 @@ return absl::nullopt; } +KeySystemPropertiesVector TestMediaClient::GetSupportedKeySystemsInternal() { + KeySystemPropertiesVector key_systems; + + key_systems.emplace_back(new AesKeySystemProperties(kUsesAes)); + + if (supports_external_key_system_) + key_systems.emplace_back(new ExternalKeySystemProperties()); + + return key_systems; +} + } // namespace class KeySystemsTest : public testing::Test { @@ -365,7 +361,6 @@ base::RunLoop run_loop; KeySystems::GetInstance()->UpdateIfNeeded(run_loop.QuitClosure()); run_loop.Run(); - ASSERT_TRUE(KeySystems::GetInstance()->IsUpToDate()); } ~KeySystemsTest() override { @@ -376,13 +371,11 @@ } void UpdateClientKeySystems() { - test_media_client_.SetKeySystemsUpdateNeeded(); test_media_client_.DisableExternalKeySystemSupport(); base::RunLoop run_loop; KeySystems::GetInstance()->UpdateIfNeeded(run_loop.QuitClosure()); run_loop.Run(); - ASSERT_TRUE(KeySystems::GetInstance()->IsUpToDate()); } typedef std::vector<std::string> CodecVector;
diff --git a/media/base/media_client.h b/media/base/media_client.h index 7fc3ddf..5e8ac795 100644 --- a/media/base/media_client.h +++ b/media/base/media_client.h
@@ -43,11 +43,6 @@ // Adds properties for supported key systems. virtual void GetSupportedKeySystems(GetSupportedKeySystemsCB cb) = 0; - // Returns whether client key systems properties should be updated. - // TODO(xhwang): Refactor this to a proper change "observer" API that is - // less fragile (don't assume GetSupportedKeySystems has just one caller). - virtual bool IsKeySystemsUpdateNeeded() = 0; - // Returns true if the given audio config is supported. virtual bool IsSupportedAudioType(const AudioType& type) = 0;
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h index dbc853c..a5e0334c 100644 --- a/media/base/mock_filters.h +++ b/media/base/mock_filters.h
@@ -858,7 +858,6 @@ // MediaClient implementation. MOCK_METHOD1(GetSupportedKeySystems, void(GetSupportedKeySystemsCB cb)); - MOCK_METHOD0(IsKeySystemsUpdateNeeded, bool()); MOCK_METHOD1(IsSupportedAudioType, bool(const media::AudioType& type)); MOCK_METHOD1(IsSupportedVideoType, bool(const media::VideoType& type)); MOCK_METHOD1(IsSupportedBitstreamAudioCodec, bool(media::AudioCodec codec));
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc index b883e0f..b9ce5f0 100644 --- a/media/base/video_frame.cc +++ b/media/base/video_frame.cc
@@ -1448,9 +1448,6 @@ return false; } - // Make sure new formats are properly accounted for in the method. - static_assert(PIXEL_FORMAT_MAX == 33, - "Added pixel format, please review AreSizesValid()"); switch (frame_control_type) { case FrameControlType::kNone: // Check that software-allocated buffer formats are not empty.
diff --git a/media/gpu/chromeos/image_processor_factory.cc b/media/gpu/chromeos/image_processor_factory.cc index 48cef57..b30b400 100644 --- a/media/gpu/chromeos/image_processor_factory.cc +++ b/media/gpu/chromeos/image_processor_factory.cc
@@ -159,6 +159,41 @@ } return nullptr; } + +std::unique_ptr<ImageProcessor> CreateLibYUVImageProcessorWithInputCandidates( + const std::vector<PixelLayoutCandidate>& input_candidates, + const gfx::Rect& input_visible_rect, + const gfx::Size& output_size, + size_t num_buffers, + scoped_refptr<base::SequencedTaskRunner> client_task_runner, + ImageProcessorFactory::PickFormatCB out_format_picker, + ImageProcessor::ErrorCB error_cb) { + if (input_candidates.size() != 1) + return nullptr; + + if (input_candidates[0].fourcc != Fourcc(Fourcc::MM21)) + return nullptr; + + std::vector<Fourcc> supported_output_formats = + LibYUVImageProcessorBackend::GetSupportedOutputFormats( + Fourcc(Fourcc::MM21)); + auto output_format = + out_format_picker.Run(supported_output_formats, Fourcc(Fourcc::NV12)); + + if (!output_format) + return nullptr; + + ImageProcessor::PortConfig input_config( + Fourcc(Fourcc::MM21), input_candidates[0].size, /*planes=*/{}, + input_visible_rect, {VideoFrame::STORAGE_DMABUFS}); + ImageProcessor::PortConfig output_config( + *output_format, output_size, /*planes=*/{}, gfx::Rect(output_size), + {VideoFrame::STORAGE_GPU_MEMORY_BUFFER}); + return ImageProcessor::Create( + base::BindRepeating(&LibYUVImageProcessorBackend::Create), input_config, + output_config, ImageProcessor::OutputMode::IMPORT, VIDEO_ROTATION_0, + std::move(error_cb), std::move(client_task_runner)); +} #endif // BUILDFLAG(USE_V4L2_CODEC) && !BUILDFLAG(USE_VAAPI) } // namespace @@ -216,6 +251,12 @@ out_format_picker, error_cb); if (processor) return processor; + + processor = CreateLibYUVImageProcessorWithInputCandidates( + input_candidates, input_visible_rect, output_size, num_buffers, + client_task_runner, out_format_picker, error_cb); + if (processor) + return processor; #endif // BUILDFLAG(USE_V4L2_CODEC) // TODO(crbug.com/1004727): Implement LibYUVImageProcessorBackend. When doing
diff --git a/media/gpu/chromeos/libyuv_image_processor_backend.cc b/media/gpu/chromeos/libyuv_image_processor_backend.cc index 6245a80..f73d66e 100644 --- a/media/gpu/chromeos/libyuv_image_processor_backend.cc +++ b/media/gpu/chromeos/libyuv_image_processor_backend.cc
@@ -100,38 +100,39 @@ kRotation, }; +static constexpr struct { + uint32_t input; + uint32_t output; + Transform transform; + SupportResult support_result; +} kSupportFormatConversionArray[] = { +#define CONV(in, out, trans, result) \ + {Fourcc::in, Fourcc::out, Transform::trans, SupportResult::result} + // Conversion. + CONV(NV12, NV12, kConversion, Supported), + CONV(YM16, NV12, kConversion, Supported), + CONV(YM16, YU12, kConversion, Supported), + CONV(YU12, NV12, kConversion, Supported), + CONV(YU12, YU12, kConversion, Supported), + CONV(YUYV, NV12, kConversion, Supported), + CONV(YUYV, YU12, kConversion, Supported), + CONV(YV12, NV12, kConversion, Supported), + CONV(MM21, NV12, kConversion, Supported), + // Scaling. + CONV(NV12, NV12, kScaling, Supported), + CONV(YM16, NV12, kScaling, SupportedWithNV12Pivot), + CONV(YM16, YU12, kScaling, SupportedWithI420Pivot), + CONV(YU12, YU12, kScaling, Supported), + CONV(YUYV, NV12, kScaling, SupportedWithNV12Pivot), + CONV(YUYV, YU12, kScaling, SupportedWithI420Pivot), + // Rotating. + CONV(NV12, NV12, kRotation, SupportedWithI420Pivot), +#undef CONV +}; + SupportResult IsConversionSupported(Fourcc input_fourcc, Fourcc output_fourcc, Transform transform) { - static constexpr struct { - uint32_t input; - uint32_t output; - Transform transform; - SupportResult support_result; - } kSupportFormatConversionArray[] = { -#define CONV(in, out, trans, result) \ - {Fourcc::in, Fourcc::out, Transform::trans, SupportResult::result} - // Conversion. - CONV(NV12, NV12, kConversion, Supported), - CONV(YM16, NV12, kConversion, Supported), - CONV(YM16, YU12, kConversion, Supported), - CONV(YU12, NV12, kConversion, Supported), - CONV(YU12, YU12, kConversion, Supported), - CONV(YUYV, NV12, kConversion, Supported), - CONV(YUYV, YU12, kConversion, Supported), - CONV(YV12, NV12, kConversion, Supported), - // Scaling. - CONV(NV12, NV12, kScaling, Supported), - CONV(YM16, NV12, kScaling, SupportedWithNV12Pivot), - CONV(YM16, YU12, kScaling, SupportedWithI420Pivot), - CONV(YU12, YU12, kScaling, Supported), - CONV(YUYV, NV12, kScaling, SupportedWithNV12Pivot), - CONV(YUYV, YU12, kScaling, SupportedWithI420Pivot), - // Rotating. - CONV(NV12, NV12, kRotation, SupportedWithI420Pivot), -#undef CONV - }; - const auto single_input_fourcc = input_fourcc.ToSinglePlanar(); const auto single_output_fourcc = output_fourcc.ToSinglePlanar(); if (!single_input_fourcc || !single_output_fourcc) @@ -407,6 +408,10 @@ return LIBYUV_FUNC(I420ToNV12, Y_V_U_DATA(input), Y_UV_DATA(output)); case PIXEL_FORMAT_NV12: + // MM21 mode. + if (input_config_.fourcc == Fourcc(Fourcc::MM21)) + return LIBYUV_FUNC(MM21ToNV12, Y_UV_DATA(input), Y_UV_DATA(output)); + // Rotation mode. if (relative_rotation_ != VIDEO_ROTATION_0) { // The size of |tmp_buffer| of NV12Rotate() should be @@ -531,4 +536,16 @@ return true; } +std::vector<Fourcc> LibYUVImageProcessorBackend::GetSupportedOutputFormats( + Fourcc input_format) { + std::vector<Fourcc> supported_formats; + for (const auto& conv : kSupportFormatConversionArray) { + if (Fourcc::FromUint32(conv.input) && + *Fourcc::FromUint32(conv.input) == input_format && + Fourcc::FromUint32(conv.output)) + supported_formats.emplace_back(*Fourcc::FromUint32(conv.output)); + } + return supported_formats; +} + } // namespace media
diff --git a/media/gpu/chromeos/libyuv_image_processor_backend.h b/media/gpu/chromeos/libyuv_image_processor_backend.h index 61d6008c..6a48cf5 100644 --- a/media/gpu/chromeos/libyuv_image_processor_backend.h +++ b/media/gpu/chromeos/libyuv_image_processor_backend.h
@@ -8,6 +8,7 @@ #include <memory> #include <vector> +#include "media/gpu/chromeos/fourcc.h" #include "media/gpu/chromeos/image_processor_backend.h" #include "media/gpu/media_gpu_export.h" #include "ui/gfx/geometry/rect.h" @@ -46,6 +47,8 @@ bool needs_linear_output_buffers() const override; + static std::vector<Fourcc> GetSupportedOutputFormats(Fourcc input_format); + private: LibYUVImageProcessorBackend( std::unique_ptr<VideoFrameMapper> input_frame_mapper,
diff --git a/media/gpu/test/video_test_helpers.cc b/media/gpu/test/video_test_helpers.cc index 9d578ec..9d228f5 100644 --- a/media/gpu/test/video_test_helpers.cc +++ b/media/gpu/test/video_test_helpers.cc
@@ -9,6 +9,8 @@ #include "base/callback_helpers.h" #include "base/logging.h" #include "base/memory/ptr_util.h" +#include "base/memory/shared_memory_mapping.h" +#include "base/memory/unsafe_shared_memory_region.h" #include "base/stl_util.h" #include "gpu/ipc/common/gpu_memory_buffer_support.h" #include "gpu/ipc/service/gpu_memory_buffer_factory.h" @@ -364,9 +366,9 @@ struct AlignedDataHelper::VideoFrameData { VideoFrameData() = default; - VideoFrameData(mojo::ScopedSharedBufferHandle mojo_handle) - : mojo_handle(std::move(mojo_handle)) {} - VideoFrameData(gfx::GpuMemoryBufferHandle gmb_handle) + explicit VideoFrameData(base::UnsafeSharedMemoryRegion shmem_region) + : shmem_region(std::move(shmem_region)) {} + explicit VideoFrameData(gfx::GpuMemoryBufferHandle gmb_handle) : gmb_handle(std::move(gmb_handle)) {} VideoFrameData(VideoFrameData&&) = default; @@ -374,7 +376,7 @@ VideoFrameData(const VideoFrameData&) = delete; VideoFrameData& operator=(const VideoFrameData&) = delete; - mojo::ScopedSharedBufferHandle mojo_handle; + base::UnsafeSharedMemoryRegion shmem_region; gfx::GpuMemoryBufferHandle gmb_handle; }; @@ -486,11 +488,10 @@ dummy_mailbox, base::DoNothing() /* mailbox_holder_release_cb_ */, frame_timestamp); } else { - const auto& mojo_handle = video_frame_data_[read_frame_index].mojo_handle; - auto dup_handle = - mojo_handle->Clone(mojo::SharedBufferHandle::AccessMode::READ_WRITE); - if (!dup_handle.is_valid()) { - LOG(ERROR) << "Failed duplicating mojo handle"; + const auto& shmem_region = video_frame_data_[read_frame_index].shmem_region; + auto dup_region = shmem_region.Duplicate(); + if (!dup_region.IsValid()) { + LOG(ERROR) << "Failed duplicating shmem region"; return nullptr; } @@ -502,10 +503,10 @@ } const size_t video_frame_size = layout_->planes().back().offset + layout_->planes().back().size; + DCHECK_EQ(video_frame_size, dup_region.GetSize()); return MojoSharedBufferVideoFrame::Create( layout_->format(), layout_->coded_size(), visible_rect_, natural_size_, - std::move(dup_handle), video_frame_size, offsets, strides, - frame_timestamp); + std::move(dup_region), offsets, strides, frame_timestamp); } } @@ -542,11 +543,11 @@ const size_t num_planes = VideoFrame::NumPlanes(pixel_format); const uint8_t* src_frame_ptr = &stream[0]; for (size_t i = 0; i < num_frames_; i++) { - auto handle = mojo::SharedBufferHandle::Create(video_frame_size); - ASSERT_TRUE(handle.is_valid()) << "Failed allocating a handle"; - auto mapping = handle->Map(video_frame_size); - ASSERT_TRUE(!!mapping); - uint8_t* buffer = reinterpret_cast<uint8_t*>(mapping.get()); + auto region = base::UnsafeSharedMemoryRegion::Create(video_frame_size); + ASSERT_TRUE(region.IsValid()) << "Failed allocating a region"; + base::WritableSharedMemoryMapping mapping = region.Map(); + ASSERT_TRUE(mapping.IsValid()); + uint8_t* buffer = mapping.GetMemoryAs<uint8_t>(); for (size_t j = 0; j < num_planes; j++) { auto src_plane_layout = src_layout.planes()[j]; auto dst_plane_layout = layout_->planes()[j]; @@ -557,7 +558,7 @@ src_plane_rows[j]); } src_frame_ptr += src_video_frame_size; - video_frame_data_[i] = VideoFrameData(std::move(handle)); + video_frame_data_[i] = VideoFrameData(std::move(region)); } }
diff --git a/media/mojo/clients/win/media_foundation_renderer_client.cc b/media/mojo/clients/win/media_foundation_renderer_client.cc index 10cfc55..4f79b94 100644 --- a/media/mojo/clients/win/media_foundation_renderer_client.cc +++ b/media/mojo/clients/win/media_foundation_renderer_client.cc
@@ -453,10 +453,8 @@ // Set DirectComposition mode and get DirectComposition surface from // MediaFoundationRenderer. renderer_extension_->GetDCOMPSurface( - mojo::WrapCallbackWithDefaultInvokeIfNotRun( - base::BindOnce(&MediaFoundationRendererClient::OnDCOMPSurfaceReceived, - weak_factory_.GetWeakPtr()), - absl::nullopt, "disconnection error")); + base::BindOnce(&MediaFoundationRendererClient::OnDCOMPSurfaceReceived, + weak_factory_.GetWeakPtr())); } void MediaFoundationRendererClient::OnDCOMPSurfaceReceived( @@ -466,11 +464,9 @@ DCHECK(has_video_); DCHECK(media_task_runner_->BelongsToCurrentThread()); + // The error should've already been handled in MediaFoundationRenderer. if (!token) { - MEDIA_LOG(ERROR, media_log_) << "GetDCOMPSurface failed: " + error; - MediaFoundationRenderer::ReportErrorReason( - MediaFoundationRenderer::ErrorReason::kOnDCompSurfaceReceivedError); - OnError(PIPELINE_ERROR_COULD_NOT_RENDER); + DLOG(ERROR) << "GetDCOMPSurface failed: " + error; return; }
diff --git a/media/mojo/common/mojo_shared_buffer_video_frame.cc b/media/mojo/common/mojo_shared_buffer_video_frame.cc index 54890bd..c195825 100644 --- a/media/mojo/common/mojo_shared_buffer_video_frame.cc +++ b/media/mojo/common/mojo_shared_buffer_video_frame.cc
@@ -11,9 +11,10 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/logging.h" +#include "base/memory/shared_memory_mapping.h" +#include "base/memory/unsafe_shared_memory_region.h" #include "base/numerics/safe_conversions.h" #include "base/numerics/safe_math.h" -#include "mojo/public/cpp/system/platform_handle.h" namespace media { @@ -40,9 +41,8 @@ // Allocate a shared memory buffer big enough to hold the desired frame. const size_t allocation_size = VideoFrame::AllocationSize(format, coded_size); - mojo::ScopedSharedBufferHandle handle = - mojo::SharedBufferHandle::Create(allocation_size); - if (!handle.is_valid()) { + auto region = base::UnsafeSharedMemoryRegion::Create(allocation_size); + if (!region.IsValid()) { DLOG(ERROR) << __func__ << " Unable to allocate memory."; return nullptr; } @@ -58,13 +58,13 @@ // - Yplane, full size (each element represents a 1x1 block) // - Uplane, quarter size (each element represents a 2x2 block) // - Vplane, quarter size (each element represents a 2x2 block) - return Create( - format, coded_size, visible_rect, dimensions, std::move(handle), - allocation_size, - {0 /* y_offset */, static_cast<uint32_t>(coded_size.GetArea()), - static_cast<uint32_t>(coded_size.GetArea() * 5 / 4)}, - {coded_size.width(), coded_size.width() / 2, coded_size.width() / 2}, - timestamp); + const uint32_t offsets[] = { + 0 /* y_offset */, static_cast<uint32_t>(coded_size.GetArea()), + static_cast<uint32_t>(coded_size.GetArea() * 5 / 4)}; + const int32_t strides[] = {coded_size.width(), coded_size.width() / 2, + coded_size.width() / 2}; + return Create(format, coded_size, visible_rect, dimensions, + std::move(region), offsets, strides, timestamp); } else { // |format| is PIXEL_FORMAT_NV12. // Create and initialize the frame. As this is NV12 format, the UV plane @@ -72,11 +72,11 @@ // as follows: // - Yplane, full size (each element represents a 1x1 block) // - UVplane, full width, half height (each pair represents a 2x2 block) - return Create( - format, coded_size, visible_rect, dimensions, std::move(handle), - allocation_size, - {0 /* y_offset */, static_cast<uint32_t>(coded_size.GetArea())}, - {coded_size.width(), coded_size.width()}, timestamp); + const uint32_t offsets[] = {0 /* y_offset */, + static_cast<uint32_t>(coded_size.GetArea())}; + const int32_t strides[] = {coded_size.width(), coded_size.width()}; + return Create(format, coded_size, visible_rect, dimensions, + std::move(region), offsets, strides, timestamp); } } @@ -99,13 +99,16 @@ aggregate_size += sizes[i]; } - mojo::ScopedSharedBufferHandle handle = - mojo::SharedBufferHandle::Create(aggregate_size); - if (!handle->is_valid()) { + auto region = base::UnsafeSharedMemoryRegion::Create(aggregate_size); + if (!region.IsValid()) { DLOG(ERROR) << "Can't create new frame backing memory"; return nullptr; } - mojo::ScopedSharedBufferMapping dst_mapping = handle->Map(aggregate_size); + base::WritableSharedMemoryMapping dst_mapping = region.Map(); + if (!dst_mapping.IsValid()) { + DLOG(ERROR) << "Can't create map frame backing memory"; + return nullptr; + } // The data from |frame| may not be consecutive between planes. Copy data into // a shared memory buffer which is tightly packed. Padding inside each planes @@ -113,8 +116,7 @@ scoped_refptr<MojoSharedBufferVideoFrame> mojo_frame = MojoSharedBufferVideoFrame::Create( frame.format(), frame.coded_size(), frame.visible_rect(), - frame.natural_size(), std::move(handle), aggregate_size, - offsets /* don't move, we use it again */, std::move(strides), + frame.natural_size(), std::move(region), offsets, strides, frame.timestamp()); CHECK(!!mojo_frame); @@ -151,10 +153,9 @@ const gfx::Size& coded_size, const gfx::Rect& visible_rect, const gfx::Size& natural_size, - mojo::ScopedSharedBufferHandle handle, - size_t data_size, - std::vector<uint32_t> offsets, - std::vector<int32_t> strides, + base::UnsafeSharedMemoryRegion region, + base::span<const uint32_t> offsets, + base::span<const int32_t> strides, base::TimeDelta timestamp) { if (!IsValidConfig(format, STORAGE_MOJO_SHARED_BUFFER, coded_size, visible_rect, natural_size)) { @@ -184,6 +185,7 @@ // range of an int) due to the IsValidConfig() check above. // // TODO(sandersd): Allow non-sequential formats. + const size_t data_size = region.GetSize(); std::vector<ColorPlaneLayout> planes(num_planes); for (size_t i = 0; i < num_planes; ++i) { if (strides[i] < 0) { @@ -227,8 +229,8 @@ // Now allocate the frame and initialize it. scoped_refptr<MojoSharedBufferVideoFrame> frame( new MojoSharedBufferVideoFrame(*layout, visible_rect, natural_size, - std::move(handle), data_size, timestamp)); - if (!frame->Init(std::move(offsets))) { + std::move(region), timestamp)); + if (!frame->Init(offsets)) { DLOG(ERROR) << __func__ << " MojoSharedBufferVideoFrame::Init failed."; return nullptr; } @@ -240,23 +242,21 @@ const VideoFrameLayout& layout, const gfx::Rect& visible_rect, const gfx::Size& natural_size, - mojo::ScopedSharedBufferHandle handle, - size_t mapped_size, + base::UnsafeSharedMemoryRegion region, base::TimeDelta timestamp) : VideoFrame(layout, STORAGE_MOJO_SHARED_BUFFER, visible_rect, natural_size, timestamp), - shared_buffer_handle_(std::move(handle)), - shared_buffer_size_(mapped_size) { - DCHECK(shared_buffer_handle_.is_valid()); + region_(std::move(region)) { + DCHECK(region_.IsValid()); } -bool MojoSharedBufferVideoFrame::Init(std::vector<uint32_t> offsets) { - DCHECK(!shared_buffer_mapping_); - shared_buffer_mapping_ = shared_buffer_handle_->Map(shared_buffer_size_); - if (!shared_buffer_mapping_) +bool MojoSharedBufferVideoFrame::Init(base::span<const uint32_t> offsets) { + DCHECK(!mapping_.IsValid()); + mapping_ = region_.Map(); + if (!mapping_.IsValid()) return false; const size_t num_planes = NumPlanes(format()); DCHECK_EQ(offsets.size(), num_planes); @@ -271,8 +271,7 @@ // Call |mojo_shared_buffer_done_cb_| to take ownership of // |shared_buffer_handle_|. if (mojo_shared_buffer_done_cb_) { - std::move(mojo_shared_buffer_done_cb_) - .Run(std::move(shared_buffer_handle_), shared_buffer_size_); + std::move(mojo_shared_buffer_done_cb_).Run(std::move(region_)); } } @@ -286,12 +285,4 @@ mojo_shared_buffer_done_cb_ = std::move(mojo_shared_buffer_done_cb); } -const mojo::SharedBufferHandle& MojoSharedBufferVideoFrame::Handle() const { - return shared_buffer_handle_.get(); -} - -size_t MojoSharedBufferVideoFrame::MappedSize() const { - return shared_buffer_size_; -} - } // namespace media
diff --git a/media/mojo/common/mojo_shared_buffer_video_frame.h b/media/mojo/common/mojo_shared_buffer_video_frame.h index 8a30bc4..aa1a92f 100644 --- a/media/mojo/common/mojo_shared_buffer_video_frame.h +++ b/media/mojo/common/mojo_shared_buffer_video_frame.h
@@ -8,13 +8,13 @@ #include <stddef.h> #include <stdint.h> -#include <vector> - #include "base/callback_forward.h" +#include "base/containers/span.h" #include "base/memory/ref_counted.h" +#include "base/memory/shared_memory_mapping.h" +#include "base/memory/unsafe_shared_memory_region.h" #include "media/base/video_frame.h" #include "media/base/video_frame_layout.h" -#include "mojo/public/cpp/system/buffer.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" @@ -28,8 +28,7 @@ // Callback called when this object is destructed. Ownership of the shared // memory is transferred to the callee. using MojoSharedBufferDoneCB = - base::OnceCallback<void(mojo::ScopedSharedBufferHandle buffer, - size_t capacity)>; + base::OnceCallback<void(base::UnsafeSharedMemoryRegion region)>; // Creates a new I420 or NV12 frame in shared memory with provided parameters // (coded_size() == natural_size() == visible_rect()), or returns nullptr. @@ -58,29 +57,25 @@ const gfx::Size& coded_size, const gfx::Rect& visible_rect, const gfx::Size& natural_size, - mojo::ScopedSharedBufferHandle handle, - size_t mapped_size, - std::vector<uint32_t> offsets, - std::vector<int32_t> strides, + base::UnsafeSharedMemoryRegion region, + base::span<const uint32_t> offsets, + base::span<const int32_t> strides, base::TimeDelta timestamp); MojoSharedBufferVideoFrame(const MojoSharedBufferVideoFrame&) = delete; MojoSharedBufferVideoFrame& operator=(const MojoSharedBufferVideoFrame&) = delete; - // Returns the offsets relative to the start of |shared_buffer| for the + // Returns the offsets relative to the start of the shmem mapping for the // |plane| specified. size_t PlaneOffset(size_t plane) const; - // Returns a reference to the mojo shared memory handle. Caller should - // duplicate the handle if they want to extend the lifetime of the buffer. - const mojo::SharedBufferHandle& Handle() const; + // Callers can `Duplicate()` the mapping to extend the lifetime of the region. + const base::UnsafeSharedMemoryRegion& shmem_region() const { return region_; } - // Returns the size of the shared memory. - size_t MappedSize() const; - - // Sets the callback to be called to free the shared buffer. If not null, - // it is called on destruction, and is passed ownership of |handle|. + // Sets the callback to be called to free the shmem region. If not null, + // the callback is called when `this` is destroyed, and ownership of + // `region_` is transferred to it. void SetMojoSharedBufferDoneCB( MojoSharedBufferDoneCB mojo_shared_buffer_done_cb); @@ -90,22 +85,21 @@ MojoSharedBufferVideoFrame(const VideoFrameLayout& layout, const gfx::Rect& visible_rect, const gfx::Size& natural_size, - mojo::ScopedSharedBufferHandle handle, - size_t mapped_size, + base::UnsafeSharedMemoryRegion region, base::TimeDelta timestamp); ~MojoSharedBufferVideoFrame() override; // Initializes the MojoSharedBufferVideoFrame by creating a mapping onto // the shared memory, and then setting offsets as specified. - bool Init(std::vector<uint32_t> offsets); + bool Init(base::span<const uint32_t> offsets); - uint8_t* shared_buffer_data() { - return reinterpret_cast<uint8_t*>(shared_buffer_mapping_.get()); - } + uint8_t* shared_buffer_data() { return mapping_.GetMemoryAs<uint8_t>(); } - mojo::ScopedSharedBufferHandle shared_buffer_handle_; - mojo::ScopedSharedBufferMapping shared_buffer_mapping_; - size_t shared_buffer_size_; + // WritableSharedMemoryRegion has strict ownership and cannot be cloned. Since + // the shared memory region may be reused and handed out to a producer + // multiple times, this must use an UnsafeSharedMemoryRegion instead. + base::UnsafeSharedMemoryRegion region_; + base::WritableSharedMemoryMapping mapping_; size_t offsets_[kMaxPlanes]; MojoSharedBufferDoneCB mojo_shared_buffer_done_cb_; };
diff --git a/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc b/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc index 7b24ef7..18a70f3 100644 --- a/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc +++ b/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc
@@ -11,31 +11,15 @@ #include "base/bind.h" #include "base/memory/ref_counted.h" +#include "base/memory/unsafe_shared_memory_region.h" +#include "base/test/bind.h" #include "base/time/time.h" -#include "mojo/public/cpp/system/buffer.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" namespace media { -namespace { - -void CompareDestructionCallbackValues( - mojo::SharedBufferHandle expected_handle, - size_t expected_handle_size, - bool* callback_called, - mojo::ScopedSharedBufferHandle actual_handle, - size_t actual_handle_size) { - // Compare expected vs actual. Ownership of the memory is transferred with - // |actual_handle|, thus it is a ScopedSharedBufferHandle. - EXPECT_EQ(expected_handle, actual_handle.get()); - EXPECT_EQ(expected_handle_size, actual_handle_size); - *callback_called = true; -} - -} // namespace - TEST(MojoSharedBufferVideoFrameTest, CreateFrameWithSharedMemoryI420) { const int kWidth = 16; const int kHeight = 9; @@ -118,16 +102,16 @@ gfx::Rect visible_rect(size); size_t requested_size = VideoFrame::AllocationSize(format, size); ASSERT_LT(y_offset, requested_size); - mojo::ScopedSharedBufferHandle handle = - mojo::SharedBufferHandle::Create(requested_size); - ASSERT_TRUE(handle.is_valid()); + auto region = base::UnsafeSharedMemoryRegion::Create(requested_size); + ASSERT_TRUE(region.IsValid()); // Allocate frame. + const uint32_t offsets[] = {y_offset, u_offset, v_offset}; + const int32_t strides[] = {y_stride, u_stride, v_stride}; scoped_refptr<MojoSharedBufferVideoFrame> frame = - MojoSharedBufferVideoFrame::Create( - format, size, visible_rect, size, std::move(handle), requested_size, - {y_offset, u_offset, v_offset}, {y_stride, u_stride, v_stride}, - kTimestamp); + MojoSharedBufferVideoFrame::Create(format, size, visible_rect, size, + std::move(region), offsets, strides, + kTimestamp); ASSERT_TRUE(frame.get()); EXPECT_EQ(frame->format(), format); @@ -166,15 +150,16 @@ gfx::Rect visible_rect(size); size_t requested_size = VideoFrame::AllocationSize(format, size); ASSERT_LT(y_offset, requested_size); - mojo::ScopedSharedBufferHandle handle = - mojo::SharedBufferHandle::Create(requested_size); - ASSERT_TRUE(handle.is_valid()); + auto region = base::UnsafeSharedMemoryRegion::Create(requested_size); + ASSERT_TRUE(region.IsValid()); // Allocate frame. + const uint32_t offsets[] = {y_offset, uv_offset}; + const int32_t strides[] = {y_stride, uv_stride}; scoped_refptr<MojoSharedBufferVideoFrame> frame = - MojoSharedBufferVideoFrame::Create( - format, size, visible_rect, size, std::move(handle), requested_size, - {y_offset, uv_offset}, {y_stride, uv_stride}, kTimestamp); + MojoSharedBufferVideoFrame::Create(format, size, visible_rect, size, + std::move(region), offsets, strides, + kTimestamp); ASSERT_TRUE(frame.get()); EXPECT_EQ(frame->format(), format); @@ -225,27 +210,41 @@ gfx::Size size(kWidth, kHeight); gfx::Rect visible_rect(size); size_t requested_size = VideoFrame::AllocationSize(format, size); - mojo::ScopedSharedBufferHandle handle = - mojo::SharedBufferHandle::Create(requested_size); - ASSERT_TRUE(handle.is_valid()); + auto region = base::UnsafeSharedMemoryRegion::Create(requested_size); + ASSERT_TRUE(region.IsValid()); - // Keep track of the original handle. MojoSharedBufferVideoFrame::Create() - // will get ownership of the memory. - mojo::SharedBufferHandle original_handle = handle.get(); + const char kTestData[] = "reduce reuse recycle"; + { + base::WritableSharedMemoryMapping mapping = region.Map(); + ASSERT_GT(mapping.size(), strlen(kTestData)); + // Note: deliberately using sizeof() to include the null terminator. + memcpy(mapping.memory(), kTestData, sizeof(kTestData)); + } // Allocate frame. + const uint32_t kOffsets[] = {0, 0, 0}; + const int32_t kStrides[] = {kWidth, kWidth, kWidth}; scoped_refptr<MojoSharedBufferVideoFrame> frame = - MojoSharedBufferVideoFrame::Create( - format, size, visible_rect, size, std::move(handle), requested_size, - {0, 0, 0}, {kWidth, kWidth, kWidth}, kTimestamp); + MojoSharedBufferVideoFrame::Create(format, size, visible_rect, size, + std::move(region), kOffsets, kStrides, + kTimestamp); ASSERT_TRUE(frame.get()); EXPECT_EQ(frame->format(), format); // Set the destruction callback. bool callback_called = false; - frame->SetMojoSharedBufferDoneCB( - base::BindOnce(&CompareDestructionCallbackValues, original_handle, - requested_size, &callback_called)); + auto destruction_cb = + base::BindLambdaForTesting([&](base::UnsafeSharedMemoryRegion region) { + callback_called = true; + ASSERT_EQ(requested_size, region.GetSize()); + // Unsafe regions are always mapped as writable. + base::WritableSharedMemoryMapping mapping = region.Map(); + // Check that the test data that was written there is still there as a + // proxy signal for checking that ownership of the shmem region has been + // transferred. + EXPECT_STREQ(kTestData, mapping.GetMemoryAs<char>()); + }); + frame->SetMojoSharedBufferDoneCB(std::move(destruction_cb)); EXPECT_FALSE(callback_called); // Force destruction of |frame|. @@ -274,16 +273,16 @@ // Allocate some shared memory. size_t requested_size = VideoFrame::AllocationSize(format, size); - mojo::ScopedSharedBufferHandle handle = - mojo::SharedBufferHandle::Create(requested_size); - ASSERT_TRUE(handle.is_valid()); + auto region = base::UnsafeSharedMemoryRegion::Create(requested_size); + ASSERT_TRUE(region.IsValid()); // Allocate frame. + const uint32_t kOffsets[] = {y_offset, u_offset, v_offset}; + const int32_t kStrides[] = {y_stride, u_stride, v_stride}; scoped_refptr<MojoSharedBufferVideoFrame> frame = - MojoSharedBufferVideoFrame::Create( - format, size, visible_rect, size, std::move(handle), requested_size, - {y_offset, u_offset, v_offset}, {y_stride, u_stride, v_stride}, - kTimestamp); + MojoSharedBufferVideoFrame::Create(format, size, visible_rect, size, + std::move(region), kOffsets, kStrides, + kTimestamp); ASSERT_TRUE(frame.get()); EXPECT_EQ(frame->format(), format); @@ -322,7 +321,8 @@ const size_t u_stride = frame->stride(VideoFrame::kUPlane); // Verifies mapped size and offset. - EXPECT_EQ(mojo_frame->MappedSize(), static_cast<size_t>(3 * stride)); + EXPECT_EQ(mojo_frame->shmem_region().GetSize(), + static_cast<size_t>(3 * stride)); EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kYPlane), 0u); EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kUPlane), y_stride); EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kVPlane), y_stride + u_stride); @@ -347,7 +347,8 @@ const size_t y_stride = frame->stride(VideoFrame::kYPlane); // Verifies mapped size and offset. - EXPECT_EQ(mojo_frame->MappedSize(), static_cast<size_t>(2 * stride)); + EXPECT_EQ(mojo_frame->shmem_region().GetSize(), + static_cast<size_t>(2 * stride)); EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kYPlane), 0u); EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kUVPlane), y_stride); }
diff --git a/media/mojo/mojom/BUILD.gn b/media/mojo/mojom/BUILD.gn index 8ef86fb..df4b0366 100644 --- a/media/mojo/mojom/BUILD.gn +++ b/media/mojo/mojom/BUILD.gn
@@ -128,6 +128,20 @@ { types = [ { + mojom = "media.mojom.AudioProcessingSettings" + cpp = "::media::AudioProcessingSettings" + }, + { + mojom = "media.mojom.AudioProcessingStats" + cpp = "::media::AudioProcessingStats" + }, + ] + traits_headers = [ "audio_processing_mojom_traits.h" ] + traits_public_deps = [ ":shared_mojom_traits" ] + }, + { + types = [ + { mojom = "media.mojom.FullscreenVideoStatus" cpp = "::blink::WebFullscreenVideoStatus" }, @@ -164,21 +178,6 @@ { types = [ { - mojom = "media.mojom.AudioProcessingSettings" - cpp = "::media::AudioProcessingSettings" - }, - { - mojom = "media.mojom.AudioProcessingStats" - cpp = "::media::AudioProcessingStats" - }, - ] - traits_headers = [ "audio_processing_mojom_traits.h" ] - traits_sources = [ "audio_processing_mojom_traits.cc" ] - traits_public_deps = [ ":shared_mojom_traits" ] - }, - { - types = [ - { mojom = "media.mojom.AudioDecoderConfig" cpp = "::media::AudioDecoderConfig" }, @@ -745,6 +744,8 @@ source_set("shared_mojom_traits") { sources = [ + "audio_processing_mojom_traits.cc", + "audio_processing_mojom_traits.h", "video_frame_metadata_mojom_traits.cc", "video_frame_metadata_mojom_traits.h", ]
diff --git a/media/mojo/mojom/audio_processing_mojom_traits.h b/media/mojo/mojom/audio_processing_mojom_traits.h index 6bd436f4..71e136a 100644 --- a/media/mojo/mojom/audio_processing_mojom_traits.h +++ b/media/mojo/mojom/audio_processing_mojom_traits.h
@@ -5,8 +5,8 @@ #define MEDIA_MOJO_MOJOM_AUDIO_PROCESSING_MOJOM_TRAITS_H_ #include "media/base/audio_processing.h" - -#include "media/mojo/mojom/audio_processing.mojom.h" +#include "media/base/audio_processor_controls.h" +#include "media/mojo/mojom/audio_processing.mojom-shared.h" #include "mojo/public/cpp/bindings/struct_traits.h" namespace mojo {
diff --git a/media/mojo/mojom/audio_processing_mojom_traits_unittest.cc b/media/mojo/mojom/audio_processing_mojom_traits_unittest.cc index c95c6330..4aa9fb0 100644 --- a/media/mojo/mojom/audio_processing_mojom_traits_unittest.cc +++ b/media/mojo/mojom/audio_processing_mojom_traits_unittest.cc
@@ -5,6 +5,7 @@ #include "media/mojo/mojom/audio_processing_mojom_traits.h" #include "media/base/audio_processing.h" +#include "media/mojo/mojom/audio_processing.mojom.h" #include "media/mojo/mojom/traits_test_service.mojom.h" #include "mojo/public/cpp/test_support/test_utils.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/media/mojo/mojom/key_system_support.mojom b/media/mojo/mojom/key_system_support.mojom index de2967f..c001fc6c 100644 --- a/media/mojo/mojom/key_system_support.mojom +++ b/media/mojo/mojom/key_system_support.mojom
@@ -14,7 +14,7 @@ // codec profiles separately. The list of supported codecs should be unique. array<AudioCodec> audio_codecs; - // Map of video codecs and associated profiles supported by the CDM + // Map from video codecs to associated profiles supported by the CDM // (e.g. vp8). If no profiles for a particular codec are specified, then // it is assumed that all profiles are supported by the CDM. The list of // profiles for each codec should be unique. @@ -29,14 +29,21 @@ CdmCapability? hw_secure_capability; }; -// Used to query the browser to see if a specific key system is supported. +// Process-wide observer used by the renderer to observe key system support +// changes. `key_systems` is a map from the key system string to the +// KeySystemCapability for that key system. +interface KeySystemSupportObserver { + // Called when there's a key system support update. + OnKeySystemSupportUpdated(map<string, KeySystemCapability> key_systems); +}; + +// Browser process singleton that a renderer process can use to subscribe to +// key system updates. interface KeySystemSupport { - // Query to determine if the browser supports the |key_system|. If supported, - // |key_system_capability| is non-null indicating supported capability. - // KeySystemSupport implementation is in the browser process, as it maintains - // the list of registered CDMs, and hardware secure support check also needs - // to run in the browser process because the render process is sandboxed. - // KeySystemSupport clients run in the renderer process. - IsKeySystemSupported(string key_system) - => (bool is_supported, KeySystemCapability? key_system_capability); + // Adds an observer to observe key system support updates. KeySystemSupport + // implementation is in the browser process, as it maintains the list of + // registered CDMs, and hardware secure support check also needs to run in the + // browser process because the render process is sandboxed. KeySystemSupport + // clients run in the renderer process. + AddObserver(pending_remote<KeySystemSupportObserver> observer); };
diff --git a/media/mojo/mojom/media_types.mojom b/media/mojo/mojom/media_types.mojom index 8223958f..6ef2ecc4e 100644 --- a/media/mojo/mojom/media_types.mojom +++ b/media/mojo/mojom/media_types.mojom
@@ -6,6 +6,7 @@ import "gpu/ipc/common/mailbox_holder.mojom"; import "gpu/ipc/common/vulkan_ycbcr_info.mojom"; +import "mojo/public/mojom/base/shared_memory.mojom"; import "mojo/public/mojom/base/time.mojom"; import "mojo/public/mojom/base/values.mojom"; import "mojo/public/mojom/base/unguessable_token.mojom"; @@ -409,9 +410,8 @@ // This defines video frame data stored in a Mojo shared buffer. struct SharedBufferVideoFrameData { - // Reference to the shared memory containing the frame's data. - handle<shared_buffer> frame_data; - uint64 frame_data_size; + // Shared memory region for the frame data. + mojo_base.mojom.UnsafeSharedMemoryRegion frame_data; // Stride and offsets for each plane. Offsets are relative to the start // of |frame_data|.
diff --git a/media/mojo/mojom/video_frame_mojom_traits.cc b/media/mojo/mojom/video_frame_mojom_traits.cc index 2fc7f20..eaee627 100644 --- a/media/mojo/mojom/video_frame_mojom_traits.cc +++ b/media/mojo/mojom/video_frame_mojom_traits.cc
@@ -9,6 +9,7 @@ #include "base/callback_helpers.h" #include "base/logging.h" +#include "base/memory/unsafe_shared_memory_region.h" #include "build/build_config.h" #include "gpu/ipc/common/gpu_memory_buffer_support.h" #include "media/base/color_plane_layout.h" @@ -40,15 +41,9 @@ const media::MojoSharedBufferVideoFrame* mojo_frame = static_cast<const media::MojoSharedBufferVideoFrame*>(input); - // Mojo shared buffer handles are always writable. For example, - // cdm_video_decoder in ToCdmVideoFrame maps a frame writable; these frames - // are returned via callback and reused in ToCdmVideoFrame. Since returning - // via callback involves a Clone(), and since cloning a region read-only - // makes both the source handle and the cloned handle read-only, it must be - // cloned writable. - mojo::ScopedSharedBufferHandle dup = mojo_frame->Handle().Clone( - mojo::SharedBufferHandle::AccessMode::READ_WRITE); - DCHECK(dup.is_valid()); + base::UnsafeSharedMemoryRegion region = + mojo_frame->shmem_region().Duplicate(); + DCHECK(region.IsValid()); size_t num_planes = media::VideoFrame::NumPlanes(mojo_frame->format()); std::vector<uint32_t> offsets(num_planes); std::vector<int32_t> strides(num_planes); @@ -59,8 +54,7 @@ return media::mojom::VideoFrameData::NewSharedBufferData( media::mojom::SharedBufferVideoFrameData::New( - std::move(dup), mojo_frame->MappedSize(), std::move(strides), - std::move(offsets))); + std::move(region), std::move(strides), std::move(offsets))); } std::vector<gpu::MailboxHolder> mailbox_holder(media::VideoFrame::kMaxPlanes); @@ -138,18 +132,19 @@ media::mojom::SharedBufferVideoFrameDataDataView shared_buffer_data; data.GetSharedBufferDataDataView(&shared_buffer_data); - std::vector<int32_t> strides; - if (!shared_buffer_data.ReadStrides(&strides)) + base::UnsafeSharedMemoryRegion region; + if (!shared_buffer_data.ReadFrameData(®ion)) return false; - std::vector<uint32_t> offsets; - if (!shared_buffer_data.ReadOffsets(&offsets)) - return false; + mojo::ArrayDataView<uint32_t> offsets; + shared_buffer_data.GetOffsetsDataView(&offsets); + + mojo::ArrayDataView<int32_t> strides; + shared_buffer_data.GetStridesDataView(&strides); + frame = media::MojoSharedBufferVideoFrame::Create( - format, coded_size, visible_rect, natural_size, - shared_buffer_data.TakeFrameData(), - shared_buffer_data.frame_data_size(), std::move(offsets), - std::move(strides), timestamp); + format, coded_size, visible_rect, natural_size, std::move(region), + offsets, strides, timestamp); } else if (data.is_gpu_memory_buffer_data()) { media::mojom::GpuMemoryBufferVideoFrameDataDataView gpu_memory_buffer_data; data.GetGpuMemoryBufferDataDataView(&gpu_memory_buffer_data);
diff --git a/media/mojo/mojom/video_frame_mojom_traits_unittest.cc b/media/mojo/mojom/video_frame_mojom_traits_unittest.cc index efd81cd..13864dfe 100644 --- a/media/mojo/mojom/video_frame_mojom_traits_unittest.cc +++ b/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
@@ -94,7 +94,7 @@ ASSERT_EQ(frame->storage_type(), VideoFrame::STORAGE_MOJO_SHARED_BUFFER); MojoSharedBufferVideoFrame* mojo_shared_buffer_frame = static_cast<MojoSharedBufferVideoFrame*>(frame.get()); - EXPECT_TRUE(mojo_shared_buffer_frame->Handle().is_valid()); + EXPECT_TRUE(mojo_shared_buffer_frame->shmem_region().IsValid()); } }
diff --git a/media/mojo/services/mojo_cdm_allocator.cc b/media/mojo/services/mojo_cdm_allocator.cc index 7f16f35..8ab6c27 100644 --- a/media/mojo/services/mojo_cdm_allocator.cc +++ b/media/mojo/services/mojo_cdm_allocator.cc
@@ -10,13 +10,14 @@ #include "base/bind.h" #include "base/callback.h" #include "base/compiler_specific.h" +#include "base/memory/shared_memory_mapping.h" +#include "base/memory/unsafe_shared_memory_region.h" #include "base/numerics/safe_conversions.h" #include "base/numerics/safe_math.h" #include "media/cdm/api/content_decryption_module.h" #include "media/cdm/cdm_helpers.h" #include "media/cdm/cdm_type_conversion.h" #include "media/mojo/common/mojo_shared_buffer_video_frame.h" -#include "mojo/public/cpp/system/buffer.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" @@ -29,23 +30,21 @@ class MojoCdmBuffer final : public cdm::Buffer { public: static MojoCdmBuffer* Create( - mojo::ScopedSharedBufferHandle buffer, - size_t capacity, + base::UnsafeSharedMemoryRegion region, MojoSharedBufferVideoFrame::MojoSharedBufferDoneCB mojo_shared_buffer_done_cb) { - DCHECK(buffer.is_valid()); + DCHECK(region.IsValid()); DCHECK(mojo_shared_buffer_done_cb); // cdm::Buffer interface limits capacity to uint32. - DCHECK_LE(capacity, std::numeric_limits<uint32_t>::max()); + CHECK_LE(region.GetSize(), std::numeric_limits<uint32_t>::max()); - auto mapping = buffer->Map(capacity); - if (!mapping) + base::WritableSharedMemoryMapping mapping = region.Map(); + if (!mapping.IsValid()) return nullptr; - return new MojoCdmBuffer( - std::move(buffer), base::checked_cast<uint32_t>(capacity), - std::move(mapping), std::move(mojo_shared_buffer_done_cb)); + return new MojoCdmBuffer(std::move(region), std::move(mapping), + std::move(mojo_shared_buffer_done_cb)); } MojoCdmBuffer(const MojoCdmBuffer&) = delete; @@ -53,21 +52,18 @@ // cdm::Buffer implementation. void Destroy() final { - // Unmap the memory before returning the handle to |allocator_|. - mapping_.reset(); - // If nobody has claimed the handle, then return it. - if (buffer_.is_valid()) { - std::move(mojo_shared_buffer_done_cb_).Run(std::move(buffer_), capacity_); + if (region_.IsValid()) { + std::move(mojo_shared_buffer_done_cb_).Run(std::move(region_)); } // No need to exist anymore. delete this; } - uint32_t Capacity() const final { return capacity_; } + uint32_t Capacity() const final { return mapping_.size(); } - uint8_t* Data() final { return static_cast<uint8_t*>(mapping_.get()); } + uint8_t* Data() final { return mapping_.GetMemoryAs<uint8_t>(); } void SetSize(uint32_t size) final { DCHECK_LE(size, Capacity()); @@ -76,34 +72,33 @@ uint32_t Size() const final { return size_; } - const mojo::SharedBufferHandle& Handle() const { return buffer_.get(); } + const base::UnsafeSharedMemoryRegion& Region() const { return region_; } - mojo::ScopedSharedBufferHandle TakeHandle() { return std::move(buffer_); } + base::UnsafeSharedMemoryRegion TakeRegion() { return std::move(region_); } private: - MojoCdmBuffer(mojo::ScopedSharedBufferHandle buffer, - uint32_t capacity, - mojo::ScopedSharedBufferMapping mapping, + MojoCdmBuffer(base::UnsafeSharedMemoryRegion region, + base::WritableSharedMemoryMapping mapping, MojoSharedBufferVideoFrame::MojoSharedBufferDoneCB mojo_shared_buffer_done_cb) - : buffer_(std::move(buffer)), + : region_(std::move(region)), mojo_shared_buffer_done_cb_(std::move(mojo_shared_buffer_done_cb)), - mapping_(std::move(mapping)), - capacity_(capacity) { - DCHECK(mapping_); + mapping_(std::move(mapping)) { + DCHECK(mapping_.IsValid()); } ~MojoCdmBuffer() final { // Verify that the buffer has been returned so it can be reused. - DCHECK(!buffer_.is_valid()); + DCHECK(!region_.IsValid()); } - mojo::ScopedSharedBufferHandle buffer_; + // Unsafe because of the requirements of VideoFrame; see + // MojoSharedBufferVideoFrame for more details. + base::UnsafeSharedMemoryRegion region_; MojoSharedBufferVideoFrame::MojoSharedBufferDoneCB mojo_shared_buffer_done_cb_; - mojo::ScopedSharedBufferMapping mapping_; - const uint32_t capacity_; + base::WritableSharedMemoryMapping mapping_; uint32_t size_ = 0; }; @@ -128,10 +123,8 @@ MojoCdmBuffer* buffer = static_cast<MojoCdmBuffer*>(FrameBuffer()); const gfx::Size frame_size(Size().width, Size().height); - // Take ownership of the mojo::ScopedSharedBufferHandle from |buffer|. - uint32_t buffer_size = buffer->Size(); - mojo::ScopedSharedBufferHandle handle = buffer->TakeHandle(); - DCHECK(handle.is_valid()); + base::UnsafeSharedMemoryRegion region = buffer->TakeRegion(); + DCHECK(region.IsValid()); // Clear FrameBuffer so that MojoCdmVideoFrame no longer has a reference // to it (memory will be transferred to MojoSharedBufferVideoFrame). @@ -140,15 +133,16 @@ // Destroy the MojoCdmBuffer as it is no longer needed. buffer->Destroy(); + const uint32_t offsets[] = {PlaneOffset(cdm::kYPlane), + PlaneOffset(cdm::kUPlane), + PlaneOffset(cdm::kVPlane)}; + const int32_t strides[] = {static_cast<int32_t>(Stride(cdm::kYPlane)), + static_cast<int32_t>(Stride(cdm::kUPlane)), + static_cast<int32_t>(Stride(cdm::kVPlane))}; scoped_refptr<MojoSharedBufferVideoFrame> frame = media::MojoSharedBufferVideoFrame::Create( ToMediaVideoFormat(Format()), frame_size, gfx::Rect(frame_size), - natural_size, std::move(handle), buffer_size, - {PlaneOffset(cdm::kYPlane), PlaneOffset(cdm::kUPlane), - PlaneOffset(cdm::kVPlane)}, - {static_cast<int32_t>(Stride(cdm::kYPlane)), - static_cast<int32_t>(Stride(cdm::kUPlane)), - static_cast<int32_t>(Stride(cdm::kVPlane))}, + natural_size, std::move(region), offsets, strides, base::Microseconds(Timestamp())); // |frame| could fail to be created if the memory can't be mapped into @@ -172,7 +166,7 @@ MojoCdmAllocator::~MojoCdmAllocator() = default; // Creates a cdm::Buffer, reusing an existing buffer if one is available. -// If not, a new buffer is created using AllocateNewBuffer(). The caller is +// If not, a new buffer is created using AllocateNewRegion(). The caller is // responsible for calling Destroy() on the buffer when it is no longer needed. cdm::Buffer* MojoCdmAllocator::CreateCdmBuffer(size_t capacity) { DCHECK(thread_checker_.CalledOnValidThread()); @@ -180,27 +174,26 @@ if (!capacity) return nullptr; - // Reuse a buffer in the free map if there is one that fits |capacity|. + // Reuse a shmem region in the free map if there is one that fits |capacity|. // Otherwise, create a new one. - mojo::ScopedSharedBufferHandle buffer; - auto found = available_buffers_.lower_bound(capacity); - if (found == available_buffers_.end()) { - buffer = AllocateNewBuffer(&capacity); - if (!buffer.is_valid()) { + base::UnsafeSharedMemoryRegion region; + auto found = available_regions_.lower_bound(capacity); + if (found == available_regions_.end()) { + region = AllocateNewRegion(capacity); + if (!region.IsValid()) { return nullptr; } } else { - capacity = found->first; - buffer = std::move(found->second); - available_buffers_.erase(found); + region = std::move(found->second); + available_regions_.erase(found); } - // Ownership of the SharedBufferHandle is passed to MojoCdmBuffer. When it is - // done with the memory, it must call AddBufferToAvailableMap() to make the - // memory available for another MojoCdmBuffer. + // Ownership of `region` is passed to MojoCdmBuffer. When it is done with the + // memory, it must call `AddRegionrToAvailableMap()` to make the memory + // available for another MojoCdmBuffer. return MojoCdmBuffer::Create( - std::move(buffer), capacity, - base::BindOnce(&MojoCdmAllocator::AddBufferToAvailableMap, + std::move(region), + base::BindOnce(&MojoCdmAllocator::AddRegionToAvailableMap, weak_ptr_factory_.GetWeakPtr())); } @@ -208,12 +201,12 @@ std::unique_ptr<VideoFrameImpl> MojoCdmAllocator::CreateCdmVideoFrame() { DCHECK(thread_checker_.CalledOnValidThread()); return std::make_unique<MojoCdmVideoFrame>( - base::BindOnce(&MojoCdmAllocator::AddBufferToAvailableMap, + base::BindOnce(&MojoCdmAllocator::AddRegionToAvailableMap, weak_ptr_factory_.GetWeakPtr())); } -mojo::ScopedSharedBufferHandle MojoCdmAllocator::AllocateNewBuffer( - size_t* capacity) { +base::UnsafeSharedMemoryRegion MojoCdmAllocator::AllocateNewRegion( + size_t capacity) { DCHECK(thread_checker_.CalledOnValidThread()); // Always pad new allocated buffer so that we don't need to reallocate @@ -227,35 +220,33 @@ // number of free buffers exceeds a limit. This mechanism helps avoid ending // up with too many small buffers, which could happen if the size to be // allocated keeps increasing. - if (available_buffers_.size() >= kFreeLimit) - available_buffers_.erase(available_buffers_.begin()); + if (available_regions_.size() >= kFreeLimit) + available_regions_.erase(available_regions_.begin()); // Creation of shared memory may be expensive if it involves synchronous IPC - // calls. That's why we try to avoid AllocateNewBuffer() as much as we can. - base::CheckedNumeric<size_t> requested_capacity(*capacity); + // calls. That's why we try to avoid AllocateNewRegion() as much as we can. + base::CheckedNumeric<size_t> requested_capacity(capacity); requested_capacity += kBufferPadding; - mojo::ScopedSharedBufferHandle handle = - mojo::SharedBufferHandle::Create(requested_capacity.ValueOrDie()); - if (!handle.is_valid()) - return handle; - *capacity = requested_capacity.ValueOrDie(); - return handle; + auto region = + base::UnsafeSharedMemoryRegion::Create(requested_capacity.ValueOrDie()); + return region; } -void MojoCdmAllocator::AddBufferToAvailableMap( - mojo::ScopedSharedBufferHandle buffer, - size_t capacity) { +void MojoCdmAllocator::AddRegionToAvailableMap( + base::UnsafeSharedMemoryRegion region) { DCHECK(thread_checker_.CalledOnValidThread()); - available_buffers_.insert(std::make_pair(capacity, std::move(buffer))); + size_t capacity = region.GetSize(); + available_regions_.insert({capacity, std::move(region)}); } -MojoHandle MojoCdmAllocator::GetHandleForTesting(cdm::Buffer* buffer) { +const base::UnsafeSharedMemoryRegion& MojoCdmAllocator::GetRegionForTesting( + cdm::Buffer* buffer) const { MojoCdmBuffer* mojo_buffer = static_cast<MojoCdmBuffer*>(buffer); - return mojo_buffer->Handle().value(); + return mojo_buffer->Region(); } -size_t MojoCdmAllocator::GetAvailableBufferCountForTesting() { - return available_buffers_.size(); +size_t MojoCdmAllocator::GetAvailableRegionCountForTesting() { + return available_regions_.size(); } } // namespace media
diff --git a/media/mojo/services/mojo_cdm_allocator.h b/media/mojo/services/mojo_cdm_allocator.h index b051571..5f538ce 100644 --- a/media/mojo/services/mojo_cdm_allocator.h +++ b/media/mojo/services/mojo_cdm_allocator.h
@@ -11,6 +11,7 @@ #include <map> #include <memory> +#include "base/memory/unsafe_shared_memory_region.h" #include "base/memory/weak_ptr.h" #include "base/threading/thread_checker.h" #include "media/cdm/cdm_allocator.h" @@ -36,30 +37,29 @@ private: friend class MojoCdmAllocatorTest; - // Map of available buffers. Done as a mapping of capacity to - // ScopedSharedBufferHandle so that we can efficiently find an available - // buffer of a particular size. Any buffers in the map are unmapped. - using AvailableBufferMap = - std::multimap<size_t, mojo::ScopedSharedBufferHandle>; + // Map of available buffers. Done as a mapping of capacity to shmem regions to + // make it efficient to find an available buffer of a particular size. + // Regions in the map are unmapped. + using AvailableRegionMap = + std::multimap<size_t, base::UnsafeSharedMemoryRegion>; - // Allocates a mojo::SharedBufferHandle of at least |capacity| bytes. - // |capacity| will be changed to reflect the actual size of the buffer - // allocated. - mojo::ScopedSharedBufferHandle AllocateNewBuffer(size_t* capacity); + // Allocates a shmem region of at least |capacity| bytes. + base::UnsafeSharedMemoryRegion AllocateNewRegion(size_t capacity); - // Returns |buffer| to the map of available buffers, ready to be used the + // Returns |region| to the map of available buffers, ready to be used the // next time CreateCdmBuffer() is called. - void AddBufferToAvailableMap(mojo::ScopedSharedBufferHandle buffer, - size_t capacity); + void AddRegionToAvailableMap(base::UnsafeSharedMemoryRegion region); - // Returns the MojoHandle for a cdm::Buffer allocated by this class. - MojoHandle GetHandleForTesting(cdm::Buffer* buffer); + // Returns the base::UnsafeSharedMemoryRegion for a cdm::Buffer allocated by + // this class. + const base::UnsafeSharedMemoryRegion& GetRegionForTesting( + cdm::Buffer* buffer) const; - // Returns the number of buffers in |available_buffers_|. - size_t GetAvailableBufferCountForTesting(); + // Returns the number of buffers in |available_regions_|. + size_t GetAvailableRegionCountForTesting(); // Map of available, already allocated buffers. - AvailableBufferMap available_buffers_; + AvailableRegionMap available_regions_; // Confirms single-threaded access. base::ThreadChecker thread_checker_;
diff --git a/media/mojo/services/mojo_cdm_allocator_unittest.cc b/media/mojo/services/mojo_cdm_allocator_unittest.cc index 6951115a..6a918fd 100644 --- a/media/mojo/services/mojo_cdm_allocator_unittest.cc +++ b/media/mojo/services/mojo_cdm_allocator_unittest.cc
@@ -7,6 +7,8 @@ #include <cstring> +#include "base/memory/shared_memory_mapping.h" +#include "base/memory/unsafe_shared_memory_region.h" #include "media/base/video_frame.h" #include "media/cdm/api/content_decryption_module.h" #include "media/cdm/cdm_helpers.h" @@ -34,12 +36,12 @@ return allocator_.CreateCdmVideoFrame(); } - MojoHandle GetHandle(cdm::Buffer* buffer) { - return allocator_.GetHandleForTesting(buffer); + const base::UnsafeSharedMemoryRegion& GetRegion(cdm::Buffer* buffer) { + return allocator_.GetRegionForTesting(buffer); } - size_t GetAvailableBufferCount() { - return allocator_.GetAvailableBufferCountForTesting(); + size_t GetAvailableRegionCount() { + return allocator_.GetAvailableRegionCountForTesting(); } private: @@ -57,15 +59,30 @@ TEST_F(MojoCdmAllocatorTest, ReuseCdmBuffer) { const size_t kRandomDataSize = 46; + const char kTestData[] = "reduce reuse recycle"; + // Create a small buffer. cdm::Buffer* buffer = CreateCdmBuffer(kRandomDataSize); - MojoHandle handle = GetHandle(buffer); + { + // Create a mapping and write some test data. + const auto& region = GetRegion(buffer); + base::WritableSharedMemoryMapping mapping = region.Map(); + // Note: deliberately using sizeof() to include the null terminator. + memcpy(mapping.memory(), kTestData, sizeof(kTestData)); + } buffer->Destroy(); // Now allocate a new buffer of the same size, it should reuse the one // just freed. cdm::Buffer* new_buffer = CreateCdmBuffer(kRandomDataSize); - EXPECT_EQ(handle, GetHandle(new_buffer)); + { + const auto& region = GetRegion(new_buffer); + // Unsafe regions are always mapped as writable. + base::WritableSharedMemoryMapping mapping = region.Map(); + // Check that the test data that was written there is still there as a proxy + // signal for checking that the shmem region is reused. + EXPECT_STREQ(kTestData, mapping.GetMemoryAs<char>()); + } new_buffer->Destroy(); } @@ -82,7 +99,7 @@ buffer_size += kBufferSizeIncrease; cdm::Buffer* buffer = CreateCdmBuffer(buffer_size); buffer->Destroy(); - EXPECT_LE(GetAvailableBufferCount(), kMaxExpectedFreeBuffers); + EXPECT_LE(GetAvailableRegionCount(), kMaxExpectedFreeBuffers); } } @@ -110,7 +127,7 @@ // Now create a buffer to hold the frame and assign it to the VideoFrameImpl. cdm::Buffer* buffer = CreateCdmBuffer(kBufferSize); - EXPECT_EQ(0u, GetAvailableBufferCount()); + EXPECT_EQ(0u, GetAvailableRegionCount()); buffer->SetSize(static_cast<uint32_t>(kBufferSize)); video_frame->SetFrameBuffer(buffer); EXPECT_NE(nullptr, video_frame->FrameBuffer()); @@ -118,16 +135,16 @@ // Transform it into a VideoFrame and make sure the buffer is no longer owned. scoped_refptr<VideoFrame> frame = video_frame->TransformToVideoFrame(kSize); EXPECT_EQ(nullptr, video_frame->FrameBuffer()); - EXPECT_EQ(0u, GetAvailableBufferCount()); + EXPECT_EQ(0u, GetAvailableRegionCount()); video_frame.reset(); // Check that the buffer is still in use. It will be freed when |frame| // is destroyed. - EXPECT_EQ(0u, GetAvailableBufferCount()); + EXPECT_EQ(0u, GetAvailableRegionCount()); frame = nullptr; // Check that the buffer is now in the free list. - EXPECT_EQ(1u, GetAvailableBufferCount()); + EXPECT_EQ(1u, GetAvailableRegionCount()); } } // namespace media
diff --git a/media/renderers/win/media_engine_notify_impl.cc b/media/renderers/win/media_engine_notify_impl.cc index 15f59a3..eb4ad5ee 100644 --- a/media/renderers/win/media_engine_notify_impl.cc +++ b/media/renderers/win/media_engine_notify_impl.cc
@@ -63,14 +63,7 @@ #undef ENUM_TO_STRING PipelineStatus MediaEngineErrorToPipelineStatus( - MF_MEDIA_ENGINE_ERR media_engine_error, - HRESULT hr) { - // HRESULT 0x8004CD12 is DRM_E_TEE_INVALID_HWDRM_STATE, which can happen - // during OS sleep/resume, or moving video to different graphics adapters. - // This is not an error, so special case it here. - if (hr == static_cast<HRESULT>(0x8004CD12)) - return PIPELINE_ERROR_HARDWARE_CONTEXT_RESET; - + MF_MEDIA_ENGINE_ERR media_engine_error) { switch (media_engine_error) { case MF_MEDIA_ENGINE_ERR_NOERROR: return PIPELINE_OK; @@ -136,7 +129,7 @@ MF_MEDIA_ENGINE_ERR error = static_cast<MF_MEDIA_ENGINE_ERR>(param1); HRESULT hr = param2; LOG(ERROR) << __func__ << ": error=" << error << ", hr=" << PrintHr(hr); - error_cb_.Run(MediaEngineErrorToPipelineStatus(error, hr), hr); + error_cb_.Run(MediaEngineErrorToPipelineStatus(error), hr); break; } case MF_MEDIA_ENGINE_EVENT_ENDED:
diff --git a/media/renderers/win/media_foundation_renderer.cc b/media/renderers/win/media_foundation_renderer.cc index 3dad1ae0..c9dc53a1 100644 --- a/media/renderers/win/media_foundation_renderer.cc +++ b/media/renderers/win/media_foundation_renderer.cc
@@ -81,13 +81,21 @@ STRINGIFY(kFailedToSetCurrentTime); STRINGIFY(kFailedToPlay); STRINGIFY(kOnPlaybackError); - STRINGIFY(kOnDCompSurfaceReceivedError); STRINGIFY(kOnDCompSurfaceHandleSetError); STRINGIFY(kOnConnectionError); + STRINGIFY(kFailedToSetDCompMode); + STRINGIFY(kFailedToGetDCompSurface); + STRINGIFY(kFailedToDuplicateHandle); } #undef STRINGIFY } +// INVALID_HANDLE_VALUE is the official invalid handle value. Historically, 0 is +// not used as a handle value too. +bool IsInvalidHandle(const HANDLE& handle) { + return handle == INVALID_HANDLE_VALUE || handle == nullptr; +} + } // namespace // static @@ -518,18 +526,20 @@ HRESULT hr = SetDCompModeInternal(); if (FAILED(hr)) { - std::string error = "Failed to set DComp mode: " + PrintHr(hr); - DLOG(ERROR) << error; - std::move(callback).Run(base::win::ScopedHandle(), error); + OnError(PIPELINE_ERROR_COULD_NOT_RENDER, ErrorReason::kFailedToSetDCompMode, + hr); + std::move(callback).Run(base::win::ScopedHandle(), PrintHr(hr)); return; } HANDLE surface_handle = INVALID_HANDLE_VALUE; hr = GetDCompSurfaceInternal(&surface_handle); - if (FAILED(hr)) { - std::string error = "Failed to get DComp surface: " + PrintHr(hr); - DLOG(ERROR) << error; - std::move(callback).Run(base::win::ScopedHandle(), error); + // The handle could still be invalid after a non failure (e.g. S_FALSE) is + // returned. See https://crbug.com/1307065. + if (FAILED(hr) || IsInvalidHandle(surface_handle)) { + OnError(PIPELINE_ERROR_COULD_NOT_RENDER, + ErrorReason::kFailedToGetDCompSurface, hr); + std::move(callback).Run(base::win::ScopedHandle(), PrintHr(hr)); return; } @@ -540,11 +550,11 @@ const BOOL result = ::DuplicateHandle( process, surface_handle, process, &duplicated_handle, GENERIC_READ | GENERIC_EXECUTE, false, DUPLICATE_CLOSE_SOURCE); - if (!result) { - std::string error = - "Duplicate surface_handle failed: " + PrintHr(::GetLastError()); - DLOG(ERROR) << error; - std::move(callback).Run(base::win::ScopedHandle(), error); + if (!result || IsInvalidHandle(surface_handle)) { + hr = ::GetLastError(); + OnError(PIPELINE_ERROR_COULD_NOT_RENDER, + ErrorReason::kFailedToDuplicateHandle, hr); + std::move(callback).Run(base::win::ScopedHandle(), PrintHr(hr)); return; } @@ -733,9 +743,6 @@ base::UmaHistogramSparse("Media.MediaFoundationRenderer.PlaybackError", hr); - if (status == PIPELINE_ERROR_HARDWARE_CONTEXT_RESET && cdm_proxy_) - cdm_proxy_->OnHardwareContextReset(); - StopSendingStatistics(); OnError(status, ErrorReason::kOnPlaybackError, hr); } @@ -869,10 +876,28 @@ const std::string error = "MediaFoundationRenderer error: " + GetErrorReasonString(reason) + (hresult.has_value() ? (" (" + PrintHr(hresult.value()) + ")") : ""); + DLOG(ERROR) << error; MEDIA_LOG(ERROR, media_log_) << error; ReportErrorReason(reason); - renderer_client_->OnError(status); + + if (!hresult.has_value()) { + renderer_client_->OnError(status); + return; + } + + // HRESULT 0x8004CD12 is DRM_E_TEE_INVALID_HWDRM_STATE, which can happen + // during OS sleep/resume, or moving video to different graphics adapters. + // This is not an error, so special case it here. + PipelineStatus status_to_report = status; + if (hresult == static_cast<HRESULT>(0x8004CD12)) { + status_to_report = PIPELINE_ERROR_HARDWARE_CONTEXT_RESET; + if (cdm_proxy_) + cdm_proxy_->OnHardwareContextReset(); + } + + status_to_report.WithData("hresult", static_cast<uint32_t>(hresult.value())); + renderer_client_->OnError(status_to_report); } void MediaFoundationRenderer::RequestNextFrameBetweenTimestamps(
diff --git a/media/renderers/win/media_foundation_renderer.h b/media/renderers/win/media_foundation_renderer.h index d9cde7b..820284a 100644 --- a/media/renderers/win/media_foundation_renderer.h +++ b/media/renderers/win/media_foundation_renderer.h
@@ -45,16 +45,19 @@ // Reported to UMA. Do not change existing values. enum class ErrorReason { kUnknown = 0, - kCdmProxyReceivedInInvalidState, - kFailedToSetSourceOnMediaEngine, - kFailedToSetCurrentTime, - kFailedToPlay, - kOnPlaybackError, - kOnDCompSurfaceReceivedError, - kOnDCompSurfaceHandleSetError, - kOnConnectionError, + kCdmProxyReceivedInInvalidState = 1, + kFailedToSetSourceOnMediaEngine = 2, + kFailedToSetCurrentTime = 3, + kFailedToPlay = 4, + kOnPlaybackError = 5, + kOnDCompSurfaceReceivedError [[deprecated]] = 6, + kOnDCompSurfaceHandleSetError = 7, + kOnConnectionError = 8, + kFailedToSetDCompMode = 9, + kFailedToGetDCompSurface = 10, + kFailedToDuplicateHandle = 11, // Add new values here and update `kMaxValue`. Never reuse existing values. - kMaxValue = kOnConnectionError, + kMaxValue = kFailedToDuplicateHandle, }; // Report `reason` to UMA. @@ -157,6 +160,7 @@ Microsoft::WRL::ComPtr<MediaEngineNotifyImpl> mf_media_engine_notify_; Microsoft::WRL::ComPtr<MediaEngineExtension> mf_media_engine_extension_; Microsoft::WRL::ComPtr<MediaFoundationSourceWrapper> mf_source_; + // This enables MFMediaEngine to use hardware acceleration for video decoding // and video processing. Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> dxgi_device_manager_;
diff --git a/media/webrtc/audio_processor_controls.h b/media/webrtc/audio_processor_controls.h deleted file mode 100644 index 9acf4ec..0000000 --- a/media/webrtc/audio_processor_controls.h +++ /dev/null
@@ -1,38 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_WEBRTC_AUDIO_PROCESSOR_CONTROLS_H_ -#define MEDIA_WEBRTC_AUDIO_PROCESSOR_CONTROLS_H_ - -#include "base/callback.h" -#include "third_party/webrtc/api/media_stream_interface.h" - -namespace base { -class File; -} - -namespace media { - -class AudioProcessorControls { - public: - using GetStatsCB = base::OnceCallback<void( - const webrtc::AudioProcessorInterface::AudioProcessorStatistics& stats)>; - - // Request the latest stats from the audio processor. Stats are returned - // asynchronously through |callback|. - virtual void GetStats(GetStatsCB callback) = 0; - - // Begin dumping echo cancellation data into |file|. - virtual void StartEchoCancellationDump(base::File file) = 0; - - // Stop any ongoin dump of echo cancellation data. - virtual void StopEchoCancellationDump() = 0; - - protected: - virtual ~AudioProcessorControls() = default; -}; - -} // namespace media - -#endif // MEDIA_WEBRTC_AUDIO_PROCESSOR_CONTROLS_H_
diff --git a/mojo/public/cpp/bindings/associated_interface_ptr_info.h b/mojo/public/cpp/bindings/associated_interface_ptr_info.h index f9aad62..7d98a0e6 100644 --- a/mojo/public/cpp/bindings/associated_interface_ptr_info.h +++ b/mojo/public/cpp/bindings/associated_interface_ptr_info.h
@@ -24,13 +24,9 @@ template <typename Interface> class AssociatedInterfacePtrInfo { public: - AssociatedInterfacePtrInfo() : version_(0u) {} - AssociatedInterfacePtrInfo(std::nullptr_t) : version_(0u) {} - AssociatedInterfacePtrInfo(AssociatedInterfacePtrInfo&& other) - : handle_(std::move(other.handle_)), version_(other.version_) { - other.version_ = 0u; - } + : handle_(std::move(other.handle_)), + version_(std::exchange(other.version_, 0u)) {} AssociatedInterfacePtrInfo(ScopedInterfaceEndpointHandle handle, uint32_t version) @@ -45,8 +41,7 @@ AssociatedInterfacePtrInfo& operator=(AssociatedInterfacePtrInfo&& other) { if (this != &other) { handle_ = std::move(other.handle_); - version_ = other.version_; - other.version_ = 0u; + version_ = std::exchange(other.version_, 0u); } return *this; @@ -54,27 +49,8 @@ bool is_valid() const { return handle_.is_valid(); } - explicit operator bool() const { return handle_.is_valid(); } - - ScopedInterfaceEndpointHandle PassHandle() { - return std::move(handle_); - } - const ScopedInterfaceEndpointHandle& handle() const { return handle_; } - void set_handle(ScopedInterfaceEndpointHandle handle) { - handle_ = std::move(handle); - } - + ScopedInterfaceEndpointHandle PassHandle() { return std::move(handle_); } uint32_t version() const { return version_; } - void set_version(uint32_t version) { version_ = version; } - - bool Equals(const AssociatedInterfacePtrInfo& other) const { - if (this == &other) - return true; - - // Now that the two refer to different objects, they are equivalent if - // and only if they are both invalid. - return !is_valid() && !other.is_valid(); - } private: ScopedInterfaceEndpointHandle handle_;
diff --git a/mojo/public/cpp/bindings/deprecated_interface_types_forward.h b/mojo/public/cpp/bindings/deprecated_interface_types_forward.h index 9d49cc2f..e9b6f7c 100644 --- a/mojo/public/cpp/bindings/deprecated_interface_types_forward.h +++ b/mojo/public/cpp/bindings/deprecated_interface_types_forward.h
@@ -9,8 +9,6 @@ namespace mojo { template <typename Interface> -class InterfacePtrInfo; -template <typename Interface> class AssociatedInterfacePtrInfo; } // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/interface_serialization.h b/mojo/public/cpp/bindings/lib/interface_serialization.h index 2e47896..3cae724 100644 --- a/mojo/public/cpp/bindings/lib/interface_serialization.h +++ b/mojo/public/cpp/bindings/lib/interface_serialization.h
@@ -28,33 +28,6 @@ template <typename Base, typename T> struct Serializer<AssociatedInterfacePtrInfoDataView<Base>, - AssociatedInterfacePtrInfo<T>> { - static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch."); - - static void Serialize(AssociatedInterfacePtrInfo<T>& input, - AssociatedInterface_Data* output, - Message* message) { - DCHECK(!input.handle().is_valid() || input.handle().pending_association()); - SerializeAssociatedInterfaceInfo(input.PassHandle(), input.version(), - *message, *output); - } - - static bool Deserialize(AssociatedInterface_Data* input, - AssociatedInterfacePtrInfo<T>* output, - Message* message) { - auto handle = DeserializeAssociatedEndpointHandle(input->handle, *message); - if (!handle.is_valid()) { - *output = AssociatedInterfacePtrInfo<T>(); - } else { - output->set_handle(std::move(handle)); - output->set_version(input->version); - } - return true; - } -}; - -template <typename Base, typename T> -struct Serializer<AssociatedInterfacePtrInfoDataView<Base>, PendingAssociatedRemote<T>> { static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
diff --git a/mojo/public/cpp/bindings/pending_associated_remote.h b/mojo/public/cpp/bindings/pending_associated_remote.h index be230da..e70fb2a 100644 --- a/mojo/public/cpp/bindings/pending_associated_remote.h +++ b/mojo/public/cpp/bindings/pending_associated_remote.h
@@ -37,11 +37,6 @@ uint32_t version) : handle_(std::move(handle)), version_(version) {} - // Temporary helper for transitioning away from old types. Intentionally an - // implicit constructor. - PendingAssociatedRemote(AssociatedInterfacePtrInfo<Interface>&& ptr_info) - : PendingAssociatedRemote(ptr_info.PassHandle(), ptr_info.version()) {} - // Disabled on NaCl since it crashes old version of clang. #if !BUILDFLAG(IS_NACL) // Move conversion operator for custom remote types. Only participates in @@ -74,12 +69,6 @@ void reset() { handle_.reset(); } - // Temporary helper for transitioning away from old bindings types. This is - // intentionally an implicit conversion. - operator AssociatedInterfacePtrInfo<Interface>() { - return AssociatedInterfacePtrInfo<Interface>(PassHandle(), version()); - } - ScopedInterfaceEndpointHandle PassHandle() { return std::move(handle_); } const ScopedInterfaceEndpointHandle& handle() const { return handle_; } void set_handle(ScopedInterfaceEndpointHandle handle) {
diff --git a/net/base/features.cc b/net/base/features.cc index 51a6af9..ae84020 100644 --- a/net/base/features.cc +++ b/net/base/features.cc
@@ -254,6 +254,8 @@ const base::Feature kPartitionedCookies{"PartitionedCookies", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kPartitionedCookiesBypassOriginTrial{ + "PartitionedCookiesBypassOriginTrial", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kExtraCookieValidityChecks{ "ExtraCookieValidityChecks", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/net/base/features.h b/net/base/features.h index 583fbbb7..4ceedfa 100644 --- a/net/base/features.h +++ b/net/base/features.h
@@ -381,6 +381,11 @@ // be sent when the browser is on the same top-level site that it was on when // the cookie was set. NET_EXPORT extern const base::Feature kPartitionedCookies; +// Flag to bypass the origin trial opt-in to use Partitioned cookies. This +// allows developers to test Partitioned cookies manually in development +// environments. +// TODO(crbug.com/1296161): Remove this feature when the CHIPS OT ends. +NET_EXPORT extern const base::Feature kPartitionedCookiesBypassOriginTrial; // When enabled, additional cookie-related APIs will perform cookie field size // and character set validation to enforce stricter conformance with RFC6265bis.
diff --git a/net/base/prioritized_task_runner.h b/net/base/prioritized_task_runner.h index d97a2bd..086f8c70 100644 --- a/net/base/prioritized_task_runner.h +++ b/net/base/prioritized_task_runner.h
@@ -63,8 +63,8 @@ base::OnceClosure reply, uint32_t priority); - // Similar to base::PostTaskAndReplyWithResult, except that the task runs at - // |priority|. See PostTaskAndReply for a description of |priority|. + // Similar to TaskRunner::PostTaskAndReplyWithResult, except that the task + // runs at |priority|. See PostTaskAndReply for a description of |priority|. template <typename TaskReturnType, typename ReplyArgType> void PostTaskAndReplyWithResult(const base::Location& from_here, base::OnceCallback<TaskReturnType()> task,
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc index f1c0b64..52b5cfba 100644 --- a/net/dns/host_resolver_manager.cc +++ b/net/dns/host_resolver_manager.cc
@@ -673,7 +673,6 @@ host_resolver_flags_( HostResolver::ParametersToHostResolverFlags(parameters_)), priority_(parameters_.initial_priority), - job_(nullptr), resolver_(std::move(resolver)), tick_clock_(tick_clock) {} @@ -686,7 +685,7 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(callback); // Start() may only be called once per request. - DCHECK(!job_); + CHECK(!job_.has_value()); DCHECK(!complete_); DCHECK(!callback_); // Parent HostResolver must still be alive to call Start(). @@ -694,7 +693,7 @@ if (!resolve_context_) { complete_ = true; - resolver_ = nullptr; + resolver_.reset(); set_error_info(ERR_CONTEXT_SHUT_DOWN, false); return ERR_NAME_NOT_RESOLVED; } @@ -703,14 +702,14 @@ int rv = resolver_->Resolve(this); DCHECK(!complete_); if (rv == ERR_IO_PENDING) { - DCHECK(job_); + CHECK(job_.has_value()); callback_ = std::move(callback); } else { - DCHECK(!job_); + CHECK(!job_.has_value()); complete_ = true; LogFinishRequest(rv, false /* async_completion */); } - resolver_ = nullptr; + resolver_.reset(); return rv; } @@ -805,46 +804,25 @@ stale_info_ = std::move(stale_info); } - void AssignJob(Job* job) { - DCHECK(job); - DCHECK(!job_); - - job_ = job; + void AssignJob(base::SafeRef<Job> job) { + CHECK(!job_.has_value()); + job_ = std::move(job); } + bool HasJob() const { return job_.has_value(); } + + // Gets the Job's key. Crashes if no Job has been assigned. + const JobKey& GetJobKey() const; + // Unassigns the Job without calling completion callback. - void OnJobCancelled(Job* job) { - DCHECK_EQ(job_, job); - job_ = nullptr; - DCHECK(!complete_); - DCHECK(callback_); - callback_.Reset(); - - // No results should be set. - DCHECK(!results_); - - LogCancelRequest(); - } + void OnJobCancelled(const JobKey& key); // Cleans up Job assignment, marks request completed, and calls the completion // callback. |is_secure_network_error| indicates whether |error| came from a // secure DNS lookup. - void OnJobCompleted(Job* job, int error, bool is_secure_network_error) { - set_error_info(error, is_secure_network_error); - - DCHECK_EQ(job_, job); - job_ = nullptr; - - DCHECK(!complete_); - complete_ = true; - - LogFinishRequest(error, true /* async_completion */); - - DCHECK(callback_); - std::move(callback_).Run(HostResolver::SquashErrorCode(error)); - } - - Job* job() const { return job_; } + void OnJobCompleted(const JobKey& job_key, + int error, + bool is_secure_network_error); // NetLog for the source, passed in HostResolver::Resolve. const NetLogWithSource& source_net_log() { return source_net_log_; } @@ -969,8 +947,8 @@ RequestPriority priority_; // The resolve job that this request is dependent on. - raw_ptr<Job> job_; - base::WeakPtr<HostResolverManager> resolver_; + absl::optional<base::SafeRef<Job>> job_; + base::WeakPtr<HostResolverManager> resolver_ = nullptr; // The user's callback to invoke when the request completes. CompletionOnceCallback callback_; @@ -2023,6 +2001,10 @@ other.network_isolation_key); } + bool operator==(const JobKey& other) const { + return !(*this < other || other < *this); + } + absl::variant<url::SchemeHostPort, std::string> host; NetworkIsolationKey network_isolation_key; DnsQueryTypeSet query_types; @@ -2112,8 +2094,8 @@ // Log any remaining Requests as cancelled. RequestImpl* req = requests_.head()->value(); req->RemoveFromList(); - DCHECK_EQ(this, req->job()); - req->OnJobCancelled(this); + CHECK(key_ == req->GetJobKey()); + req->OnJobCancelled(key_); } } @@ -2146,7 +2128,7 @@ // separated by scheme/port. DCHECK_EQ(GetHostname(key_.host), GetHostname(request->request_host())); - request->AssignJob(this); + request->AssignJob(weak_ptr_factory_.GetSafeRef()); priority_tracker_.Add(request->priority()); @@ -2376,6 +2358,8 @@ } } + const JobKey& key() const { return key_; } + bool is_queued() const { return !handle_.is_null(); } bool is_running() const { return job_running_; } @@ -2853,14 +2837,14 @@ while (!requests_.empty()) { RequestImpl* req = requests_.head()->value(); req->RemoveFromList(); - DCHECK_EQ(this, req->job()); + CHECK(key_ == req->GetJobKey()); if (results.error() == OK && !req->parameters().is_speculative) { req->set_results( results.CopyWithDefaultPort(GetPort(req->request_host()))); } req->OnJobCompleted( - this, results.error(), + key_, results.error(), secure && results.error() != OK /* is_secure_network_error */); // Check if the resolver was destroyed as a result of running the @@ -3272,7 +3256,7 @@ int HostResolverManager::Resolve(RequestImpl* request) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // Request should not yet have a scheduled Job. - DCHECK(!request->job()); + DCHECK(!request->HasJob()); // Request may only be resolved once. DCHECK(!request->complete()); // MDNS requests do not support skipping cache or stale lookups. @@ -4244,18 +4228,57 @@ HostResolverManager::RequestImpl::~RequestImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!job_) + if (!job_.has_value()) return; - job_->CancelRequest(this); + job_.value()->CancelRequest(this); LogCancelRequest(); } void HostResolverManager::RequestImpl::ChangeRequestPriority( RequestPriority priority) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(job_); - job_->ChangeRequestPriority(this, priority); + CHECK(job_.has_value()); + job_.value()->ChangeRequestPriority(this, priority); +} + +const HostResolverManager::JobKey& HostResolverManager::RequestImpl::GetJobKey() + const { + CHECK(job_.has_value()); + return job_.value()->key(); +} + +void HostResolverManager::RequestImpl::OnJobCancelled(const JobKey& job_key) { + CHECK(job_.has_value()); + CHECK(job_key == job_.value()->key()); + job_.reset(); + DCHECK(!complete_); + DCHECK(callback_); + callback_.Reset(); + + // No results should be set. + DCHECK(!results_); + + LogCancelRequest(); +} + +void HostResolverManager::RequestImpl::OnJobCompleted( + const JobKey& job_key, + int error, + bool is_secure_network_error) { + set_error_info(error, is_secure_network_error); + + CHECK(job_.has_value()); + CHECK(job_key == job_.value()->key()); + job_.reset(); + + DCHECK(!complete_); + complete_ = true; + + LogFinishRequest(error, true /* async_completion */); + + DCHECK(callback_); + std::move(callback_).Run(HostResolver::SquashErrorCode(error)); } } // namespace net
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json index 76ca003c..77d4fdb9 100644 --- a/net/http/transport_security_state_static.json +++ b/net/http/transport_security_state_static.json
@@ -1926,7 +1926,6 @@ { "name": "tracktivity.com.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "twitteroauth.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "vitrado.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "webtalis.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "wevahoo.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "zentralwolke.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "zhovner.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -2378,7 +2377,6 @@ { "name": "xtrim.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yenniferallulli.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yenniferallulli.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "yenniferallulli.es", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yenniferallulli.moda", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yenniferallulli.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "akachanikuji.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -2471,7 +2469,6 @@ { "name": "enterdev.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "ezimoeko.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "eztv.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "feedthebot.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "festember.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "fidelapp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "floweslawncare.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -3224,7 +3221,6 @@ { "name": "tokke.dk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "unapp.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "valopv.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "varvy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "videomail.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "welovemail.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "whispeer.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -3798,7 +3794,6 @@ { "name": "bcsytv.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "beholdthehurricane.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "beranovi.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "betterhelp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "borysek.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "broadsheet.com.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "broersma.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -3943,7 +3938,6 @@ { "name": "0x90.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "alexwardweb.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "atolm.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "avastantivirus.ro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "berst.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "bigbluedoor.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "binaryevolved.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -5243,7 +5237,6 @@ { "name": "wegner.no", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "wilddog.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "wlzhiyin.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "womf.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "wondermags.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "zqhong.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "dorianharmans.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -5334,7 +5327,6 @@ { "name": "beanjuice.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "beardydave.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "beinad.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "bely-mishka.by", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "bendemaree.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "bendingtheending.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "benk.press", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -5583,7 +5575,6 @@ { "name": "gfm.tech", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gigacog.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gijsbertus.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "gjspunk.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "glentakahashi.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "go.ax", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "goalsetup.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -6743,7 +6734,6 @@ { "name": "wittydonut.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "womb.city", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "woording.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "wpac.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "wphostingblog.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "wql.zj.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "xmr.to", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -6871,7 +6861,6 @@ { "name": "kostya.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kunstundunrat.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "laobox.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "learntube.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lemoine.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "leonmahler.consulting", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "leovanna.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -6970,7 +6959,6 @@ { "name": "yagihiro.tech", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "zenithmedia.ca", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "zhendingresources.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "zmy.im", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "3s-hosting.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "7kovrikov.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "adderall.space", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -7529,7 +7517,6 @@ { "name": "bpadvisors.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "bukkenfan.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "bytesystems.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "campfourpaws.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "chaoswebs.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "chiphell.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "chorpinkpoemps.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -8084,7 +8071,6 @@ { "name": "consciousbrand.org.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "consciousbranding.org.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "consciousbrands.net.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "consumer.gov", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "contactbig.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "containerstatistics.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "convocatoriafundacionpepsicomexico.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -8566,7 +8552,6 @@ { "name": "hotting.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "houser.lu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "howbehealthy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "hr-intranet.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "http418.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "huarongdao.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "huersch.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -8583,7 +8568,6 @@ { "name": "ibnuwebhost.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "iceloch.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "icpc2016.in.th", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "icreative.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "ict-concept.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "ideasmeetingpoint.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "idedr.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -8697,7 +8681,6 @@ { "name": "juwairen.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "jym.fit", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "jznet.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "kaangenc.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kaasbijwijn.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kabuabc.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "kadioglumakina.com.tr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -9132,7 +9115,6 @@ { "name": "paulinewesterman.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "paxwinkel.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "paypaq.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "payroll.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "payslipview.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "paytwopay.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "pbapp.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -9606,7 +9588,6 @@ { "name": "theurbanyoga.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "thewindow.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "thierfreund.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "thinktux.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "thirdpartytrade.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "thirty5.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "threatcentral.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -11937,7 +11918,6 @@ { "name": "diezel.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "diff2html.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "diffnow.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "digimagical.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "digwp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "dir2epub.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "dir2epub.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -12149,7 +12129,6 @@ { "name": "hasdf.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "hatethe.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "hcs-company.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "healthjoy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "healtious.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "hearty.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "hearty.space", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -13136,7 +13115,6 @@ { "name": "4-it.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "aigcev.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "adventureally.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "abnarnro.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "anfsanchezo.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "achtzehn.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "3sreporting.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -14455,7 +14433,6 @@ { "name": "storyland.ie", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "stevensheffey.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sobieray.dyndns.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "snip.host", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sperohub.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "soe-server.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "seemeagain.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -14914,7 +14891,6 @@ { "name": "consumerfiles.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "bythisverse.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "chosenplaintext.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "consumidor.gov", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "comerford.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "cganx.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "console.rest", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -15199,7 +15175,6 @@ { "name": "hazyrom.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "get-on.bid", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "harrysmallbones.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "hackerspace-ntnu.no", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "guentherhouse.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gpcsolutions.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "herebedragons.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -17113,7 +17088,6 @@ { "name": "localnetwork.nz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lecourtier.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "livejasmin.dk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "luludapomerania.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "grandchamproofing.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "loforo.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lionlyrics.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -17510,7 +17484,6 @@ { "name": "pixdigital.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "proggersession.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "puneflowermall.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "regain.us", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "progarm.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "raymondelooff.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "probiv.biz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -17776,7 +17749,6 @@ { "name": "suempresa.cloud", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sysadminstory.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "shereallyheals.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "teencounseling.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "texus.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "ssbkk.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tahakomat.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -17877,7 +17849,6 @@ { "name": "thrivewellnesshub.co.za", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tomabrafix.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "toool.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "tomaskavalek.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tuxlife.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "treino.blog.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "topdesk.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -18549,7 +18520,6 @@ { "name": "coding.lv", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "channellife.asia", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "codelitmus.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "craigwfox.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "cnbs.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "coreum.ca", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "confuddledpenguin.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -18926,7 +18896,6 @@ { "name": "feisbed.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gaygeeks.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gdz-otvety.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "feitobrasilcosmeticos.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gdz-spishy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "g1.ie", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gdz.tv", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -18942,7 +18911,6 @@ { "name": "ghrelinblocker.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "genesischangelog.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gatemoves.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "geeks.lgbt", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "getsubs.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "getresilience.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "gflame.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -20081,7 +20049,6 @@ { "name": "souravsaha.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "streamlineautogroup.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "stickswag.cf", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "spykedigital.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sugarshin.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "static-myfxee-808795.c.cdn77.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sporter.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -20241,7 +20208,6 @@ { "name": "top10mountainbikes.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "thedailyupvote.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tretail.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "tlsbv.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "toyotamotala.se", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "treatprostatewithhifu.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tulsameetingroom.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -21880,7 +21846,6 @@ { "name": "bornhack.dk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "bosworthdental.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "bottke.berlin", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "bouah.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "bouchard-mathieux.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "bounceboxspc.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "boutiqueguenaelleverdin.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -22258,7 +22223,6 @@ { "name": "discover-mercure.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "discoverrsv.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "ditch.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "diti.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "diveidc.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "divinegames.studio", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "dj-x.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -24051,7 +24015,6 @@ { "name": "schwarzwald-flirt.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "scintilla.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "scintillating.stream", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "scionasset.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "scorocode.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "scredible.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "screen64.tk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -24737,7 +24700,6 @@ { "name": "yfengs.moe", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yffengshi.ml", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yikeyong.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "yin8888.tv", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yinhe12.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yiz96.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "yob.vn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -24877,7 +24839,6 @@ { "name": "adzuna.sg", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "abn-consultants.ie", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "0005.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "adprospb.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "affordablepapers.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "africantourer.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "3778xl.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -26372,7 +26333,6 @@ { "name": "lithianissaneugeneparts.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "livepaperhelp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "livekort.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "laforetenchantee.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lezard-com.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lexxyn.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "leavesofchangeweekly.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -26407,7 +26367,6 @@ { "name": "lensdoctor.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lachainedesentrepreneurs.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "luffyhair.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "littleqiu.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "livrariahugodesaovitor.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "lojadocristaozinho.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "logopedistalanni.it", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -27128,7 +27087,6 @@ { "name": "routeragency.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sdsmt.engineering", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sanitairwinkel.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "redperegrine.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "schippendale.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "schmaeh-coaching.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "schraugerrun.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -27218,7 +27176,6 @@ { "name": "shiseki.top", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "simonpaarlberg.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "shiatsu-institut.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "shk.im", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "shoptec.sk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "sinefili.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "simonspeich.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -27453,7 +27410,6 @@ { "name": "thesishelp.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tagdocumentary.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "swisscannabis.club", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "thebakingclass.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "tekstschrijvers.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "thekrewserver.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "theosophie-afrique.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -30312,7 +30268,6 @@ { "name": "1396.cc", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "1396.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "247medplan.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "4mybaby.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "69mentor.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "7delights.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "7delights.in", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -31189,7 +31144,6 @@ { "name": "catdecor.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "catgirl.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "cavevinsdefrance.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "centralcountiesservices.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "certmonitor.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "cfno.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "cgsmart.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -31334,7 +31288,6 @@ { "name": "driver61.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "driverscollection.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "drixn.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "drixn.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "drixn.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "drixn.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "droithxn.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -32005,7 +31958,6 @@ { "name": "portsdebalears.gob.es", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "postfalls-naturopathic.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "potworowski.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "pourout.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "powersergthisisthewebsitefuckyouscott.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "pptavmdata.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "praerien-racing.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -32100,7 +32052,6 @@ { "name": "rsm-intern.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "rtenews.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "rtesport.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, - { "name": "rubbleremovalsbenoni.co.za", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "rushiiworks.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "ruskod.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, { "name": "rwky.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true }, @@ -34675,7 +34626,6 @@ { "name": "csfloors.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cu247secure.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cuecamania.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "customizeyourshower.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cyclisjumper.gallery", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cytech.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "czaw.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -34882,7 +34832,6 @@ { "name": "lollaconcept.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lordgun.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lovenwishes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "lucakrebs.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lugui.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "luiscapelo.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "luizkowalski.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -35196,7 +35145,6 @@ { "name": "anconaswine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "andoms.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "andrewensley.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "annotate.software", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "antivirusprotection.reviews", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "apiled.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "apponline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -35442,7 +35390,6 @@ { "name": "lacuevadechauvet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lag-gbr.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lanternalauth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "laraigneedusoir.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "larondinedisinfestazione.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lasuzefc.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "laviedalex.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -35958,7 +35905,6 @@ { "name": "lolnames.gg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lon-so.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lookagain.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "losebellyfat.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lostandcash.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lsvih.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "luckyfrog.hk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -36786,7 +36732,6 @@ { "name": "galle.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ganaenergia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ganasoku.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "gaudeamus-folklor.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "gavinsblog.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "gayukai.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "gazachallenge.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -37703,7 +37648,6 @@ { "name": "weld.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "werkenvoorphiladelphia.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wezl.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "whistler-transfers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "whitehousedrugpolicy.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "whocybered.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "willowtree.school", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -39144,13 +39088,11 @@ { "name": "remedios-caserospara.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "remissan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "reservoirtp.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "resolving.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "restopro.nyc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "retefarmaciecostadamalfi.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "retireyourpassword.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rickvanderzwet.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rody-design.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "roodfruit.studio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "roughcopy.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rpmdrivingschool.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rtzoeller.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -39325,7 +39267,6 @@ { "name": "xujan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yolo-csgo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "youtubeviews.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "ysicing.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yubico.co.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yubico.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yubikey.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -39957,7 +39898,6 @@ { "name": "themefoxx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "themonkeytrail.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thestudyla.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "theuucc.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "think-positive-watches.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thomasscholz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "timbrust.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -39986,7 +39926,6 @@ { "name": "unstamps.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "usage.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "usastaffing.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "uwsoftware.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "uwvloereruit.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vapecrunch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "venenum.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -41820,7 +41759,6 @@ { "name": "mizuho-trade.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mobile-holzofenpizza.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mobilelooper.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "mohela.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "more-terrain.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "morhys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mosshi.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -42007,7 +41945,6 @@ { "name": "teplofom.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "teplomash24.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thebusinessofgoodfilm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "thesimplifiers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thestral.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thestralbot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "think-asia.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -42772,7 +42709,6 @@ { "name": "damonline.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "danielhinterlechner.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "datalife.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "dayofdays.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dearktiel.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "deckbuilderamerica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "deelmijnreis.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -45478,7 +45414,6 @@ { "name": "reflectivity.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "reflexive-engineering.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "reflexive.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "refuelcollective.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "reinaertvandecruys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "repology.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "residentiallocksmithsanantoniotx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -46654,7 +46589,6 @@ { "name": "taizegroep.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tallyfy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tannerryan.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "taoways.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tasks.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tearoomlints.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "techniclab.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -46778,7 +46712,6 @@ { "name": "xn--nf1a578axkh.xn--fiqs8s", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "xn--nrrdetval-v2ab.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "xn--y8jarb5hca.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "xpbytes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "xtips.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ygobbs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yourneighborhub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -47378,7 +47311,6 @@ { "name": "dansdiscounttools.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dappworld.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "davesharpe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "davidforward.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "davidforward.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "davidmn.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dawgs.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -47507,7 +47439,6 @@ { "name": "foundationspecialistmi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fracreazioni.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "frankbellamy.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "free.ac.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "freehao123.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "frontierdiscount.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "frostysummers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -48110,7 +48041,6 @@ { "name": "tecma.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tellcorpassessoria.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "termux.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "tetraetc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "the-woods.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thebarrens.nu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thebestpersonin.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -48689,7 +48619,6 @@ { "name": "rdjb2b.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "reallytrusted.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "redsquirrelcampsite.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "refinansiering.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rentalmed.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "resultsatretail.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "richardharpur.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -49365,7 +49294,6 @@ { "name": "bethpage.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "biddle.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "biegal.ski", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "blackyau.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bliker.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bloglogistics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "blogthedayaway.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -49382,7 +49310,6 @@ { "name": "brouwerijdeblauweijsbeer.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bwf11.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bwf55.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "bwf6.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bwf66.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bwf77.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bwf99.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -49886,7 +49813,6 @@ { "name": "rebelonline.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "receptionpoint.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "reesmichael1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "refuelcreative.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "regresionavidaspasadas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "remax.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "remini.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -50851,7 +50777,6 @@ { "name": "securityzap.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "seg-sys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "seguros-de-salud-y-vida.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "senego.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "senobio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "seoankara.name.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sexflare.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -51268,7 +51193,6 @@ { "name": "lizmooredestinationweddings.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "llemoz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "loanreadycredit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "locomocosec.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "loli.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "longtermcare.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "loteamentomontereiitu.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -52220,7 +52144,6 @@ { "name": "ehrenburg.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "eichler.work", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "einsteincapital.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "electricalfencingbedfordview.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "elfussports.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "eltlaw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "elvcino.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -52280,7 +52203,6 @@ { "name": "floraexpress.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "floridagulfbeachrealty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "floridasexhealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "fnh-expert.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "form3w.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "formsbyair.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fossforward.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -52476,7 +52398,6 @@ { "name": "madisonent-facialplasticsurgery.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "madisonsquarerealestate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "madokami.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "madrecha.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "magnate.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "magravsitalia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mailnara.co.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -53166,7 +53087,6 @@ { "name": "bluesuncamping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bo4tracker.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "botoes-primor.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "braathe.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bramsikkens.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "breznet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "brian-gordon.name", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -53539,7 +53459,6 @@ { "name": "hks-projekt.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "hm773.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "hogarthdavieslloyd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "holadinero.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "holofox.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "hopemeet.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "hopemeet.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -54279,7 +54198,6 @@ { "name": "thomas-klubert.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thomaskaviani.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "threefantasy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "thrush.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tidy.chat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tittelbach.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tobi-server.goip.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -54442,7 +54360,6 @@ { "name": "ylwz.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yokone3-kutikomi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yosida95.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "ysicing.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yuexiangzs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "zagluszaczgps.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "zaltv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -54800,7 +54717,6 @@ { "name": "wrd48.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "xenolith.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "xn--mntsamling-0cb.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "yangcs.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yhhh.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yourbittorrent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yourbittorrent.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -55497,7 +55413,6 @@ { "name": "wotsunduk.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wscore.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wunschpreisauto.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "wuyang.ws", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "xdtag.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "xhotlips.date", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "xinsane.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -56569,7 +56484,6 @@ { "name": "samorazvitie.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sangyoui.health", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sanovnik.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "sanych-msk.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "saorview.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sarahcheyette.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sarkoziadam.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -56581,7 +56495,6 @@ { "name": "schmidtlohwasser.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "schonstedt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "science.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "secondnature.bio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sehablazolano.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sek.ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sektor.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -57101,7 +57014,6 @@ { "name": "scottmay.id.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "secure-computing.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "securefiletransfer.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "securityrussia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "seminariruumid.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "server92.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sexar.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -57793,7 +57705,6 @@ { "name": "shopdongho.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sietejefes.com.ar", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "simulping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "sirihouse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "skillside.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "skorpil.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "smaltimentoamianto.campania.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -58018,7 +57929,6 @@ { "name": "legoutcheznous.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "legyenkianegykereked.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "leignier.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "lenostech.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lepourquoiducomment.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "les-explos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "letsprint3d.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -58215,7 +58125,6 @@ { "name": "797715.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "877791.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "998081.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "abjay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "accreditamento.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "accrosoft.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "achat-volets-roulants.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -58342,7 +58251,6 @@ { "name": "gamerepublic.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "gornergrat-kulm.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "govsurvey.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "grazitti.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ha.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "haaksmadehaanuitvaart.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "haarigerrattenarsch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -58552,7 +58460,6 @@ { "name": "whitehouse.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wildcardcorp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wildcardfederal.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "wugniu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "xrbox.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yachting-home.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yakmail.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -59825,7 +59732,6 @@ { "name": "lucasit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "magicbeanschool.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "marjorie-wiki.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "market-vanna.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "markshroyer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "matthew-cash.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "medik8.com.cy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -59888,7 +59794,6 @@ { "name": "qihl.gg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "qualitation.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "quentin-sauvetre.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "radomir-online.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "radzikow.ski", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rascahan.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "reiciunas.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -59912,7 +59817,6 @@ { "name": "shard.vc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "shiqishidai.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "skynet800.goip.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "smesitel-online.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "smicompact.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "snowreport.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "somnomedics.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -62928,7 +62832,6 @@ { "name": "9397aa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9397b.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9397c.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "9397cc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9397e.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9397f.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9397h.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -62940,7 +62843,6 @@ { "name": "9397oo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9397p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9397pp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "9397q.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9397r.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9397v.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9397ww.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -62949,7 +62851,6 @@ { "name": "9721ff.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9721g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9721j.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "9721l.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9721ll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9721nn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "9721o.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -63025,7 +62926,6 @@ { "name": "adnexa.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ajhstamps.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "alexandercanton.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "allcleaningservice.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "animefire.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "aquamarin.icu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "archematerial.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -63084,7 +62984,6 @@ { "name": "d9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dd5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dd9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "dd9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dd9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "deadmorose.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "denali.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -63131,7 +63030,6 @@ { "name": "ff5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ff9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ff9397.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "ff9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ff9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fhar.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "flowchats.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -63287,7 +63185,6 @@ { "name": "n5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "n9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "n9397.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "n9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "n9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "nadine-birkner.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "nationalservice.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -63319,7 +63216,6 @@ { "name": "osom.finance", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "p5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "p9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "p9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "p9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "paratlantalalkozas.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "partin.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -63350,7 +63246,6 @@ { "name": "quedos.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "r5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "r9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "r9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "r9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "radioradicchio.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "railto.llc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -63473,7 +63368,6 @@ { "name": "w5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "w9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "w9397.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "w9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "w9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "walkhisway.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "walletconnector.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -63486,7 +63380,6 @@ { "name": "wiocha.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ww5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ww9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "ww9397.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ww9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ww9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "x5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -63554,7 +63447,6 @@ { "name": "yy-s.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yy5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yy9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "yy9297.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yy9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yy9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "z5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -64376,14 +64268,10 @@ { "name": "6729f.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729ff.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729g.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "6729g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729gg.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "6729gg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729h.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "6729h.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729hh.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729i.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "6729i.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729ii.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729ipa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729j.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -64391,13 +64279,11 @@ { "name": "6729jj.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729jj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729k.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "6729k.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729kk.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729l.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729ll.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729ll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729m.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "6729nn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729o.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729o.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729oo.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -64408,20 +64294,17 @@ { "name": "6729q.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729q.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729qq.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "6729qq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729r.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729rr.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729rr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729s.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729ss.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "6729ss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729t.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729tt.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729vv.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729w.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729w.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729ww.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "6729ww.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729x.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729x.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729xx.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -64508,7 +64391,6 @@ { "name": "918rw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "987666365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "a291.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "a6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "a6957.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "a6957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "a88fc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -64697,7 +64579,6 @@ { "name": "htmdom.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "huipc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "i6729.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "i6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "i6957.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "i6957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "iamhealthystore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -64732,7 +64613,6 @@ { "name": "jttech.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "k6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "k6957.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "k6957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kaleidlink.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kbc.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kids-world.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -64783,7 +64663,6 @@ { "name": "mouche.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mrichard333.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "n6729.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "n6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "n6957.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "n6957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "netexpat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -65029,7 +64908,6 @@ { "name": "6729mm.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729mm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729n.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "6729n.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729nn.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "6729u.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -65121,7 +64999,6 @@ { "name": "cruicky.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cyber-core.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cybercustodian.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "d6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "d6957.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "daniela-schwarz-individuelle-lebensberatung-coaching.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "danielluisrodriguezs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -65565,7 +65442,6 @@ { "name": "gass-transformatoren.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "gemstn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "genioideal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "gggggg.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "giaoxudongtri.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "glexia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "gobiz.com.my", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -66326,7 +66202,6 @@ { "name": "gynem.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "hannes.paris", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "hellosalmon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "highpressuretech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "horsky.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "housemates.uk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "humanit.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -67353,7 +67228,6 @@ { "name": "perini.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pesdacgh.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "petnow.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "petrovich.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "philanima.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "photosaloncontest.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pignus.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -67399,7 +67273,6 @@ { "name": "stinkefingereinhorn.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "stkildaosteopathy.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "styledbysally.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "sudocat.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "superpi.noip.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "supplypartnersdecolombia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "susthx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -68110,7 +67983,6 @@ { "name": "365y7.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "365y77.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "365y9.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "365y99.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "365zg.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "380111000.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "380111777.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -68936,7 +68808,6 @@ { "name": "euroshop.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "eusarse.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "evadental.institute", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "eveaz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "eventosformativos.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "everberg.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "everglowtrading.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -73546,9 +73417,6 @@ { "name": "nsine.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "octopoos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "octopoos.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "official-sensitive.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "official-sensitive.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "official-sensitive.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "official-sensitive.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ontogenese.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "openwrt-dist.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -73591,7 +73459,6 @@ { "name": "richieheijmans.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ruf888.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "runningandoutdoors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "ryanjarvis.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "s-pro.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "safetysign.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "salnet.wf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -74472,7 +74339,6 @@ { "name": "kirscrb.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kiwibird.tokyo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "klempin.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "klikweb.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "knowledgebuilds.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "krupacars.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ks89.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -75924,7 +75790,6 @@ { "name": "penconsultants.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "philipdeussen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "philipdeussen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "planhub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "platform39.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "playinfinityvr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "plumbingofmesquite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -76189,7 +76054,6 @@ { "name": "centurion-consulting.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "certisoncologysolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chainge-re.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "change-coaching-gmbh.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chicjrajeevalochana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chmc.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chris-siedler.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -76451,7 +76315,6 @@ { "name": "rotate4all.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "royalaubar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ruvinroshan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "rw2.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rythm.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "saiyans.com.ve", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "saledump.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -76885,7 +76748,6 @@ { "name": "odegua.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "oegd.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ololmke.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "omega-marijuana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "omshivalab.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "onlinestoresite.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "opsre.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -77711,7 +77573,6 @@ { "name": "cliffyb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cloudpole.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "clutch.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "codelei.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "comcov.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "connectingrentals.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "connectingrentalsofbethel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -78375,7 +78236,6 @@ { "name": "lucymontebello-arte.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "luisfariasgrupo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "m2h-fiscaliste.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "maichun.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "main-freedom.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "maischances.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "malediven.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -78620,7 +78480,6 @@ { "name": "tommymoya.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tomvanlaer.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "top-autoshop.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "totvs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "toujour.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "transforumation.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "triangle-energie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -78777,7 +78636,6 @@ { "name": "centurion-consulting.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "centurion-consulting.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "checkra.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "chika.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chimpmatic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chr1sbin.works", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cissofitness.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -79060,7 +78918,6 @@ { "name": "pro-lq.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pro-lq.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "prodentalsantacruz.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "proextra.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "prograce.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pupset.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pusehusetkattehotell.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -80987,7 +80844,6 @@ { "name": "fed-shashek.spb.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ferestre-bucuresti.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ferfer.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "finn-thorben.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fiyatgrafik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "flugrecht.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fourwaysplumber24-7.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -81443,7 +81299,6 @@ { "name": "asiaflash.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "aspirevc-prod.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "atelier-des-apprentissages.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "audiencedefolie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "auroraofficefurniture.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "awsnuke.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "b67701.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -81697,7 +81552,6 @@ { "name": "matthewcollins.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "matthewkerley.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "media-store.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "mediabookdb.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "megapl.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "meherpurnews.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mercatoitticosbt.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -81888,7 +81742,6 @@ { "name": "waaifu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wbcasaverde.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "webplace4u.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "weekendcandy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "woohay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wordpress-experts.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wplibrary.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -81897,7 +81750,6 @@ { "name": "xyzyz.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "xyzzyyyz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yearli.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "yhanthydech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "zeit.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "zeit.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "zjy7722.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -82252,7 +82104,6 @@ { "name": "e-bookshelf.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "e4.chat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ebill.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "echo-n.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ecoformeurope.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ecologica.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "eewna.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -82462,7 +82313,6 @@ { "name": "lesacredescouleurs.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "levidromelist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "libertytereconoce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "libertywines.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "libertywines.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lifesavvymedia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lilosaludable.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -82696,7 +82546,6 @@ { "name": "salaminos.no-ip.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "saltaranuncios.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "samisoft.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "sanctum.geek.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sandra-perlbach.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "saorsat.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "saronikos.guide", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -84128,7 +83977,6 @@ { "name": "yoogirls.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yxbet43.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "yyr.im", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "znbr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "zusterjansen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "zwilla.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "11ag8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -85894,7 +85742,6 @@ { "name": "1v9.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "22884.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "22994.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "233.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "3000peaks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "319xpj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "360gpscorp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -86599,7 +86446,6 @@ { "name": "platiniumvapes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "platodecomida.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "plexiglasssheetscuttosize.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "plumbalot.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "plzen-sadrokarton.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pmsg.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pneumatikos.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -86781,7 +86627,6 @@ { "name": "tecnipuntoseguridad.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "telecallsrl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "televisionrepairnearme.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "tencar.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tenckhoff.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tenens.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tentacle.monster", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -88542,7 +88387,6 @@ { "name": "infinitybooksindia.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "injapan.life", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "inkandtonerni.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "insideoutfuel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "investorfare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ip.dog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "irenelove.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -89114,7 +88958,6 @@ { "name": "soziale.email", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "spabellabolivia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "standoffdrop.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "starase.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "steelshop.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "stock-ai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "strefapi.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -90449,7 +90292,6 @@ { "name": "veritalife.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "viacheslavpleshkov.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vincehut.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "vinumenu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vipkit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "visitationbvm.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "visualvolta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -96113,7 +95955,6 @@ { "name": "nsoiran.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "nsrc.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "nssfchile.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "nubunk.com.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "nuclearforum.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "nuclearhell.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "nuclearnation.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -97161,7 +97002,6 @@ { "name": "reyaltec.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rezistor.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rf-gamer.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "rf.studio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rfbcnet.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rfnews.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rfomega.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -99834,7 +99674,6 @@ { "name": "blheritage-tours.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "booksmp3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "borza.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "brianfanzo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "brightsparks.com.sg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bvb.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cabale.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -100668,7 +100507,6 @@ { "name": "proficiodigital.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "protection-plexi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "protection-plexi.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "psicologomogidascruzes.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "psychologue-grenoble.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ptcmonitoring.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "punjabdirectory.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -101300,7 +101138,6 @@ { "name": "w3ctag.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wardpieters.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "web-worker.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "web.hr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "webhr.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wego.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "whatthefoxhat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -101656,7 +101493,6 @@ { "name": "odorucinema.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "oestemc.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ok-test.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "olacatlitter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "omegletalk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "omggo.ph", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "opti-net.solutions", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -102286,7 +102122,6 @@ { "name": "tienic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tinycat99.ws", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "titaniumangel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "tnt.construction", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "topmejores.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "totpolyglot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tralios.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -102793,7 +102628,6 @@ { "name": "red-eyed-tree-frogs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "redearsliderturtles.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "regamega.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "reisenbauer.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rejpaci.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "renam.md", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "reypi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -102820,7 +102654,6 @@ { "name": "sixe.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "slugify.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "smartpos.net.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "smoe.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "somoslaarmenia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sotypicallydutch.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "spackmanimages.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -103547,7 +103380,6 @@ { "name": "aiken.golf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "aim.org.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "aion-beritra.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "akoya.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "aktarma.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "alghadpowersolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "alinol.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -103782,7 +103614,6 @@ { "name": "kescher.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kihi.news", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kingsol.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "kizuki1749.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "klinikum-oldenburg.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "klofteam.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kloftowel.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -105215,7 +105046,6 @@ { "name": "laoudit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "larenas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ldbeauty.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "legowerewolf.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "leo.co.ke", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "letriolet-tignes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "link.sb", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -105240,7 +105070,6 @@ { "name": "matrimonio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mattmorrissound.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mayacoa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "medsanuk.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "medstatix-dev.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "megatorrenthd.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mf58.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -105290,7 +105119,6 @@ { "name": "oralb-prestazioni-odontoiatriche.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "oralbregalaoralb.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "organicseo4u.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "osaka-hero-project.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "osteopathe-voisine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "outervision.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "oversightboard.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -105846,8 +105674,6 @@ { "name": "riosoils.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rivalsa.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rncc.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "roodfruit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "roodfruit.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rubdiavila.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "russiancms.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "s-4.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -106261,7 +106087,6 @@ { "name": "javanie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jaylee.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jcbfshr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "jeevanpaul.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jellebuitenhuis.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jeneratorkiralama.name.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jezebel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -106280,7 +106105,6 @@ { "name": "kissmateszabolcs.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kit.watch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kleincliche.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "knovator.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "koe.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kostenlosepornos.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kotaku.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -107166,7 +106990,6 @@ { "name": "casino.viajes", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "casinos.news", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cbdlession.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "ccamatilfiji.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "certificateoflogistics.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chanakyanewz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chekhov.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -108108,8 +107931,6 @@ { "name": "tafusu-support.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "taylored.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tchatland.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "tci-style.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "tciit.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "team-toranomon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "terraesencial.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "theachero.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -108612,7 +108433,6 @@ { "name": "sayilarmuhendislik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "scrutinizer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sealandair.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "senarin.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sendai-cc.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sendai-cooking.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sensualgoth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -109866,7 +109686,6 @@ { "name": "fincaalegranza.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "findalocalaccountingfirm.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fittwon.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "florianimdahl.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fmovies.qa", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fpsclasico.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "franciscoalpendre.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -110610,7 +110429,6 @@ { "name": "visscher.codes", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vitario.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vitus-meppen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "vusdigital.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "w2me.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wapasrd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wdesign.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -110934,7 +110752,6 @@ { "name": "destiny.gg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "devpost.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "devskyport.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "dfepharma.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dimaweb.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "discountcasino19.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dishwasherrepair-austin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -111283,7 +111100,6 @@ { "name": "makewebbetter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mamba.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "manage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "manopause.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mansour.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "manuelefysiotherapeut.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mapgues.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -111311,7 +111127,6 @@ { "name": "metapsychie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "meteoradar.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mettelenejohansson.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "miaoft.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "michalkozak.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "microfusion.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mimgnj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -111456,7 +111271,6 @@ { "name": "pundix.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pvpserverler.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "q1.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "q9297.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "qhk.ci", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "qiliang.wang", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "qingniantuzhai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -111494,7 +111308,6 @@ { "name": "richmondcountyclerk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rightrasta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ringgitplus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "rip.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "riteboost.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ro.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ro.exchange", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -112345,7 +112158,6 @@ { "name": "imperiocursospro.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "indimaxindia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "insanb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "ireps.gov.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "itarc.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "iteksys.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ivkymppi.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -112635,7 +112447,6 @@ { "name": "atc-fr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "avatype.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "awsl.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "b0x0.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "b2b-leads.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "balicyclingtours.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bamaland.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -112733,7 +112544,6 @@ { "name": "f88vip34.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fairtrade.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fattoriabio.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "fidesic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "files.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "finsify.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "firmfunding.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -113767,7 +113577,6 @@ { "name": "samroelants.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "samuel-philipp.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "santanderassetmanagement.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "scamtested.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "scholarsclub.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "scottandtammy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sdhb.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -113915,7 +113724,6 @@ { "name": "bancoserfinanza.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "banglatypography.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "baysideaba.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "bazar.bg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "belmundo.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "benee-awraham.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "berela.com.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -114320,7 +114128,6 @@ { "name": "thekitchenfarnborough.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thewrightflyer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thiasil.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "timespreader.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "timotheeduran.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tinyhousesforsale-us.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tiwilandcouncil.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -115693,7 +115500,6 @@ { "name": "viktorchinkonsung.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "viktorchinkonsung.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "viperperformance.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "viseum.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vitabsolu.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vivetoluca.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vjshi.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -116823,7 +116629,6 @@ { "name": "mord-ost.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mos-camin.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "motastore.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "motherguru.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "motherhoodinblack.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "motortrend.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "msoffice-inc.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -116883,7 +116688,6 @@ { "name": "osaki.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ostra.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ostracize.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "oversimplifiedstatistics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "oxygenserv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pablofonta.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pac-muenchen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -117742,7 +117546,6 @@ { "name": "j4m.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jakemansfield.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jcse.mil", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "jdinjury.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jetsadabetchoke77.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jimwoodrealty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "jiotvdth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -117865,7 +117668,6 @@ { "name": "medicina-antiage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "medicine-consultant.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "medyascope.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "megatyumen.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "melikoff.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mentorbizlist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "menupay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -117933,7 +117735,6 @@ { "name": "myeducationalplatform.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mylifesphotograph.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mynavi-kaigo.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "mynetpay.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "myphotographytips.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "nachalosbog.bg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "najprzepis.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -118187,9 +117988,7 @@ { "name": "streamonline.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "strikevectorex.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sts-consulting.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "stubbings.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "stubbingsmail.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "stubbmail.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "studboo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "studierttomnoch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sueno.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -119155,7 +118954,6 @@ { "name": "chiromeisjes-boxberg.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chiropraktik-wildner.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chizouworld.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "chjeco.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chollospain.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chosenos.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chrisi-si.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -119273,7 +119071,6 @@ { "name": "cooks.house", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "coolwaterevergreendrilling.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "copenhagenleadtech.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "copewithdata.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "copperexports.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "coqiptv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "corbium.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -119413,7 +119210,6 @@ { "name": "deafsound.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dealsbythebay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dealstreet.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "dealwithstatistics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "deathwarrior.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "debeer.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "decorator.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -120588,7 +120384,6 @@ { "name": "kaiseraerospace.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kaliforniya.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kalinka-shop.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "kalmar.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kaltoft.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kamandula.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kamareddine.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -120997,7 +120792,6 @@ { "name": "matheball.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mathebau.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mathewlane.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "matinataskincare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "matov.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "matrixfm.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "matthewimaniphotography.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -121363,7 +121157,6 @@ { "name": "outbound.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ovalle.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ovallevirtual.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "oversimplifiedeconomics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "overthegate.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ovkerk-avezaath.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "owningless.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -121821,7 +121614,6 @@ { "name": "scstg.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sdfamilycare.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "seanchristian.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "seaspiration.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "secong.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "secretdeals.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sectrans.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -121844,7 +121636,6 @@ { "name": "servingroddick.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "servingupsouthern.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sevacy.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "sevasmos.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "seveiller-simplement.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "seven-seas.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "seven.social", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -123842,7 +123633,6 @@ { "name": "princez.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "probateandplanning.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "prodigibook.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "productosdeteruel.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "prombaza31.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "propertycareincorporated.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "propertymingo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -125476,7 +125266,6 @@ { "name": "kevindustries.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kevinfigueroamusic.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kevinmathiesen.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "kevinmo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "keyblock.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "keyblock.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "keyblock.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -127766,7 +127555,6 @@ { "name": "africanmangoforum.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "agencygood.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "agenter.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "agilepeopleopsframework.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "agiloo.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "agirlknows.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "agnusbostel.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -129059,7 +128847,6 @@ { "name": "m2jest1c.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "m9t.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ma110.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "macstore.pe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "made-nous.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "madechocolaterie.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "madrasareforms.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -129940,7 +129727,6 @@ { "name": "sweatercon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sweetsugarcakes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "swellnote.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "symphony.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "synctechosting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "synthesis.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "syrianair.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -130244,7 +130030,6 @@ { "name": "wonderbox.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wonderbox.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wong-sleweah.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "woodentreasure.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wordcharma.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "workfromhomebusinessopportunities.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "workiva.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -130405,7 +130190,6 @@ { "name": "asociaciones.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "asphy.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "assurance-emprunteur.bzh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "astacreative.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "astoure.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "astralriders.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "astronomiadecolombia.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -130433,7 +130217,6 @@ { "name": "azsupport.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "azsupport.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "b-designer.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "babacsalogato.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "babblefeed.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "babehunt.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "babuccu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -130708,7 +130491,6 @@ { "name": "ebilanzplus.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ebooks4gate.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ediberto.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "edukle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "efran-eliyev.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "efterfest.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "el-tatwer.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -131349,7 +131131,6 @@ { "name": "spiffsearch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "spillefuglen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "spiludennemid.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "spinachcannabis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "spindelnet.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sportcenter.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sportvision.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -131557,7 +131338,6 @@ { "name": "adrieng.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "advancedhealthmedical.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "agamabox.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "aimsoftnet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "aircompressormachine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "airmanproduction.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ak-vsk.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -131875,7 +131655,6 @@ { "name": "californiabudgetfinance.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "camped.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cangku.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "capeprivacy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "capris.cr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cartegrise.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "casinocity.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -132382,7 +132161,6 @@ { "name": "mlpavimentosdehormigonimpreso.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "modderday.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "modernqr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "mohelafederal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mondayaftersunday.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "montre-luxe-occasion.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "montyvlogs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -132627,7 +132405,6 @@ { "name": "thewindowsclub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "thoreauskalendar.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "threeriversopenhouse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "thrivinggracefully.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ticketunity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tiendamaspatchwork.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "timeslive.co.ke", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -135739,7 +135516,6 @@ { "name": "poimenidou.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "politicalscore101.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pomerol-au-coeur.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "poolvilla-margarita.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "pornbabetyra.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "porumbei.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "powerbux.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -136097,7 +135873,6 @@ { "name": "artcatch.art", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "as-aeu-ecp-dev-ecomeeting.azurewebsites.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "as-aeu-ecp-qas-ecomeeting.azurewebsites.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "asklawyersforjustice.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "astronomygcse.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "atalarmedya.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "aureliavelvet.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -137515,7 +137290,6 @@ { "name": "bubbleclips.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bubbleclipsnetwork.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "budgetwebsites.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "burnayavoda.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "burstequity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "burstequity.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "burstequity.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -139810,7 +139584,6 @@ { "name": "ladykarame.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lafeepraline.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lagroza.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "lamedubois-parquet.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lamudi.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lamuixeranga.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lancelucido.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -139969,7 +139742,6 @@ { "name": "masskick.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "masteranimal.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "matematik-ozel-ders.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "mathez.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mavente.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "maxmind-test.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "maxmusic.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -141805,7 +141577,6 @@ { "name": "mhasika.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mi-amigo.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "miao.team", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "miaovps.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "michaelcailloux.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mido4link.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "midrandsplumbing.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -142509,7 +142280,6 @@ { "name": "declared.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "declaredme.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "definethenoise.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "degrootenslot.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "despondentrock.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "detale.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "deusadaseducao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -142599,7 +142369,6 @@ { "name": "finndel.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "flemishopelclub.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "forhims.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "formosus.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "forsbenin.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "freecatz.pe.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "freesteam.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -143806,7 +143575,6 @@ { "name": "immortalelf.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "imrozrum.k12.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "individuals.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "ingenes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "intelly.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "invento.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "investor.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -143953,7 +143721,6 @@ { "name": "magnetremodeling.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mahdi.style", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "maisproduzida.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "makebadge.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "malacat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "maldivestraveller.mv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mamilove.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -145853,7 +145620,6 @@ { "name": "never-mind.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "newbabylon.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "newstj.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "next.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ng.edu.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "nicolaschelly.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "nielsdesign.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -146899,7 +146665,6 @@ { "name": "davidsdika.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dawnbyte.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "deinjoghurt.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "dejongebeth.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "deliuksta.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "delprete.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "departure-transfer-reservation.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -147230,7 +146995,6 @@ { "name": "quickquote.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "quirkycruise.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "radiocommande-forestiere.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "radiopanikos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "radopsec.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "radopsec.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ragstores.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -147758,7 +147522,6 @@ { "name": "mahmoodmehrabi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mahmoodmehrabi.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mail-verifier.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "mangatafestas.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "marcelburger.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "markuspooch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "marmelo.digital", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -148161,7 +147924,6 @@ { "name": "concreteworksplus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "conform.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "congdongvietnhat.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "coronalab.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "cpflsolucoes.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "craftersmarket.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "craftyun.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -149167,7 +148929,6 @@ { "name": "saintshopoficial.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "saiwebtv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "salussafety.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "sandholevets.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "santeracristal.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "savicki.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "savicki.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -149595,7 +149356,6 @@ { "name": "labinsights.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ladbroke.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lakashirdetesek.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "lalunedangkor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "leaguecloud.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "leism.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "leismann.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -150042,7 +149802,6 @@ { "name": "ejkgroep.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ejkinternet.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ejkradio.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "electricalfencingfourways.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "electricfencesouthafrica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "elevatedarborcare.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "emergingindustryassociation.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -150133,7 +149892,6 @@ { "name": "khouloud.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "kiehost.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "koalabur.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "kocovi.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "krafting.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lady-sadieann.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lakashirdetes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -150257,7 +150015,6 @@ { "name": "serve.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "servercore.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "serviciotecnicoencomputacion.com.ve", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "sg-guentersleben.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "shoppingonlinecoffee2u.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "shoveltoss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sidralmundet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -150431,7 +150188,6 @@ { "name": "ayein.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "baas.agency", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "badrap.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "baederlacke.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bankcustomer.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "baratzegrowshop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "barz.link", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -150586,7 +150342,6 @@ { "name": "faroitalia.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fcblueboys.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "femmenordic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "fermenting.studio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ferrarigreen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fh-toolbox.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "fiducoldex.com.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -150923,7 +150678,6 @@ { "name": "realact-sawater.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "realamiga.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "reduxlineberryfactorycart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "reparo.pe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "revolutionhealth.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "rhinesuchus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "riberasalines.cat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -151638,7 +151392,6 @@ { "name": "lymphaticclinic.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lysa-hora.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "m426.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "macbach.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "machelpnashville.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "maker.systems", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "makulatura.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -152155,7 +151908,6 @@ { "name": "alberguecovadonga.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "alelectricista.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "alemautos.com.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "alfagroup-aluminium.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "aliancadesentupidora.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "allattaremoda.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "allianceautomation.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -152653,7 +152405,6 @@ { "name": "potsandplanters.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "powerapp.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ppgod.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "presence-relation.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "prodoric.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "profservice.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "progettoforme.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -152866,7 +152617,6 @@ { "name": "utahonlinedivorce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vanheede.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "variance.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "vasectomie-pierre-boucher.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vaultlabs1226.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vcudu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "velopinion.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -153194,7 +152944,6 @@ { "name": "dring.tf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "drjoesimmigration.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "drsoul.band", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "duckmob.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dumbcryptopunks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dumbmeta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dunassyn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -153499,7 +153248,6 @@ { "name": "mortilki.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ms-rss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mydabb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "myhealthchecked.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mysilvershield.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "n3oneglass-uat.azurewebsites.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "naacam.org.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -153865,7 +153613,6 @@ { "name": "weirdcompany.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "wenablog.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "westcoastdrones.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "westrandgardeningservices.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "what.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "whichphish.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "whizkidpcservices.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -153943,9 +153690,7 @@ { "name": "altcoinfiyatlari.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "amarildoruci.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "amondial.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "ampnuts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "andar-reuma.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "andrew-simon.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "androx.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "androx.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "animaproduksiyon.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -153978,7 +153723,6 @@ { "name": "boulangerie-patisserie.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bouvetmarechal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "brownsville360.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "bx.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "canadapet.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "capslock.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "carstar.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -154294,7 +154038,6 @@ { "name": "sonbilgi.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sontaycamera.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "soopy.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "srealmoreno.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "starrosesandplants.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "statscrew.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "stepin.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -155567,7 +155310,6 @@ { "name": "bigbangco.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bigbeautysecrets.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bigcitylife.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "bikesiliconvalley.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bilhos.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "bilisimdanismani.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "billigflug.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -155859,7 +155601,6 @@ { "name": "chakanaherb.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chaletapartmentrentals.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chaletverzekeringen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "chamberlinfamilyphilanthropy.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chantero.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chaoticevil.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "chaowan.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -156020,7 +155761,6 @@ { "name": "coniglione.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "connectedandsmart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "connectivityparty.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "connectnedcommunity.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "connectnow.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "conpsy.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "consideratio.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -156417,7 +156157,6 @@ { "name": "ecolive.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "economie2.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ecotrade-disinfestazioni.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "eczanemimarlik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ederasrl.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "edging.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "edilondon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -156448,7 +156187,6 @@ { "name": "eimeko.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "eintoepfe-bruchsal.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "eirikyrolae.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "ekaceluller.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ekozercy.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ekspertemerytalny.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ela-n.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -156538,7 +156276,6 @@ { "name": "epoch-film.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "eprint-grimsby.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "eprom.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "equalto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "equilibrium.med.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "equine-dentistry-endoscope.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "equine-dentistry-scope.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -156553,7 +156290,6 @@ { "name": "ericsilva.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ericvantijn.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "eriksen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "error.tools", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "es-ramonage.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "es.gratis", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "escspain.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -157277,7 +157013,6 @@ { "name": "hopepartnershipproject.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "hopi.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "horionimoveis.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "horncastleanglingcentre.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "horoscopo.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "horseridingdurban.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "hoshino-naika.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -157945,7 +157680,6 @@ { "name": "limatolavvocati.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "lindoors.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "linea-nova.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "lingualinx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "linholiver.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "link26.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "link2u.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -158112,7 +157846,6 @@ { "name": "mamtastationers.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "managedcontractors.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "manawa.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "mangaplay.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mangelot-hosting.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "maniasoft.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "manueldelgadohomes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -158260,7 +157993,6 @@ { "name": "mightyfive.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "migrinfo.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mijnonesie.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "mijnzusenik.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mikeschaffnerphotography.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "mikhail-youzhny.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "miki.community", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -159271,7 +159003,6 @@ { "name": "ski-outdoor-shop.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "skill-x.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "skinnation.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "skinplaybeauty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "skinrender.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "skky.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sklep-proekologia.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -159429,7 +159160,6 @@ { "name": "sqrl.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "squareeye.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "squid.gay", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "sscnic.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "sslgram.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ssmd.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ssmwebportal.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -159608,7 +159338,6 @@ { "name": "tedyst.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "teengamingnights.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "teestore.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "tegus.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tehnomagija.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "tekcan.av.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "teknik-sipil.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -160108,7 +159837,6 @@ { "name": "vollenberg.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vonnu.edu.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "voorde.lol", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "vox.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "voxelcat.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vrchat.community", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "vrdennis.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -160147,7 +159875,6 @@ { "name": "webal.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "webcreative.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "webdesigncompanyindia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "webdoxclm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "webeck-information-systems.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "weblightnovel.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "weblocus.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -160485,6 +160212,67 @@ { "name": "go4rest.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "godark.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "zuu.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "10961096.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "10minutesemail.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "18pee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "19gold.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "21hours.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "21pet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "21property.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "21stcenturyoptics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "21up.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "21venture.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "247vision.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24action.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24alarm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24ball.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24control.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24fair.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24fan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24gis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24h.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24hod.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24hunter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24images.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24meg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24read.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24share.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24slot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24status.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "24vod.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2boost.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2cv-co.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2handcar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2head.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2hypeenterprises.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2serious.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2steel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2target.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2tinteractive.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2value.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "2x2x.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "7sec.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "7thcircledesigns.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "801hao.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "802hao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "803hao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "805hao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "807hao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "808mao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "809hao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "80kittens.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "888806.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "97m.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "9ccn.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "9de.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "a-air.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "aaa.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "abacus-marketing.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "abrange.inf.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "abuse.cat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "narodne.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "ncctouring.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, + { "name": "zmsp.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, // END OF 1-YEAR BULK HSTS ENTRIES // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/net/url_request/http_with_dns_over_https_unittest.cc b/net/url_request/http_with_dns_over_https_unittest.cc index c0cb53d..6f30967d 100644 --- a/net/url_request/http_with_dns_over_https_unittest.cc +++ b/net/url_request/http_with_dns_over_https_unittest.cc
@@ -39,6 +39,7 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -80,7 +81,6 @@ public: HttpWithDnsOverHttpsTest() : host_resolver_proc_(new TestHostResolverProc()), - request_context_(true), test_server_(EmbeddedTestServer::Type::TYPE_HTTPS), test_https_requests_served_(0) { EmbeddedTestServer::ServerCertificateConfig cert_config; @@ -108,23 +108,22 @@ manager_options.dns_config_overrides.dns_over_https_config = *DnsOverHttpsConfig::FromString(doh_server_.GetPostOnlyTemplate()); manager_options.dns_config_overrides.use_local_ipv6 = true; - resolver_ = HostResolver::CreateStandaloneContextResolver( + auto resolver = HostResolver::CreateStandaloneContextResolver( /*net_log=*/nullptr, manager_options); // Configure `resolver_` to use `host_resolver_proc_` to resolve // `doh_server_` itself. Additionally, without an explicit HostResolverProc, // HostResolverManager::HaveTestProcOverride disables the built-in DNS // client. - resolver_->SetProcParamsForTesting( + resolver->SetProcParamsForTesting( ProcTaskParams(host_resolver_proc_.get(), 1)); - resolver_->SetRequestContext(&request_context_); - request_context_.set_host_resolver(resolver_.get()); - - request_context_.Init(); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->set_host_resolver(std::move(resolver)); + request_context_ = context_builder->Build(); } - URLRequestContext* context() { return &request_context_; } + URLRequestContext* context() { return request_context_.get(); } std::unique_ptr<test_server::HttpResponse> HandleDefaultRequest( const test_server::HttpRequest& request) { @@ -137,9 +136,8 @@ } protected: - std::unique_ptr<ContextHostResolver> resolver_; scoped_refptr<net::TestHostResolverProc> host_resolver_proc_; - TestURLRequestContext request_context_; + std::unique_ptr<URLRequestContext> request_context_; TestDohServer doh_server_; EmbeddedTestServer test_server_; uint32_t test_https_requests_served_; @@ -202,7 +200,7 @@ // Set up an idle socket. HttpTransactionFactory* transaction_factory = - request_context_.http_transaction_factory(); + request_context_->http_transaction_factory(); HttpStreamFactory::JobFactory default_job_factory; HttpNetworkSession* network_session = transaction_factory->GetSession(); base::RunLoop loop;
diff --git a/net/url_request/report_sender_unittest.cc b/net/url_request/report_sender_unittest.cc index a675f5bb..49d2695 100644 --- a/net/url_request/report_sender_unittest.cc +++ b/net/url_request/report_sender_unittest.cc
@@ -20,6 +20,8 @@ #include "net/test/url_request/url_request_mock_data_job.h" #include "net/test/url_request/url_request_mock_http_job.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_filter.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -202,9 +204,11 @@ class ReportSenderTest : public TestWithTaskEnvironment { public: - ReportSenderTest() : context_(true) { - context_.set_network_delegate(&network_delegate_); - context_.Init(); + ReportSenderTest() { + auto builder = CreateTestURLRequestContextBuilder(); + builder->set_network_delegate( + std::make_unique<TestReportSenderNetworkDelegate>()); + context_ = builder->Build(); } void SetUp() override { @@ -218,7 +222,14 @@ void TearDown() override { URLRequestFilter::GetInstance()->ClearHandlers(); } - TestURLRequestContext* context() { return &context_; } + URLRequestContext* context() { return context_.get(); } + + TestReportSenderNetworkDelegate& network_delegate() { + // This cast is safe because we set a TestReportSenderNetworkDelegate in the + // constructor. + return *static_cast<TestReportSenderNetworkDelegate*>( + context_->network_delegate()); + } protected: void SendReport( @@ -232,15 +243,16 @@ NetworkIsolationKey::CreateTransient(); base::RunLoop run_loop; - network_delegate_.set_url_request_destroyed_callback( + network_delegate().set_url_request_destroyed_callback( run_loop.QuitClosure()); - network_delegate_.set_expect_url(url); - network_delegate_.ExpectReport(report); - network_delegate_.set_expected_content_type("application/foobar"); - network_delegate_.set_expected_network_isolation_key(network_isolation_key); + network_delegate().set_expect_url(url); + network_delegate().ExpectReport(report); + network_delegate().set_expected_content_type("application/foobar"); + network_delegate().set_expected_network_isolation_key( + network_isolation_key); - EXPECT_EQ(request_sequence_number, network_delegate_.num_requests()); + EXPECT_EQ(request_sequence_number, network_delegate().num_requests()); reporter->Send(url, "application/foobar", report, network_isolation_key, std::move(success_callback), std::move(error_callback)); @@ -250,7 +262,7 @@ // sent. run_loop.Run(); - EXPECT_EQ(request_sequence_number + 1, network_delegate_.num_requests()); + EXPECT_EQ(request_sequence_number + 1, network_delegate().num_requests()); } void SendReport(ReportSender* reporter, @@ -262,10 +274,8 @@ base::OnceCallback<void(const GURL&, int, int)>()); } - TestReportSenderNetworkDelegate network_delegate_; - private: - TestURLRequestContext context_; + std::unique_ptr<URLRequestContext> context_; }; // Test that ReportSender::Send creates a URLRequest for the @@ -285,18 +295,18 @@ TEST_F(ReportSenderTest, SendMultipleReportsSimultaneously) { base::RunLoop run_loop; - network_delegate_.set_all_url_requests_destroyed_callback( + network_delegate().set_all_url_requests_destroyed_callback( run_loop.QuitClosure()); GURL url = URLRequestMockDataJob::GetMockHttpsUrl("dummy data", 1); - network_delegate_.set_expect_url(url); - network_delegate_.ExpectReport(kDummyReport); - network_delegate_.ExpectReport(kSecondDummyReport); - network_delegate_.set_expected_content_type("application/foobar"); + network_delegate().set_expect_url(url); + network_delegate().ExpectReport(kDummyReport); + network_delegate().ExpectReport(kSecondDummyReport); + network_delegate().set_expected_content_type("application/foobar"); ReportSender reporter(context(), TRAFFIC_ANNOTATION_FOR_TESTS); - EXPECT_EQ(0u, network_delegate_.num_requests()); + EXPECT_EQ(0u, network_delegate().num_requests()); reporter.Send(url, "application/foobar", kDummyReport, NetworkIsolationKey(), base::OnceCallback<void()>(), @@ -307,23 +317,23 @@ run_loop.Run(); - EXPECT_EQ(2u, network_delegate_.num_requests()); + EXPECT_EQ(2u, network_delegate().num_requests()); } // Test that pending URLRequests get cleaned up when the report sender // is deleted. TEST_F(ReportSenderTest, PendingRequestGetsDeleted) { bool url_request_destroyed = false; - network_delegate_.set_url_request_destroyed_callback(base::BindRepeating( + network_delegate().set_url_request_destroyed_callback(base::BindRepeating( &MarkURLRequestDestroyed, base::Unretained(&url_request_destroyed))); GURL url = URLRequestFailedJob::GetMockHttpUrlWithFailurePhase( URLRequestFailedJob::START, ERR_IO_PENDING); - network_delegate_.set_expect_url(url); - network_delegate_.ExpectReport(kDummyReport); - network_delegate_.set_expected_content_type("application/foobar"); + network_delegate().set_expect_url(url); + network_delegate().ExpectReport(kDummyReport); + network_delegate().set_expected_content_type("application/foobar"); - EXPECT_EQ(0u, network_delegate_.num_requests()); + EXPECT_EQ(0u, network_delegate().num_requests()); std::unique_ptr<ReportSender> reporter( new ReportSender(context(), TRAFFIC_ANNOTATION_FOR_TESTS)); @@ -332,7 +342,7 @@ base::OnceCallback<void(const GURL&, int, int)>()); reporter.reset(); - EXPECT_EQ(1u, network_delegate_.num_requests()); + EXPECT_EQ(1u, network_delegate().num_requests()); EXPECT_TRUE(url_request_destroyed); }
diff --git a/net/url_request/url_fetcher_impl_unittest.cc b/net/url_request/url_fetcher_impl_unittest.cc index 4288aff..9e4dbb08 100644 --- a/net/url_request/url_fetcher_impl_unittest.cc +++ b/net/url_request/url_fetcher_impl_unittest.cc
@@ -48,6 +48,8 @@ #include "net/test/test_with_task_environment.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_fetcher_delegate.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_test_util.h" #include "net/url_request/url_request_throttler_manager.h" @@ -184,45 +186,13 @@ FILE_PATH_LITERAL("net/data/url_request_unittest/BullRunSpeech.txt")); } -// A TestURLRequestContext with a ThrottleManager and a MockHostResolver. -class FetcherTestURLRequestContext : public TestURLRequestContext { - public: - // All requests for |hanging_domain| will hang on host resolution until the - // mock_resolver()->ResolveAllPending() is called. - FetcherTestURLRequestContext( - const std::string& hanging_domain, - std::unique_ptr<ProxyResolutionService> proxy_resolution_service) - : TestURLRequestContext(true), mock_resolver_(new MockHostResolver()) { - mock_resolver_->set_ondemand_mode(true); - mock_resolver_->rules()->AddRule(hanging_domain, "127.0.0.1"); - // Pass ownership to ContextStorage to ensure correct destruction order. - context_storage_.set_host_resolver( - std::unique_ptr<HostResolver>(mock_resolver_)); - context_storage_.set_throttler_manager( - std::make_unique<URLRequestThrottlerManager>()); - context_storage_.set_proxy_resolution_service( - std::move(proxy_resolution_service)); - Init(); - } - - FetcherTestURLRequestContext(const FetcherTestURLRequestContext&) = delete; - FetcherTestURLRequestContext& operator=(const FetcherTestURLRequestContext&) = - delete; - - MockHostResolver* mock_resolver() { return mock_resolver_; } - - private: - raw_ptr<MockHostResolver> mock_resolver_; -}; - class FetcherTestURLRequestContextGetter : public URLRequestContextGetter { public: FetcherTestURLRequestContextGetter( scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, const std::string& hanging_domain) : network_task_runner_(network_task_runner), - hanging_domain_(hanging_domain), - shutting_down_(false) {} + hanging_domain_(hanging_domain) {} FetcherTestURLRequestContextGetter( const FetcherTestURLRequestContextGetter&) = delete; @@ -234,8 +204,14 @@ on_destruction_callback_ = std::move(on_destruction_callback); } + MockHostResolver* mock_resolver() { + DCHECK(context_); + // This cast is safe because we set a MockHostResolver in the constructor. + return static_cast<MockHostResolver*>(context_->host_resolver()); + } + // URLRequestContextGetter: - FetcherTestURLRequestContext* GetURLRequestContext() override { + URLRequestContext* GetURLRequestContext() override { // Calling this on the wrong thread may be either a bug in the test or a bug // in production code. EXPECT_TRUE(network_task_runner_->BelongsToCurrentThread()); @@ -244,8 +220,19 @@ return nullptr; if (!context_) { - context_ = std::make_unique<FetcherTestURLRequestContext>( - hanging_domain_, std::move(proxy_resolution_service_)); + auto mock_resolver = std::make_unique<MockHostResolver>(); + mock_resolver->set_ondemand_mode(true); + mock_resolver->rules()->AddRule(hanging_domain_, "127.0.0.1"); + + auto builder = CreateTestURLRequestContextBuilder(); + builder->set_host_resolver(std::move(mock_resolver)); + builder->set_throttling_enabled(true); + if (proxy_resolution_service_) { + builder->set_proxy_resolution_service( + std::move(proxy_resolution_service_)); + } + + context_ = builder->Build(); } return context_.get(); @@ -310,9 +297,8 @@ context_.reset(); } - // Convenience method to access the context as a FetcherTestURLRequestContext - // without going through GetURLRequestContext. - FetcherTestURLRequestContext* context() { + // Convenience method to access the context. + URLRequestContext* context() { DCHECK(network_task_runner_->BelongsToCurrentThread()); return context_.get(); } @@ -339,8 +325,8 @@ // May be null. std::unique_ptr<ProxyResolutionService> proxy_resolution_service_; - std::unique_ptr<FetcherTestURLRequestContext> context_; - bool shutting_down_; + std::unique_ptr<URLRequestContext> context_; + bool shutting_down_ = false; base::OnceClosure on_destruction_callback_; }; @@ -630,7 +616,7 @@ CreateSameThreadContextGetter()); // Force context creation. context_getter->GetURLRequestContext(); - MockHostResolver* mock_resolver = context_getter->context()->mock_resolver(); + MockHostResolver* mock_resolver = context_getter->mock_resolver(); WaitingURLFetcherDelegate delegate; delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); @@ -652,7 +638,7 @@ CreateSameThreadContextGetter()); // Force context creation. context_getter->GetURLRequestContext(); - MockHostResolver* mock_resolver = context_getter->context()->mock_resolver(); + MockHostResolver* mock_resolver = context_getter->mock_resolver(); WaitingURLFetcherDelegate delegate; delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); @@ -684,7 +670,7 @@ CreateSameThreadContextGetter()); // Force context creation. context_getter->GetURLRequestContext(); - MockHostResolver* mock_resolver = context_getter->context()->mock_resolver(); + MockHostResolver* mock_resolver = context_getter->mock_resolver(); WaitingURLFetcherDelegate delegate; delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter); @@ -730,7 +716,7 @@ CreateSameThreadContextGetter()); // Force context creation. context_getter->GetURLRequestContext(); - MockHostResolver* mock_resolver = context_getter->context()->mock_resolver(); + MockHostResolver* mock_resolver = context_getter->mock_resolver(); WaitingURLFetcherDelegate delegate; delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter);
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc index e210e0d..34bec81 100644 --- a/net/url_request/url_request_context_builder.cc +++ b/net/url_request/url_request_context_builder.cc
@@ -295,6 +295,7 @@ void URLRequestContextBuilder::SetCreateHttpTransactionFactoryCallback( CreateHttpTransactionFactoryCallback create_http_network_transaction_factory) { + http_transaction_factory_.reset(); create_http_network_transaction_factory_ = std::move(create_http_network_transaction_factory); } @@ -563,7 +564,9 @@ http_network_session_params_, network_session_context)); std::unique_ptr<HttpTransactionFactory> http_transaction_factory; - if (!create_http_network_transaction_factory_.is_null()) { + if (http_transaction_factory_) { + http_transaction_factory = std::move(http_transaction_factory_); + } else if (!create_http_network_transaction_factory_.is_null()) { http_transaction_factory = std::move(create_http_network_transaction_factory_) .Run(storage->http_network_session());
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h index 43223f0..4b002f96 100644 --- a/net/url_request/url_request_context_builder.h +++ b/net/url_request/url_request_context_builder.h
@@ -322,6 +322,13 @@ CreateHttpTransactionFactoryCallback create_http_network_transaction_factory); + template <typename T> + T* SetHttpTransactionFactoryForTesting(std::unique_ptr<T> factory) { + create_http_network_transaction_factory_.Reset(); + http_transaction_factory_ = std::move(factory); + return static_cast<T*>(http_transaction_factory_.get()); + } + // Sets a ClientSocketFactory so a test can mock out sockets. This must // outlive the URLRequestContext that will be built. void set_client_socket_factory_for_testing( @@ -387,6 +394,7 @@ HttpCacheParams http_cache_params_; HttpNetworkSessionParams http_network_session_params_; CreateHttpTransactionFactoryCallback create_http_network_transaction_factory_; + std::unique_ptr<HttpTransactionFactory> http_transaction_factory_; base::FilePath transport_security_persister_file_path_; std::vector<std::string> hsts_policy_bypass_list_; raw_ptr<NetLog> net_log_ = nullptr;
diff --git a/net/url_request/url_request_filter_unittest.cc b/net/url_request/url_request_filter_unittest.cc index 91fd91a..2f653dc 100644 --- a/net/url_request/url_request_filter_unittest.cc +++ b/net/url_request/url_request_filter_unittest.cc
@@ -13,6 +13,7 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_interceptor.h" #include "net/url_request/url_request_job.h" #include "net/url_request/url_request_test_job.h" @@ -53,15 +54,15 @@ base::test::TaskEnvironment task_environment( base::test::TaskEnvironment::MainThreadType::IO); TestDelegate delegate; - TestURLRequestContext request_context; + auto context = CreateTestURLRequestContextBuilder()->Build(); URLRequestFilter* filter = URLRequestFilter::GetInstance(); const GURL kUrl1("http://foo.com/"); - std::unique_ptr<URLRequest> request1(request_context.CreateRequest( + std::unique_ptr<URLRequest> request1(context->CreateRequest( kUrl1, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); const GURL kUrl2("http://bar.com/"); - std::unique_ptr<URLRequest> request2(request_context.CreateRequest( + std::unique_ptr<URLRequest> request2(context->CreateRequest( kUrl2, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); // Check AddUrlInterceptor checks for invalid URLs.
diff --git a/net/url_request/url_request_fuzzer.cc b/net/url_request/url_request_fuzzer.cc index 8a191ec6..fca705d 100644 --- a/net/url_request/url_request_fuzzer.cc +++ b/net/url_request/url_request_fuzzer.cc
@@ -17,6 +17,7 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_test_util.h" #include "url/gurl.h" @@ -37,17 +38,18 @@ return 0; FuzzedDataProvider data_provider(data, size); - net::TestURLRequestContext url_request_context(true); + auto context_builder = net::CreateTestURLRequestContextBuilder(); net::FuzzedSocketFactory fuzzed_socket_factory(&data_provider); - url_request_context.set_client_socket_factory(&fuzzed_socket_factory); - url_request_context.Init(); + context_builder->set_client_socket_factory_for_testing( + &fuzzed_socket_factory); + auto url_request_context = context_builder->Build(); net::TestDelegate delegate; std::unique_ptr<net::URLRequest> url_request( - url_request_context.CreateRequest(GURL("http://foo/"), - net::DEFAULT_PRIORITY, &delegate, - TRAFFIC_ANNOTATION_FOR_TESTS)); + url_request_context->CreateRequest(GURL("http://foo/"), + net::DEFAULT_PRIORITY, &delegate, + TRAFFIC_ANNOTATION_FOR_TESTS)); url_request->Start(); // TestDelegate quits the message loop on completion. base::RunLoop().Run();
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc index ac8891c0..52d93b4 100644 --- a/net/url_request/url_request_http_job_unittest.cc +++ b/net/url_request/url_request_http_job_unittest.cc
@@ -52,6 +52,8 @@ #include "net/test/test_with_task_environment.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_test_util.h" #include "net/url_request/websocket_handshake_userdata_key.h" #include "net/websockets/websocket_test_util.h" @@ -131,15 +133,16 @@ class URLRequestHttpJobSetUpSourceTest : public TestWithTaskEnvironment { public: - URLRequestHttpJobSetUpSourceTest() : context_(true) { - context_.set_client_socket_factory(&socket_factory_); - context_.Init(); + URLRequestHttpJobSetUpSourceTest() { + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->set_client_socket_factory_for_testing(&socket_factory_); + context_ = context_builder->Build(); } protected: MockClientSocketFactory socket_factory_; - TestURLRequestContext context_; + std::unique_ptr<URLRequestContext> context_; TestDelegate delegate_; }; @@ -154,8 +157,8 @@ socket_factory_.AddSocketDataProvider(&socket_data); std::unique_ptr<URLRequest> request = - context_.CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY, - &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS); + context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY, + &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS); auto job = std::make_unique<TestURLRequestHttpJob>(request.get()); job->set_use_null_source_stream(true); TestScopedURLInterceptor interceptor(request->url(), std::move(job)); @@ -178,8 +181,8 @@ socket_factory_.AddSocketDataProvider(&socket_data); std::unique_ptr<URLRequest> request = - context_.CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY, - &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS); + context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY, + &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS); auto job = std::make_unique<TestURLRequestHttpJob>(request.get()); TestScopedURLInterceptor interceptor(request->url(), std::move(job)); request->Start(); @@ -197,13 +200,14 @@ class URLRequestHttpJobWithProxy { public: explicit URLRequestHttpJobWithProxy( - std::unique_ptr<ProxyResolutionService> proxy_resolution_service) - : proxy_resolution_service_(std::move(proxy_resolution_service)), - context_(new TestURLRequestContext(true)) { - context_->set_client_socket_factory(&socket_factory_); - context_->set_network_delegate(&network_delegate_); - context_->set_proxy_resolution_service(proxy_resolution_service_.get()); - context_->Init(); + std::unique_ptr<ProxyResolutionService> proxy_resolution_service) { + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->set_client_socket_factory_for_testing(&socket_factory_); + if (proxy_resolution_service) { + context_builder->set_proxy_resolution_service( + std::move(proxy_resolution_service)); + } + context_ = context_builder->Build(); } URLRequestHttpJobWithProxy(const URLRequestHttpJobWithProxy&) = delete; @@ -211,9 +215,7 @@ delete; MockClientSocketFactory socket_factory_; - TestNetworkDelegate network_delegate_; - std::unique_ptr<ProxyResolutionService> proxy_resolution_service_; - std::unique_ptr<TestURLRequestContext> context_; + std::unique_ptr<URLRequestContext> context_; }; // Tests that when proxy is not used, the proxy server is set correctly on the @@ -340,19 +342,37 @@ class URLRequestHttpJobTest : public TestWithTaskEnvironment { protected: - URLRequestHttpJobTest() : context_(true) { - context_.set_http_transaction_factory(&network_layer_); - context_.set_net_log(NetLog::Get()); - context_.Init(); + URLRequestHttpJobTest() { + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + context_builder->set_net_log(NetLog::Get()); + context_ = context_builder->Build(); - req_ = - context_.CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY, - &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS); + req_ = context_->CreateRequest(GURL("http://www.example.com"), + DEFAULT_PRIORITY, &delegate_, + TRAFFIC_ANNOTATION_FOR_TESTS); } - MockNetworkLayer network_layer_; + MockNetworkLayer& network_layer() { + // This cast is safe because we set a MockNetworkLayer in the constructor. + return *static_cast<MockNetworkLayer*>( + context_->http_transaction_factory()); + } - TestURLRequestContext context_; + std::unique_ptr<URLRequest> CreateFirstPartyRequest( + const URLRequestContext& context, + const GURL& url, + URLRequest::Delegate* delegate) { + auto req = context.CreateRequest(url, DEFAULT_PRIORITY, delegate, + TRAFFIC_ANNOTATION_FOR_TESTS); + req->set_initiator(url::Origin::Create(url)); + req->set_site_for_cookies(SiteForCookies::FromUrl(url)); + return req; + } + + std::unique_ptr<URLRequestContext> context_; TestDelegate delegate_; RecordingNetLogObserver net_log_observer_; std::unique_ptr<URLRequest> req_; @@ -360,16 +380,14 @@ class URLRequestHttpJobWithMockSocketsTest : public TestWithTaskEnvironment { protected: - URLRequestHttpJobWithMockSocketsTest() - : context_(new TestURLRequestContext(true)) { - context_->set_client_socket_factory(&socket_factory_); - context_->set_network_delegate(&network_delegate_); - context_->Init(); + URLRequestHttpJobWithMockSocketsTest() { + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->set_client_socket_factory_for_testing(&socket_factory_); + context_ = context_builder->Build(); } MockClientSocketFactory socket_factory_; - TestNetworkDelegate network_delegate_; - std::unique_ptr<TestURLRequestContext> context_; + std::unique_ptr<URLRequestContext> context_; }; TEST_F(URLRequestHttpJobWithMockSocketsTest, @@ -1039,15 +1057,14 @@ } TEST_F(URLRequestHttpJobTest, TestCancelWhileReadingCookies) { - DelayedCookieMonster cookie_monster; - TestURLRequestContext context(true); - context.set_cookie_store(&cookie_monster); - context.Init(); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetCookieStore(std::make_unique<DelayedCookieMonster>()); + auto context = context_builder->Build(); TestDelegate delegate; std::unique_ptr<URLRequest> request = - context.CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY, - &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); + context->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY, + &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); request->Start(); request->Cancel(); @@ -1076,12 +1093,12 @@ req_->url(), std::make_unique<TestURLRequestHttpJob>(req_.get())); req_->SetPriority(LOW); - EXPECT_FALSE(network_layer_.last_transaction()); + EXPECT_FALSE(network_layer().last_transaction()); req_->Start(); - ASSERT_TRUE(network_layer_.last_transaction()); - EXPECT_EQ(LOW, network_layer_.last_transaction()->priority()); + ASSERT_TRUE(network_layer().last_transaction()); + EXPECT_EQ(LOW, network_layer().last_transaction()->priority()); } // Make sure that URLRequestHttpJob passes on its priority updates to @@ -1091,20 +1108,20 @@ req_->url(), std::make_unique<TestURLRequestHttpJob>(req_.get())); req_->SetPriority(LOW); req_->Start(); - ASSERT_TRUE(network_layer_.last_transaction()); - EXPECT_EQ(LOW, network_layer_.last_transaction()->priority()); + ASSERT_TRUE(network_layer().last_transaction()); + EXPECT_EQ(LOW, network_layer().last_transaction()->priority()); req_->SetPriority(HIGHEST); - EXPECT_EQ(HIGHEST, network_layer_.last_transaction()->priority()); + EXPECT_EQ(HIGHEST, network_layer().last_transaction()->priority()); } TEST_F(URLRequestHttpJobTest, HSTSInternalRedirectTest) { // Setup HSTS state. - context_.transport_security_state()->AddHSTS( + context_->transport_security_state()->AddHSTS( "upgrade.test", base::Time::Now() + base::Seconds(10), true); ASSERT_TRUE( - context_.transport_security_state()->ShouldUpgradeToSSL("upgrade.test")); - ASSERT_FALSE(context_.transport_security_state()->ShouldUpgradeToSSL( + context_->transport_security_state()->ShouldUpgradeToSSL("upgrade.test")); + ASSERT_FALSE(context_->transport_security_state()->ShouldUpgradeToSSL( "no-upgrade.test")); struct TestCase { @@ -1134,7 +1151,7 @@ TestDelegate d; TestNetworkDelegate network_delegate; - std::unique_ptr<URLRequest> r(context_.CreateRequest( + std::unique_ptr<URLRequest> r(context_->CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS, is_for_websockets)); @@ -1165,11 +1182,11 @@ https_test.AddDefaultHandlers(base::FilePath()); ASSERT_TRUE(https_test.Start()); - TestURLRequestContext context; - context.transport_security_state()->AddHSTS( + auto context = CreateTestURLRequestContextBuilder()->Build(); + context->transport_security_state()->AddHSTS( "127.0.0.1", base::Time::Now() + base::Seconds(10), true); ASSERT_TRUE( - context.transport_security_state()->ShouldUpgradeToSSL("127.0.0.1")); + context->transport_security_state()->ShouldUpgradeToSSL("127.0.0.1")); GURL::Replacements replace_scheme; replace_scheme.SetSchemeStr("http"); @@ -1183,7 +1200,7 @@ HttpRawRequestHeaders raw_req_headers; - std::unique_ptr<URLRequest> r(context.CreateRequest( + std::unique_ptr<URLRequest> r(context->CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetExtraRequestHeaders(extra_headers); r->SetRequestHeadersCallback(base::BindRepeating( @@ -1214,7 +1231,7 @@ HttpRawRequestHeaders raw_req_headers; - std::unique_ptr<URLRequest> r(context.CreateRequest( + std::unique_ptr<URLRequest> r(context->CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetRequestHeadersCallback(base::BindRepeating( &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers))); @@ -1233,7 +1250,7 @@ HttpRawRequestHeaders raw_req_headers; - std::unique_ptr<URLRequest> r(context.CreateRequest( + std::unique_ptr<URLRequest> r(context->CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetRequestHeadersCallback(base::BindRepeating( &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers))); @@ -1247,17 +1264,17 @@ class URLRequestHttpJobWithBrotliSupportTest : public TestWithTaskEnvironment { protected: - URLRequestHttpJobWithBrotliSupportTest() - : context_(new TestURLRequestContext(true)) { - auto params = std::make_unique<HttpNetworkSessionParams>(); - context_->set_enable_brotli(true); - context_->set_http_network_session_params(std::move(params)); - context_->set_client_socket_factory(&socket_factory_); - context_->Init(); + URLRequestHttpJobWithBrotliSupportTest() { + HttpNetworkSessionParams params; + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->set_enable_brotli(true); + context_builder->set_http_network_session_params(params); + context_builder->set_client_socket_factory_for_testing(&socket_factory_); + context_ = context_builder->Build(); } MockClientSocketFactory socket_factory_; - std::unique_ptr<TestURLRequestContext> context_; + std::unique_ptr<URLRequestContext> context_; }; TEST_F(URLRequestHttpJobWithBrotliSupportTest, NoBrotliAdvertisementOverHttp) { @@ -1393,7 +1410,7 @@ #if BUILDFLAG(IS_ANDROID) TEST_F(URLRequestHttpJobTest, AndroidCleartextPermittedTest) { - context_.set_check_cleartext_permitted(true); + context_->set_check_cleartext_permitted(true); static constexpr struct TestCase { const char* url; @@ -1424,8 +1441,8 @@ TestDelegate delegate; std::unique_ptr<URLRequest> request = - context_.CreateRequest(GURL(test.url), DEFAULT_PRIORITY, &delegate, - TRAFFIC_ANNOTATION_FOR_TESTS); + context_->CreateRequest(GURL(test.url), DEFAULT_PRIORITY, &delegate, + TRAFFIC_ANNOTATION_FOR_TESTS); request->Start(); delegate.RunUntilComplete(); @@ -1450,21 +1467,17 @@ class URLRequestHttpJobWebSocketTest : public TestWithTaskEnvironment { protected: - URLRequestHttpJobWebSocketTest() : context_(true) { - context_.set_network_delegate(&network_delegate_); - context_.set_client_socket_factory(&socket_factory_); - context_.Init(); + URLRequestHttpJobWebSocketTest() { + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->set_client_socket_factory_for_testing(&socket_factory_); + context_ = context_builder->Build(); req_ = - context_.CreateRequest(GURL("ws://www.example.org"), DEFAULT_PRIORITY, - &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS, - /*is_for_websockets=*/true); + context_->CreateRequest(GURL("ws://www.example.org"), DEFAULT_PRIORITY, + &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS, + /*is_for_websockets=*/true); } - // A Network Delegate is required for the WebSocketHandshakeStreamBase - // object to be passed on to the HttpNetworkTransaction. - TestNetworkDelegate network_delegate_; - - TestURLRequestContext context_; + std::unique_ptr<URLRequestContext> context_; MockClientSocketFactory socket_factory_; TestDelegate delegate_; std::unique_ptr<URLRequest> req_; @@ -1550,7 +1563,7 @@ return callback.result().status.IsInclude(); } -void RunRequest(TestURLRequestContext* context, const GURL& url) { +void RunRequest(URLRequestContext* context, const GURL& url) { TestDelegate delegate; std::unique_ptr<URLRequest> request = context->CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); @@ -1568,11 +1581,13 @@ base::HistogramTester histograms; const std::string test_histogram = "Cookie.CookieSchemeRequestScheme"; - CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr, - /*first_party_sets_enabled=*/false); - TestURLRequestContext context(true); - context.set_cookie_store(&cm); - context.Init(); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetCookieStore(std::make_unique<CookieMonster>( + /*store=*/nullptr, /*net_log=*/nullptr, + /*first_party_sets_enabled=*/false)); + auto context = context_builder->Build(); + + auto* cookie_store = static_cast<CookieMonster*>(context->cookie_store()); // Secure set cookie marked as Unset source scheme. // Using port 7 because it fails the transaction without sending a request and @@ -1593,12 +1608,12 @@ unset_cookie1->SetSourceScheme(net::CookieSourceScheme::kUnset); CookieList list1 = {*unset_cookie1}; - EXPECT_TRUE(SetAllCookies(&cm, list1)); - RunRequest(&context, nonsecure_url_for_unset1); + EXPECT_TRUE(SetAllCookies(cookie_store, list1)); + RunRequest(context.get(), nonsecure_url_for_unset1); histograms.ExpectBucketCount( test_histogram, URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 1); - RunRequest(&context, secure_url_for_unset1); + RunRequest(context.get(), secure_url_for_unset1); histograms.ExpectBucketCount( test_histogram, URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 2); @@ -1614,12 +1629,12 @@ unset_cookie2->SetSourceScheme(net::CookieSourceScheme::kUnset); CookieList list2 = {*unset_cookie2}; - EXPECT_TRUE(SetAllCookies(&cm, list2)); - RunRequest(&context, nonsecure_url_for_unset2); + EXPECT_TRUE(SetAllCookies(cookie_store, list2)); + RunRequest(context.get(), nonsecure_url_for_unset2); histograms.ExpectBucketCount( test_histogram, URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 3); - RunRequest(&context, secure_url_for_unset2); + RunRequest(context.get(), secure_url_for_unset2); histograms.ExpectBucketCount( test_histogram, URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 4); @@ -1628,13 +1643,13 @@ GURL nonsecure_url_for_secure_set("http://secureset.example:7"); GURL secure_url_for_secure_set("https://secureset.example:7"); - EXPECT_TRUE( - CreateAndSetCookie(&cm, secure_url_for_secure_set, "SecureScheme=val")); - RunRequest(&context, nonsecure_url_for_secure_set); + EXPECT_TRUE(CreateAndSetCookie(cookie_store, secure_url_for_secure_set, + "SecureScheme=val")); + RunRequest(context.get(), nonsecure_url_for_secure_set); histograms.ExpectBucketCount( test_histogram, URLRequestHttpJob::CookieRequestScheme::kSecureSetNonsecureRequest, 1); - RunRequest(&context, secure_url_for_secure_set); + RunRequest(context.get(), secure_url_for_secure_set); histograms.ExpectBucketCount( test_histogram, URLRequestHttpJob::CookieRequestScheme::kSecureSetSecureRequest, 1); @@ -1643,13 +1658,13 @@ GURL nonsecure_url_for_nonsecure_set("http://nonsecureset.example:7"); GURL secure_url_for_nonsecure_set("https://nonsecureset.example:7"); - EXPECT_TRUE(CreateAndSetCookie(&cm, nonsecure_url_for_nonsecure_set, + EXPECT_TRUE(CreateAndSetCookie(cookie_store, nonsecure_url_for_nonsecure_set, "NonSecureScheme=val")); - RunRequest(&context, nonsecure_url_for_nonsecure_set); + RunRequest(context.get(), nonsecure_url_for_nonsecure_set); histograms.ExpectBucketCount( test_histogram, URLRequestHttpJob::CookieRequestScheme::kNonsecureSetNonsecureRequest, 1); - RunRequest(&context, secure_url_for_nonsecure_set); + RunRequest(context.get(), secure_url_for_nonsecure_set); histograms.ExpectBucketCount( test_histogram, URLRequestHttpJob::CookieRequestScheme::kNonsecureSetSecureRequest, 1); @@ -1661,13 +1676,13 @@ HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); - FilteringTestNetworkDelegate network_delegate; - CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr, - /*first_party_sets_enabled=*/false); - TestURLRequestContext context(true); - context.set_cookie_store(&cm); - context.set_network_delegate(&network_delegate); - context.Init(); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetCookieStore(std::make_unique<CookieMonster>( + /*store=*/nullptr, /*net_log=*/nullptr, + /*first_party_sets_enabled=*/false)); + auto& network_delegate = *context_builder->set_network_delegate( + std::make_unique<FilteringTestNetworkDelegate>()); + auto context = context_builder->Build(); // Set cookies. { @@ -1676,8 +1691,8 @@ "/set-cookie?one=1&" "two=2&" "three=3"); - std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( - test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + std::unique_ptr<URLRequest> req = + CreateFirstPartyRequest(*context, test_url, &d); req->Start(); d.RunUntilComplete(); } @@ -1690,9 +1705,8 @@ // `allow_credentials`, since that skips querying the cookie store). network_delegate.set_force_privacy_mode(true); TestDelegate d; - std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( - test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, - TRAFFIC_ANNOTATION_FOR_TESTS)); + std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( + *context, test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); @@ -1731,15 +1745,15 @@ HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); - FilteringTestNetworkDelegate network_delegate; - network_delegate.set_block_get_cookies_by_name(true); - network_delegate.SetCookieFilter("blocked_"); - CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr, - /*first_party_sets_enabled=*/false); - TestURLRequestContext context(true); - context.set_cookie_store(&cm); - context.set_network_delegate(&network_delegate); - context.Init(); + auto network_delegate = std::make_unique<FilteringTestNetworkDelegate>(); + network_delegate->set_block_get_cookies_by_name(true); + network_delegate->SetCookieFilter("blocked_"); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetCookieStore(std::make_unique<CookieMonster>( + /*store=*/nullptr, /*net_log=*/nullptr, + /*first_party_sets_enabled=*/false)); + context_builder->set_network_delegate(std::move(network_delegate)); + auto context = context_builder->Build(); // Set cookies. { @@ -1748,17 +1762,16 @@ "/set-cookie?blocked_one=1;SameSite=Lax;Secure&" "blocked_two=1;SameSite=Lax;Secure&" "allowed=1;SameSite=Lax;Secure"); - std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( - test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + std::unique_ptr<URLRequest> req = + CreateFirstPartyRequest(*context, test_url, &d); req->Start(); d.RunUntilComplete(); } // Get cookies. TestDelegate d; - std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( - test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, - TRAFFIC_ANNOTATION_FOR_TESTS)); + std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( + *context, test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); @@ -1811,13 +1824,14 @@ https_test.AddDefaultHandlers(base::FilePath()); ASSERT_TRUE(https_test.Start()); - TestURLRequestContext context; - CookieMonster cookie_monster(nullptr, nullptr, - false /* first_party_sets_enabled */); - context.set_cookie_store(&cookie_monster); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetCookieStore(std::make_unique<CookieMonster>( + /*store=*/nullptr, /*net_log=*/nullptr, + /*first_party_sets_enabled=*/false)); + auto context = context_builder->Build(); TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/set-cookie?__Host-foo=bar;SameSite=None;Secure;Path=/" ";Partitioned;"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); @@ -1834,7 +1848,7 @@ { // Test request from the same top-level site. TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kTestIsolationInfo); @@ -1850,7 +1864,7 @@ IsolationInfo::CreateForInternalRequest(kOtherTopFrameOrigin); TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kOtherTestIsolationInfo); @@ -1894,16 +1908,18 @@ first_party_sets.insert(std::make_pair( kOwnerSite, std::set<SchemefulSite>({kOwnerSite, kMemberSite}))); - TestURLRequestContext context; - CookieMonster cookie_monster(nullptr, nullptr, - false /* first_party_sets_enabled */); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto cookie_monster = std::make_unique<CookieMonster>( + /*store=*/nullptr, /*net_log=*/nullptr, + /*first_party_sets_enabled=*/false); auto cookie_access_delegate = std::make_unique<TestCookieAccessDelegate>(); cookie_access_delegate->SetFirstPartySets(first_party_sets); - cookie_monster.SetCookieAccessDelegate(std::move(cookie_access_delegate)); - context.set_cookie_store(&cookie_monster); + cookie_monster->SetCookieAccessDelegate(std::move(cookie_access_delegate)); + context_builder->SetCookieStore(std::move(cookie_monster)); + auto context = context_builder->Build(); TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/set-cookie?__Host-foo=0;SameSite=None;Secure;Path=/" ";Partitioned;"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); @@ -1918,7 +1934,7 @@ // Test the cookie is present in a request with the same top-frame site as // when the cookie was set. TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kOwnerIsolationInfo); @@ -1931,7 +1947,7 @@ // Requests whose top-frame site are in the set should have access to the // partitioned cookie. TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kMemberIsolationInfo); @@ -1941,7 +1957,7 @@ } // Set a cookie from the member site. - req = context.CreateRequest( + req = context->CreateRequest( https_test.GetURL("/set-cookie?__Host-bar=1;SameSite=None;Secure;Path=/" ";Partitioned;"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); @@ -1954,7 +1970,7 @@ // Check request whose top-frame site is the owner site has the cookie set // on the member site. TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kOwnerIsolationInfo); @@ -1968,7 +1984,7 @@ // in the set. If partitioned cookies are disabled, then the cookies should // be available. TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kNonMemberIsolationInfo); @@ -1987,13 +2003,13 @@ https_test.AddDefaultHandlers(base::FilePath()); ASSERT_TRUE(https_test.Start()); - FilteringTestNetworkDelegate network_delegate; - CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr, - /*first_party_sets_enabled=*/false); - TestURLRequestContext context(true); - context.set_cookie_store(&cm); - context.set_network_delegate(&network_delegate); - context.Init(); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetCookieStore( + std::make_unique<CookieMonster>(/*store=*/nullptr, /*net_log=*/nullptr, + /*first_party_sets_enabled=*/false)); + auto& network_delegate = *context_builder->set_network_delegate( + std::make_unique<FilteringTestNetworkDelegate>()); + auto context = context_builder->Build(); const url::Origin kTopFrameOrigin = url::Origin::Create(GURL("https://www.toplevelsite.com")); @@ -2002,7 +2018,7 @@ // Set an unpartitioned and partitioned cookie. TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL( "/set-cookie?__Host-partitioned=0;SameSite=None;Secure;Path=/" ";Partitioned;&__Host-unpartitioned=1;SameSite=None;Secure;Path=/"), @@ -2014,7 +2030,7 @@ { // Get both cookies when privacy mode is disabled. TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kTestIsolationInfo); @@ -2028,7 +2044,7 @@ network_delegate.set_force_privacy_mode(true); network_delegate.set_partitioned_state_allowed(true); TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kTestIsolationInfo); @@ -2063,7 +2079,7 @@ network_delegate.set_force_privacy_mode(true); network_delegate.set_partitioned_state_allowed(false); TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kTestIsolationInfo); @@ -2098,13 +2114,14 @@ https_test.AddDefaultHandlers(base::FilePath()); ASSERT_TRUE(https_test.Start()); - TestURLRequestContext context; - CookieMonster cookie_monster(nullptr, nullptr, - false /* first_party_sets_enabled */); - context.set_cookie_store(&cookie_monster); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetCookieStore( + std::make_unique<CookieMonster>(/*store=*/nullptr, /*net_log=*/nullptr, + /*first_party_sets_enabled=*/false)); + auto context = context_builder->Build(); TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/set-cookie?__Host-foo=bar;SameSite=None;Secure;Path=/" ";Partitioned;"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); @@ -2121,7 +2138,7 @@ { // Test request from the same top-level site. TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?sec-ch-partitioned-cookies"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kTestIsolationInfo); @@ -2141,7 +2158,7 @@ IsolationInfo::CreateForInternalRequest(kOtherTopFrameOrigin); TestDelegate delegate; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( https_test.GetURL("/echoheader?sec-ch-partitioned-cookies"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(kOtherTestIsolationInfo);
diff --git a/net/url_request/url_request_job_factory_unittest.cc b/net/url_request/url_request_job_factory_unittest.cc index 9d2faec..f52c6635 100644 --- a/net/url_request/url_request_job_factory_unittest.cc +++ b/net/url_request/url_request_job_factory_unittest.cc
@@ -15,6 +15,8 @@ #include "net/test/gtest_util.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_job.h" #include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" @@ -58,10 +60,10 @@ base::test::TaskEnvironment task_environment( base::test::TaskEnvironment::MainThreadType::IO); TestDelegate delegate; - TestURLRequestContext request_context; + auto request_context = CreateTestURLRequestContextBuilder()->Build(); std::unique_ptr<URLRequest> request( - request_context.CreateRequest(GURL("foo://bar"), DEFAULT_PRIORITY, - &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); + request_context->CreateRequest(GURL("foo://bar"), DEFAULT_PRIORITY, + &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); base::RunLoop().Run(); @@ -72,31 +74,19 @@ base::test::TaskEnvironment task_environment( base::test::TaskEnvironment::MainThreadType::IO); TestDelegate delegate; - URLRequestJobFactory job_factory; - TestURLRequestContext request_context; - request_context.set_job_factory(&job_factory); - job_factory.SetProtocolHandler("foo", - std::make_unique<DummyProtocolHandler>()); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetProtocolHandler("foo", + std::make_unique<DummyProtocolHandler>()); + auto request_context = context_builder->Build(); std::unique_ptr<URLRequest> request( - request_context.CreateRequest(GURL("foo://bar"), DEFAULT_PRIORITY, - &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); + request_context->CreateRequest(GURL("foo://bar"), DEFAULT_PRIORITY, + &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); base::RunLoop().Run(); EXPECT_EQ(OK, delegate.request_status()); } -TEST(URLRequestJobFactoryTest, DeleteProtocolHandler) { - base::test::TaskEnvironment task_environment( - base::test::TaskEnvironment::MainThreadType::IO); - URLRequestJobFactory job_factory; - TestURLRequestContext request_context; - request_context.set_job_factory(&job_factory); - job_factory.SetProtocolHandler("foo", - std::make_unique<DummyProtocolHandler>()); - job_factory.SetProtocolHandler("foo", nullptr); -} - } // namespace } // namespace net
diff --git a/net/url_request/url_request_job_unittest.cc b/net/url_request/url_request_job_unittest.cc index 51942c4..6eacf022 100644 --- a/net/url_request/url_request_job_unittest.cc +++ b/net/url_request/url_request_job_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include "base/run_loop.h" +#include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "net/base/features.h" #include "net/base/request_priority.h" @@ -18,6 +19,8 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/referrer_policy.h" #include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -291,14 +294,16 @@ using URLRequestJobTest = TestWithTaskEnvironment; TEST_F(URLRequestJobTest, TransactionNoFilter) { - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; std::unique_ptr<URLRequest> req( - context.CreateRequest(GURL(kNoFilterTransaction.url), DEFAULT_PRIORITY, - &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + context->CreateRequest(GURL(kNoFilterTransaction.url), DEFAULT_PRIORITY, + &d, TRAFFIC_ANNOTATION_FOR_TESTS)); AddMockTransaction(&kNoFilterTransaction); req->set_method("GET"); @@ -309,7 +314,7 @@ EXPECT_FALSE(d.request_failed()); EXPECT_EQ(200, req->GetResponseCode()); EXPECT_EQ("hello", d.data_received()); - EXPECT_TRUE(network_layer.done_reading_called()); + EXPECT_TRUE(network_layer->done_reading_called()); // When there's no filter and a Content-Length, expected content size should // be available. EXPECT_EQ(30, req->GetExpectedContentSize()); @@ -318,12 +323,14 @@ } TEST_F(URLRequestJobTest, TransactionNoFilterWithInvalidLength) { - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( GURL(kNoFilterTransactionWithInvalidLength.url), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); AddMockTransaction(&kNoFilterTransactionWithInvalidLength); @@ -336,7 +343,7 @@ EXPECT_FALSE(d.request_failed()); EXPECT_EQ(200, req->GetResponseCode()); EXPECT_EQ("hello", d.data_received()); - EXPECT_TRUE(network_layer.done_reading_called()); + EXPECT_TRUE(network_layer->done_reading_called()); // Invalid Content-Lengths that start with a + should not be reported. EXPECT_EQ(-1, req->GetExpectedContentSize()); @@ -344,14 +351,16 @@ } TEST_F(URLRequestJobTest, TransactionNotifiedWhenDone) { - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; std::unique_ptr<URLRequest> req( - context.CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d, - TRAFFIC_ANNOTATION_FOR_TESTS)); + context->CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d, + TRAFFIC_ANNOTATION_FOR_TESTS)); AddMockTransaction(&kGZipTransaction); req->set_method("GET"); @@ -363,7 +372,7 @@ EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(200, req->GetResponseCode()); EXPECT_EQ("", d.data_received()); - EXPECT_TRUE(network_layer.done_reading_called()); + EXPECT_TRUE(network_layer->done_reading_called()); // When there's a filter and a Content-Length, expected content size should // not be available. EXPECT_EQ(-1, req->GetExpectedContentSize()); @@ -372,14 +381,16 @@ } TEST_F(URLRequestJobTest, SyncTransactionNotifiedWhenDone) { - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; std::unique_ptr<URLRequest> req( - context.CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d, - TRAFFIC_ANNOTATION_FOR_TESTS)); + context->CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d, + TRAFFIC_ANNOTATION_FOR_TESTS)); MockTransaction transaction(kGZipTransaction); transaction.test_mode = TEST_MODE_SYNC_ALL; AddMockTransaction(&transaction); @@ -393,7 +404,7 @@ EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(200, req->GetResponseCode()); EXPECT_EQ("", d.data_received()); - EXPECT_TRUE(network_layer.done_reading_called()); + EXPECT_TRUE(network_layer->done_reading_called()); // When there's a filter and a Content-Length, expected content size should // not be available. EXPECT_EQ(-1, req->GetExpectedContentSize()); @@ -403,14 +414,16 @@ // Tests processing a large gzip header one byte at a time. TEST_F(URLRequestJobTest, SyncSlowTransaction) { - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; std::unique_ptr<URLRequest> req( - context.CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d, - TRAFFIC_ANNOTATION_FOR_TESTS)); + context->CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d, + TRAFFIC_ANNOTATION_FOR_TESTS)); MockTransaction transaction(kGZipTransaction); transaction.test_mode = TEST_MODE_SYNC_ALL | TEST_MODE_SLOW_READ; transaction.handler = &BigGZipServer; @@ -425,21 +438,23 @@ EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(200, req->GetResponseCode()); EXPECT_EQ("", d.data_received()); - EXPECT_TRUE(network_layer.done_reading_called()); + EXPECT_TRUE(network_layer->done_reading_called()); EXPECT_EQ(-1, req->GetExpectedContentSize()); RemoveMockTransaction(&transaction); } TEST_F(URLRequestJobTest, RedirectTransactionNotifiedWhenDone) { - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; std::unique_ptr<URLRequest> req( - context.CreateRequest(GURL(kRedirectTransaction.url), DEFAULT_PRIORITY, - &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + context->CreateRequest(GURL(kRedirectTransaction.url), DEFAULT_PRIORITY, + &d, TRAFFIC_ANNOTATION_FOR_TESTS)); AddMockTransaction(&kRedirectTransaction); req->set_method("GET"); @@ -447,7 +462,7 @@ d.RunUntilComplete(); - EXPECT_TRUE(network_layer.done_reading_called()); + EXPECT_TRUE(network_layer->done_reading_called()); RemoveMockTransaction(&kRedirectTransaction); } @@ -495,14 +510,16 @@ request_headers.c_str(), test.response_headers, &transaction); - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; std::unique_ptr<URLRequest> req( - context.CreateRequest(GURL(transaction.url), DEFAULT_PRIORITY, &d, - TRAFFIC_ANNOTATION_FOR_TESTS)); + context->CreateRequest(GURL(transaction.url), DEFAULT_PRIORITY, &d, + TRAFFIC_ANNOTATION_FOR_TESTS)); AddMockTransaction(&transaction); req->set_referrer_policy(test.original_referrer_policy); @@ -513,7 +530,7 @@ d.RunUntilComplete(); - EXPECT_TRUE(network_layer.done_reading_called()); + EXPECT_TRUE(network_layer->done_reading_called()); RemoveMockTransaction(&transaction); @@ -525,17 +542,19 @@ } TEST_F(URLRequestJobTest, TransactionNotCachedWhenNetworkDelegateRedirects) { - MockNetworkLayer network_layer; - TestNetworkDelegate network_delegate; - network_delegate.set_redirect_on_headers_received_url(GURL("http://foo")); - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); - context.set_network_delegate(&network_delegate); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + auto network_delegate = std::make_unique<TestNetworkDelegate>(); + network_delegate->set_redirect_on_headers_received_url(GURL("http://foo")); + context_builder->DisableHttpCache(); + context_builder->set_network_delegate(std::move(network_delegate)); + auto context = context_builder->Build(); TestDelegate d; std::unique_ptr<URLRequest> req( - context.CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d, - TRAFFIC_ANNOTATION_FOR_TESTS)); + context->CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d, + TRAFFIC_ANNOTATION_FOR_TESTS)); AddMockTransaction(&kGZipTransaction); req->set_method("GET"); @@ -543,7 +562,7 @@ d.RunUntilComplete(); - EXPECT_TRUE(network_layer.stop_caching_called()); + EXPECT_TRUE(network_layer->stop_caching_called()); RemoveMockTransaction(&kGZipTransaction); } @@ -552,12 +571,14 @@ // calling ReadFilteredData. // Regression test for crbug.com/553300. TEST_F(URLRequestJobTest, EmptyBodySkipFilter) { - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( GURL(kEmptyBodyGzipTransaction.url), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); AddMockTransaction(&kEmptyBodyGzipTransaction); @@ -570,19 +591,21 @@ EXPECT_FALSE(d.request_failed()); EXPECT_EQ(200, req->GetResponseCode()); EXPECT_TRUE(d.data_received().empty()); - EXPECT_TRUE(network_layer.done_reading_called()); + EXPECT_TRUE(network_layer->done_reading_called()); RemoveMockTransaction(&kEmptyBodyGzipTransaction); } // Regression test for crbug.com/575213. TEST_F(URLRequestJobTest, InvalidContentGZipTransaction) { - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( GURL(kInvalidContentGZipTransaction.url), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); AddMockTransaction(&kInvalidContentGZipTransaction); @@ -598,21 +621,23 @@ EXPECT_EQ(200, req->GetResponseCode()); EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, d.request_status()); EXPECT_TRUE(d.data_received().empty()); - EXPECT_FALSE(network_layer.done_reading_called()); + EXPECT_FALSE(network_layer->done_reading_called()); RemoveMockTransaction(&kInvalidContentGZipTransaction); } // Regression test for crbug.com/553300. TEST_F(URLRequestJobTest, SlowFilterRead) { - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; std::unique_ptr<URLRequest> req( - context.CreateRequest(GURL(kGzipSlowTransaction.url), DEFAULT_PRIORITY, - &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + context->CreateRequest(GURL(kGzipSlowTransaction.url), DEFAULT_PRIORITY, + &d, TRAFFIC_ANNOTATION_FOR_TESTS)); AddMockTransaction(&kGzipSlowTransaction); req->set_method("GET"); @@ -623,20 +648,22 @@ EXPECT_FALSE(d.request_failed()); EXPECT_EQ(200, req->GetResponseCode()); EXPECT_EQ("hello\n", d.data_received()); - EXPECT_TRUE(network_layer.done_reading_called()); + EXPECT_TRUE(network_layer->done_reading_called()); RemoveMockTransaction(&kGzipSlowTransaction); } TEST_F(URLRequestJobTest, SlowBrotliRead) { - MockNetworkLayer network_layer; - TestURLRequestContext context; - context.set_http_transaction_factory(&network_layer); + auto context_builder = CreateTestURLRequestContextBuilder(); + auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting( + std::make_unique<MockNetworkLayer>()); + context_builder->DisableHttpCache(); + auto context = context_builder->Build(); TestDelegate d; std::unique_ptr<URLRequest> req( - context.CreateRequest(GURL(kBrotliSlowTransaction.url), DEFAULT_PRIORITY, - &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + context->CreateRequest(GURL(kBrotliSlowTransaction.url), DEFAULT_PRIORITY, + &d, TRAFFIC_ANNOTATION_FOR_TESTS)); AddMockTransaction(&kBrotliSlowTransaction); req->set_method("GET"); @@ -647,7 +674,7 @@ EXPECT_FALSE(d.request_failed()); EXPECT_EQ(200, req->GetResponseCode()); EXPECT_EQ(kHelloData, d.data_received()); - EXPECT_TRUE(network_layer.done_reading_called()); + EXPECT_TRUE(network_layer->done_reading_called()); // When there's a filter and a Content-Length, expected content size should // not be available. EXPECT_EQ(-1, req->GetExpectedContentSize());
diff --git a/net/url_request/url_request_quic_perftest.cc b/net/url_request/url_request_quic_perftest.cc index e09ddb94..7ea9e6c 100644 --- a/net/url_request/url_request_quic_perftest.cc +++ b/net/url_request/url_request_quic_perftest.cc
@@ -39,6 +39,8 @@ #include "net/tools/quic/quic_simple_server.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -105,7 +107,6 @@ base::trace_event::InitializeMemoryDumpManagerForInProcessTesting( /*is_coordinator_process=*/false); memory_dump_manager_->set_dumper_registrations_ignored_for_testing(false); - context_ = std::make_unique<TestURLRequestContext>(true); memory_dump_manager_->set_dumper_registrations_ignored_for_testing(true); StartTcpServer(); StartQuicServer(); @@ -113,29 +114,31 @@ // Host mapping. std::unique_ptr<MockHostResolver> resolver(new MockHostResolver()); resolver->rules()->AddRule(kAltSvcHost, "127.0.0.1"); - host_resolver_ = std::make_unique<MappedHostResolver>(std::move(resolver)); + auto host_resolver = + std::make_unique<MappedHostResolver>(std::move(resolver)); std::string map_rule = base::StringPrintf("MAP %s 127.0.0.1:%d", kOriginHost, tcp_server_->port()); - EXPECT_TRUE(host_resolver_->AddRuleFromString(map_rule)); + EXPECT_TRUE(host_resolver->AddRuleFromString(map_rule)); net::HttpNetworkSessionContext network_session_context; - network_session_context.cert_verifier = &cert_verifier_; - auto params = std::make_unique<HttpNetworkSessionParams>(); - params->enable_quic = true; - params->enable_user_alternate_protocol_ports = true; - quic_context_.params()->allow_remote_alt_svc = true; - context_->set_host_resolver(host_resolver_.get()); - context_->set_http_network_session_params(std::move(params)); - context_->set_cert_verifier(&cert_verifier_); - context_->set_quic_context(&quic_context_); - context_->Init(); + HttpNetworkSessionParams params; + params.enable_quic = true; + params.enable_user_alternate_protocol_ports = true; + auto quic_context = std::make_unique<QuicContext>(); + quic_context->params()->allow_remote_alt_svc = true; + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->set_host_resolver(std::move(host_resolver)); + context_builder->set_http_network_session_params(params); + context_builder->SetCertVerifier(std::make_unique<MockCertVerifier>()); + context_builder->set_quic_context(std::move(quic_context)); + context_ = context_builder->Build(); } void TearDown() override { if (quic_server_) { quic_server_->Shutdown(); - // If possible, deliver the conncetion close packet to the client before - // destruct the TestURLRequestContext. + // If possible, deliver the connection close packet to the client before + // destruct the URLRequestContext. base::RunLoop().RunUntilIdle(); } // |tcp_server_| shuts down in EmbeddedTestServer destructor. @@ -169,8 +172,8 @@ verify_result.verified_cert = ImportCertFromFile( GetTestCertsDirectory(), "quic-chain.pem"); verify_result.is_issued_by_known_root = true; - cert_verifier_.AddResultForCert(verify_result.verified_cert.get(), - verify_result, OK); + cert_verifier().AddResultForCert(verify_result.verified_cert.get(), + verify_result, OK); } void StartTcpServer() { @@ -181,19 +184,21 @@ CertVerifyResult verify_result; verify_result.verified_cert = tcp_server_->GetCertificate(); - cert_verifier_.AddResultForCert(tcp_server_->GetCertificate(), - verify_result, OK); + cert_verifier().AddResultForCert(tcp_server_->GetCertificate(), + verify_result, OK); + } + + MockCertVerifier& cert_verifier() { + // This cast is safe because we set a MockCertVerifier in the constructor. + return *static_cast<MockCertVerifier*>(context_->cert_verifier()); } std::unique_ptr<base::trace_event::MemoryDumpManager> memory_dump_manager_; - std::unique_ptr<MappedHostResolver> host_resolver_; std::unique_ptr<EmbeddedTestServer> tcp_server_; std::unique_ptr<QuicSimpleServer> quic_server_; std::unique_ptr<base::test::SingleThreadTaskEnvironment> task_environment_; - std::unique_ptr<TestURLRequestContext> context_; + std::unique_ptr<URLRequestContext> context_; quic::QuicMemoryCacheBackend memory_cache_backend_; - MockCertVerifier cert_verifier_; - QuicContext quic_context_; }; void CheckScalarInDump(const MemoryAllocatorDump* dump,
diff --git a/net/url_request/url_request_quic_unittest.cc b/net/url_request/url_request_quic_unittest.cc index 9568fcae..e64f233 100644 --- a/net/url_request/url_request_quic_unittest.cc +++ b/net/url_request/url_request_quic_unittest.cc
@@ -39,6 +39,8 @@ #include "net/tools/quic/quic_simple_server.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -120,58 +122,56 @@ : public TestWithTaskEnvironment, public ::testing::WithParamInterface<quic::ParsedQuicVersion> { protected: - URLRequestQuicTest() : context_(new TestURLRequestContext(true)) { + URLRequestQuicTest() + : context_builder_(CreateTestURLRequestContextBuilder()) { QuicEnableVersion(version()); StartQuicServer(version()); - auto params = std::make_unique<HttpNetworkSessionParams>(); + HttpNetworkSessionParams params; CertVerifyResult verify_result; verify_result.verified_cert = ImportCertFromFile( GetTestCertsDirectory(), "quic-chain.pem"); - cert_verifier_.AddResultForCertAndHost(verify_result.verified_cert.get(), + auto cert_verifier = std::make_unique<MockCertVerifier>(); + cert_verifier->AddResultForCertAndHost(verify_result.verified_cert.get(), kTestServerHost, verify_result, OK); // To simplify the test, and avoid the race with the HTTP request, we force // QUIC for these requests. - context_->set_quic_context(&quic_context_); - quic_context_.params()->supported_versions = {version()}; - quic_context_.params()->origins_to_force_quic_on.insert( + auto quic_context = std::make_unique<QuicContext>(); + quic_context->params()->supported_versions = {version()}; + quic_context->params()->origins_to_force_quic_on.insert( HostPortPair(kTestServerHost, 443)); - params->enable_quic = true; - params->enable_server_push_cancellation = true; - context_->set_host_resolver(host_resolver_.get()); - context_->set_http_network_session_params(std::move(params)); - context_->set_cert_verifier(&cert_verifier_); - context_->set_net_log(NetLog::Get()); - transport_security_state_.SetExpectCTReporter(&expect_ct_reporter_); - context_->set_transport_security_state(&transport_security_state_); + context_builder_->set_quic_context(std::move(quic_context)); + params.enable_quic = true; + params.enable_server_push_cancellation = true; + context_builder_->set_host_resolver(std::move(host_resolver_)); + context_builder_->set_http_network_session_params(params); + context_builder_->SetCertVerifier(std::move(cert_verifier)); + context_builder_->set_net_log(NetLog::Get()); } void TearDown() override { if (server_) { server_->Shutdown(); - // If possible, deliver the conncetion close packet to the client before - // destruct the TestURLRequestContext. base::RunLoop().RunUntilIdle(); } } - // Sets a NetworkDelegate to use for |context_|. Must be done before Init(). - void SetNetworkDelegate(NetworkDelegate* network_delegate) { - context_->set_network_delegate(network_delegate); + URLRequestContextBuilder* context_builder() { return context_builder_.get(); } + + std::unique_ptr<URLRequestContext> BuildContext() { + auto context = context_builder_->Build(); + + context->transport_security_state()->SetExpectCTReporter( + &expect_ct_reporter_); + return context; } - // Can be used to modify |context_|. Only safe to modify before Init() is - // called. - TestURLRequestContext* context() { return context_.get(); } - - // Initializes the TestURLRequestContext |context_|. - void Init() { context_->Init(); } - - std::unique_ptr<URLRequest> CreateRequest(const GURL& url, - RequestPriority priority, - URLRequest::Delegate* delegate) { - return context_->CreateRequest(url, priority, delegate, - TRAFFIC_ANNOTATION_FOR_TESTS); + static std::unique_ptr<URLRequest> CreateRequest( + URLRequestContext* context, + const GURL& url, + URLRequest::Delegate* delegate) { + return context->CreateRequest(url, DEFAULT_PRIORITY, delegate, + TRAFFIC_ANNOTATION_FOR_TESTS); } unsigned int GetRstErrorCountReceivedByServer( @@ -212,10 +212,6 @@ MockExpectCTReporter* expect_ct_reporter() { return &expect_ct_reporter_; } - TransportSecurityState* transport_security_state() { - return &transport_security_state_; - } - protected: // Returns a fully-qualified URL for |path| on the test server. std::string UrlFromPath(base::StringPiece path) { @@ -279,14 +275,11 @@ } MockExpectCTReporter expect_ct_reporter_; - TransportSecurityState transport_security_state_; std::unique_ptr<MappedHostResolver> host_resolver_; std::unique_ptr<QuicSimpleServer> server_; - std::unique_ptr<TestURLRequestContext> context_; - QuicContext quic_context_; quic::QuicMemoryCacheBackend memory_cache_backend_; - MockCertVerifier cert_verifier_; + std::unique_ptr<URLRequestContextBuilder> context_builder_; QuicFlagSaver flags_; // Save/restore all QUIC flag values. }; @@ -376,10 +369,10 @@ ::testing::PrintToStringParamName()); TEST_P(URLRequestQuicTest, TestGetRequest) { - Init(); + auto context = BuildContext(); CheckLoadTimingDelegate delegate(false); std::unique_ptr<URLRequest> request = - CreateRequest(GURL(UrlFromPath(kHelloPath)), DEFAULT_PRIORITY, &delegate); + CreateRequest(context.get(), GURL(UrlFromPath(kHelloPath)), &delegate); request->Start(); ASSERT_TRUE(request->is_pending()); @@ -394,10 +387,10 @@ // should not have |LoadTimingInfo::connect_timing|. TEST_P(URLRequestQuicTest, TestTwoRequests) { base::RunLoop run_loop; - WaitForCompletionNetworkDelegate network_delegate( - run_loop.QuitClosure(), /*num_expected_requests=*/2); - SetNetworkDelegate(&network_delegate); - Init(); + context_builder()->set_network_delegate( + std::make_unique<WaitForCompletionNetworkDelegate>( + run_loop.QuitClosure(), /*num_expected_requests=*/2)); + auto context = BuildContext(); GURL url = GURL(UrlFromPath(kHelloPath)); auto isolation_info = @@ -406,13 +399,13 @@ CheckLoadTimingDelegate delegate(false); delegate.set_on_complete(base::DoNothing()); std::unique_ptr<URLRequest> request = - CreateRequest(url, DEFAULT_PRIORITY, &delegate); + CreateRequest(context.get(), url, &delegate); request->set_isolation_info(isolation_info); CheckLoadTimingDelegate delegate2(true); delegate2.set_on_complete(base::DoNothing()); std::unique_ptr<URLRequest> request2 = - CreateRequest(url, DEFAULT_PRIORITY, &delegate2); + CreateRequest(context.get(), url, &delegate2); request2->set_isolation_info(isolation_info); request->Start(); @@ -428,15 +421,14 @@ } TEST_P(URLRequestQuicTest, RequestHeadersCallback) { - Init(); + auto context = BuildContext(); HttpRawRequestHeaders raw_headers; TestDelegate delegate; - TestURLRequestContext context; HttpRequestHeaders extra_headers; extra_headers.SetHeader("X-Foo", "bar"); std::unique_ptr<URLRequest> request = - CreateRequest(GURL(UrlFromPath(kHelloPath)), DEFAULT_PRIORITY, &delegate); + CreateRequest(context.get(), GURL(UrlFromPath(kHelloPath)), &delegate); request->SetExtraRequestHeaders(extra_headers); request->SetRequestHeadersCallback( @@ -477,20 +469,20 @@ // disabled_features {}); - MockCTPolicyEnforcerNonCompliant ct_enforcer; - context()->set_ct_policy_enforcer(&ct_enforcer); - Init(); + context_builder()->set_ct_policy_enforcer( + std::make_unique<MockCTPolicyEnforcerNonCompliant>()); + auto context = BuildContext(); GURL report_uri("https://report.test/"); IsolationInfo isolation_info = IsolationInfo::CreateTransient(); - transport_security_state()->AddExpectCT( + context->transport_security_state()->AddExpectCT( kTestServerHost, base::Time::Now() + base::Days(1), true /* enforce */, report_uri, isolation_info.network_isolation_key()); base::RunLoop run_loop; TestDelegate delegate; std::unique_ptr<URLRequest> request = - CreateRequest(GURL(UrlFromPath(kHelloPath)), DEFAULT_PRIORITY, &delegate); + CreateRequest(context.get(), GURL(UrlFromPath(kHelloPath)), &delegate); request->set_isolation_info(isolation_info); request->Start(); delegate.RunUntilComplete();
diff --git a/net/url_request/url_request_throttler_simulation_unittest.cc b/net/url_request/url_request_throttler_simulation_unittest.cc index edb0cf7a..e55ed330 100644 --- a/net/url_request/url_request_throttler_simulation_unittest.cc +++ b/net/url_request/url_request_throttler_simulation_unittest.cc
@@ -26,6 +26,7 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_test_util.h" #include "net/url_request/url_request_throttler_manager.h" #include "net/url_request/url_request_throttler_test_support.h" @@ -125,10 +126,11 @@ num_current_tick_queries_(0), num_overloaded_ticks_(0), max_experienced_queries_per_tick_(0), - mock_request_(context_.CreateRequest(GURL(), - DEFAULT_PRIORITY, - nullptr, - TRAFFIC_ANNOTATION_FOR_TESTS)) {} + context_(CreateTestURLRequestContextBuilder()->Build()), + mock_request_(context_->CreateRequest(GURL(), + DEFAULT_PRIORITY, + nullptr, + TRAFFIC_ANNOTATION_FOR_TESTS)) {} Server(const Server&) = delete; Server& operator=(const Server&) = delete; @@ -283,7 +285,7 @@ return output; } - const URLRequestContext& context() const { return context_; } + const URLRequestContext& context() const { return *context_; } private: TimeTicks now_; @@ -297,7 +299,7 @@ int max_experienced_queries_per_tick_; std::vector<int> requests_per_tick_; - TestURLRequestContext context_; + std::unique_ptr<URLRequestContext> context_; std::unique_ptr<URLRequest> mock_request_; };
diff --git a/net/url_request/url_request_throttler_unittest.cc b/net/url_request/url_request_throttler_unittest.cc index 6b34917..2f748210 100644 --- a/net/url_request/url_request_throttler_unittest.cc +++ b/net/url_request/url_request_throttler_unittest.cc
@@ -16,6 +16,7 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_test_util.h" #include "net/url_request/url_request_throttler_manager.h" #include "net/url_request/url_request_throttler_test_support.h" @@ -160,10 +161,11 @@ class URLRequestThrottlerEntryTest : public TestWithTaskEnvironment { protected: URLRequestThrottlerEntryTest() - : request_(context_.CreateRequest(GURL(), - DEFAULT_PRIORITY, - nullptr, - TRAFFIC_ANNOTATION_FOR_TESTS)) {} + : context_(CreateTestURLRequestContextBuilder()->Build()), + request_(context_->CreateRequest(GURL(), + DEFAULT_PRIORITY, + nullptr, + TRAFFIC_ANNOTATION_FOR_TESTS)) {} void SetUp() override; @@ -171,7 +173,7 @@ MockURLRequestThrottlerManager manager_; // Dummy object, not used. scoped_refptr<MockURLRequestThrottlerEntry> entry_; - TestURLRequestContext context_; + std::unique_ptr<URLRequestContext> context_; std::unique_ptr<URLRequest> request_; }; @@ -312,15 +314,16 @@ class URLRequestThrottlerManagerTest : public TestWithTaskEnvironment { protected: URLRequestThrottlerManagerTest() - : request_(context_.CreateRequest(GURL(), - DEFAULT_PRIORITY, - nullptr, - TRAFFIC_ANNOTATION_FOR_TESTS)) {} + : context_(CreateTestURLRequestContextBuilder()->Build()), + request_(context_->CreateRequest(GURL(), + DEFAULT_PRIORITY, + nullptr, + TRAFFIC_ANNOTATION_FOR_TESTS)) {} void SetUp() override { request_->SetLoadFlags(0); } // context_ must be declared before request_. - TestURLRequestContext context_; + std::unique_ptr<URLRequestContext> context_; std::unique_ptr<URLRequest> request_; };
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 0b38995..9f7ec13e 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc
@@ -142,6 +142,7 @@ #include "net/url_request/referrer_policy.h" #include "net/url_request/static_http_user_agent_settings.h" #include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_filter.h" #include "net/url_request/url_request_http_job.h" @@ -13100,26 +13101,25 @@ : public URLRequestTest, public testing::WithParamInterface<bool> { public: - URLRequestMaybeAsyncFirstPartySetsTest() - : cookie_monster_(/*store=*/nullptr, - /*net_log=*/nullptr, - /*first_party_sets_enabled=*/true) { + URLRequestMaybeAsyncFirstPartySetsTest() { CHECK(test_server_.Start()); } + + std::unique_ptr<CookieStore> CreateCookieStore() { + auto cookie_monster = + std::make_unique<CookieMonster>(/*store=*/nullptr, + /*net_log=*/nullptr, + /*first_party_sets_enabled=*/true); auto cookie_access_delegate = std::make_unique<TestCookieAccessDelegate>(); cookie_access_delegate->set_invoke_callbacks_asynchronously( invoke_callbacks_asynchronously()); - cookie_monster_.SetCookieAccessDelegate(std::move(cookie_access_delegate)); - - CHECK(test_server_.Start()); + cookie_monster->SetCookieAccessDelegate(std::move(cookie_access_delegate)); + return cookie_monster; } bool invoke_callbacks_asynchronously() { return GetParam(); } - CookieStore* cookie_store() { return &cookie_monster_; } - HttpTestServer& test_server() { return test_server_; } private: - CookieMonster cookie_monster_; HttpTestServer test_server_; }; @@ -13129,12 +13129,12 @@ url::Origin::Create(test_server().GetURL(kHost, "/")); const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); - TestURLRequestContext context(/*delay_initialization=*/true); - context.set_cookie_store(cookie_store()); - context.Init(); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetCookieStore(CreateCookieStore()); + auto context = context_builder->Build(); TestDelegate d; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( test_server().GetURL(kHost, "/echo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info( @@ -13154,12 +13154,12 @@ url::Origin::Create(test_server().GetURL(kHost, "/")); const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); - TestURLRequestContext context(/*delay_initialization=*/true); - context.set_cookie_store(cookie_store()); - context.Init(); + auto context_builder = CreateTestURLRequestContextBuilder(); + context_builder->SetCookieStore(CreateCookieStore()); + auto context = context_builder->Build(); TestDelegate d; - std::unique_ptr<URLRequest> req(context.CreateRequest( + std::unique_ptr<URLRequest> req(context->CreateRequest( test_server().GetURL(kHost, base::StrCat({ "/server-redirect?",
diff --git a/pdf/pdf_view_plugin_base.cc b/pdf/pdf_view_plugin_base.cc index b4f03096..40ef7bd5 100644 --- a/pdf/pdf_view_plugin_base.cc +++ b/pdf/pdf_view_plugin_base.cc
@@ -927,10 +927,6 @@ engine()->PageOffsetUpdated(available_area_.OffsetFromOrigin()); engine()->PluginSizeUpdated(available_area_.size()); - - if (document_size_.IsEmpty()) - return; - paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size())); } void PdfViewPluginBase::CalculateBackgroundParts() { @@ -1088,7 +1084,10 @@ void PdfViewPluginBase::SetZoom(double scale) { double old_zoom = zoom_; zoom_ = scale; + OnGeometryChanged(old_zoom, device_scale_); + if (!document_size_.IsEmpty()) + paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size())); } // static @@ -1310,7 +1309,10 @@ // TODO(crbug.com/1013800): Eliminate need to get document size from here. document_size_ = engine()->ApplyDocumentLayout(layout_options); + OnGeometryChanged(zoom_, device_scale_); + if (!document_size_.IsEmpty()) + paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size())); // Send 100% loading progress only after initial layout negotiated. if (last_progress_sent_ < 100 && @@ -1759,7 +1761,10 @@ print_preview_loaded_page_count_ = 1; AppendBlankPrintPreviewPages(); } + OnGeometryChanged(0, 0); + if (!document_size_.IsEmpty()) + paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size())); } void PdfViewPluginBase::AppendBlankPrintPreviewPages() {
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc index d916c59c..4eb78bd 100644 --- a/sandbox/policy/win/sandbox_win.cc +++ b/sandbox/policy/win/sandbox_win.cc
@@ -781,15 +781,16 @@ if (base::win::GetVersion() >= base::win::Version::WIN8 || (::IsProcessInJob(::GetCurrentProcess(), nullptr, &in_job) && !in_job)) { - static HANDLE job_object_handle = nullptr; - if (!job_object_handle) { - Job job_obj; - DWORD result = job_obj.Init(JOB_UNPROTECTED, nullptr, 0, 0); - if (result != ERROR_SUCCESS) + static Job* job_object = nullptr; + if (!job_object) { + job_object = new Job; + DWORD result = job_object->Init(JOB_UNPROTECTED, nullptr, 0, 0); + if (result != ERROR_SUCCESS) { + job_object = nullptr; return SBOX_ERROR_CANNOT_INIT_JOB; - job_object_handle = job_obj.Take().Take(); + } } - options.job_handle = job_object_handle; + options.job_handle = job_object->GetHandle(); } }
diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc index 6bce0cc3..0f5c9757d 100644 --- a/sandbox/win/src/broker_services.cc +++ b/sandbox/win/src/broker_services.cc
@@ -80,18 +80,15 @@ // Helper structure that allows the Broker to associate a job notification // with a job object and with a policy. struct JobTracker { - JobTracker(base::win::ScopedHandle job, - scoped_refptr<sandbox::PolicyBase> policy, - DWORD process_id) - : job(std::move(job)), policy(policy), process_id(process_id) {} + JobTracker(scoped_refptr<sandbox::PolicyBase> policy, DWORD process_id) + : policy(policy), process_id(process_id) {} ~JobTracker() { // As if TerminateProcess() was called for all associated processes. // Handles are still valid. - ::TerminateJobObject(job.Get(), sandbox::SBOX_ALL_OK); - policy->OnJobEmpty(job.Get()); + ::TerminateJobObject(policy->GetJobHandle(), sandbox::SBOX_ALL_OK); + policy->OnJobEmpty(); } - base::win::ScopedHandle job; scoped_refptr<sandbox::PolicyBase> policy; DWORD process_id; }; @@ -204,13 +201,11 @@ // with it has terminated. It is safe to free the tracker // and release its reference to the associated policy object // which will Close the job handle. - HANDLE job_handle = tracker->job.Get(); - // Erase by comparing with the job handle. - jobs.erase(std::remove_if(jobs.begin(), jobs.end(), - [&](auto&& p) -> bool { - return p->job.Get() == job_handle; - }), + // Erase directly. + jobs.erase(std::remove_if( + jobs.begin(), jobs.end(), + [&](auto&& p) -> bool { return p.get() == tracker; }), jobs.end()); break; } @@ -255,7 +250,7 @@ } case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: { - bool res = ::TerminateJobObject(tracker->job.Get(), + bool res = ::TerminateJobObject(tracker->policy->GetJobHandle(), sandbox::SBOX_FATAL_MEMORY_EXCEEDED); DCHECK(res); break; @@ -269,7 +264,7 @@ } else if (THREAD_CTRL_NEW_JOB_TRACKER == key) { std::unique_ptr<JobTracker> tracker; tracker.reset(reinterpret_cast<JobTracker*>(ovl)); - DCHECK(tracker->job.IsValid()); + DCHECK(tracker->policy->HasJob()); child_process_ids.insert(tracker->process_id); jobs.push_back(std::move(tracker)); @@ -494,8 +489,7 @@ return SBOX_ERROR_BAD_PARAMS; } - base::win::ScopedHandle job; - result = policy_base->MakeJobObject(&job); + result = policy_base->InitJob(); if (SBOX_ALL_OK != result) return result; @@ -525,8 +519,9 @@ startup_info->SetAppContainer(container); // On Win10, jobs are associated via startup_info. - if (base::win::GetVersion() >= base::win::Version::WIN10 && job.IsValid()) { - startup_info->AddJobToAssociate(job.Get()); + if (base::win::GetVersion() >= base::win::Version::WIN10 && + policy_base->HasJob()) { + startup_info->AddJobToAssociate(policy_base->GetJobHandle()); } if (!startup_info->BuildStartupInformation()) @@ -543,8 +538,8 @@ } } std::unique_ptr<TargetProcess> target = std::make_unique<TargetProcess>( - std::move(initial_token), std::move(lockdown_token), job.Get(), - thread_pool_, imp_caps); + std::move(initial_token), std::move(lockdown_token), + policy_base->GetJobHandle(), thread_pool_, imp_caps); result = target->Create(exe_path, command_line, std::move(startup_info), &process_info, last_error); @@ -554,11 +549,11 @@ return result; } - if (job.IsValid() && policy_base->GetJobLevel() <= JOB_LIMITED_USER) { + if (policy_base->HasJob() && policy_base->GetJobLevel() <= JOB_LIMITED_USER) { // Restrict the job from containing any processes. Job restrictions // are only applied at process creation, so the target process is // unaffected. - result = policy_base->DropActiveProcessLimit(&job); + result = policy_base->DropActiveProcessLimit(); if (result != SBOX_ALL_OK) { target->Terminate(); return result; @@ -583,9 +578,9 @@ return result; } - if (job.IsValid()) { + if (policy_base->HasJob()) { JobTracker* tracker = - new JobTracker(std::move(job), policy_base, process_info.process_id()); + new JobTracker(policy_base, process_info.process_id()); // Post the tracker to the tracking thread, then associate the job with // the tracker. The worker thread takes ownership of these objects. @@ -593,8 +588,8 @@ job_port_.Get(), 0, THREAD_CTRL_NEW_JOB_TRACKER, reinterpret_cast<LPOVERLAPPED>(tracker))); // There is no obvious cleanup here. - CHECK( - AssociateCompletionPort(tracker->job.Get(), job_port_.Get(), tracker)); + CHECK(AssociateCompletionPort(policy_base->GetJobHandle(), job_port_.Get(), + tracker)); } else { // Duplicate the process handle to give the tracking machinery // something valid to wait on in the tracking thread.
diff --git a/sandbox/win/src/job.cc b/sandbox/win/src/job.cc index d12a565..f9097b67 100644 --- a/sandbox/win/src/job.cc +++ b/sandbox/win/src/job.cc
@@ -39,9 +39,6 @@ case JOB_LOCKDOWN: { jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; - [[fallthrough]]; - } - case JOB_RESTRICTED: { jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_WRITECLIPBOARD; jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_READCLIPBOARD; jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES; @@ -90,6 +87,14 @@ return ERROR_SUCCESS; } +bool Job::IsValid() { + return job_handle_.IsValid(); +} + +HANDLE Job::GetHandle() { + return job_handle_.Get(); +} + DWORD Job::UserHandleGrantAccess(HANDLE handle) { if (!job_handle_.IsValid()) return ERROR_NO_DATA; @@ -102,10 +107,6 @@ return ERROR_SUCCESS; } -base::win::ScopedHandle Job::Take() { - return std::move(job_handle_); -} - DWORD Job::AssignProcessToJob(HANDLE process_handle) { if (!job_handle_.IsValid()) return ERROR_NO_DATA; @@ -116,23 +117,25 @@ return ERROR_SUCCESS; } -// static -DWORD Job::SetActiveProcessLimit(base::win::ScopedHandle* job_handle, - DWORD processes) { +DWORD Job::SetActiveProcessLimit(DWORD processes) { JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {}; - if (!::QueryInformationJobObject(job_handle->Get(), - JobObjectExtendedLimitInformation, &jeli, - sizeof(jeli), nullptr)) - return ::GetLastError(); + if (!job_handle_.IsValid()) + return ERROR_NO_DATA; + if (!::QueryInformationJobObject(job_handle_.Get(), + JobObjectExtendedLimitInformation, &jeli, + sizeof(jeli), nullptr)) { + return ::GetLastError(); + } jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS; jeli.BasicLimitInformation.ActiveProcessLimit = processes; - if (!::SetInformationJobObject(job_handle->Get(), + if (!::SetInformationJobObject(job_handle_.Get(), JobObjectExtendedLimitInformation, &jeli, - sizeof(jeli))) + sizeof(jeli))) { return ::GetLastError(); + } return ERROR_SUCCESS; }
diff --git a/sandbox/win/src/job.h b/sandbox/win/src/job.h index 6922338c..1bef847c 100644 --- a/sandbox/win/src/job.h +++ b/sandbox/win/src/job.h
@@ -52,13 +52,14 @@ // the error. DWORD UserHandleGrantAccess(HANDLE handle); - // Revokes ownership to the job handle and returns it. - // If the object is not yet initialized, it returns an invalid handle. - base::win::ScopedHandle Take(); + // True if the job has been initialized and has a valid handle. + bool IsValid(); - // Updates the active process limit for |job_handle|. - static DWORD SetActiveProcessLimit(base::win::ScopedHandle* job_handle, - DWORD processes); + // If the object is not yet initialized, returns nullptr. + HANDLE GetHandle(); + + // Updates the active process limit. + DWORD SetActiveProcessLimit(DWORD processes); private: // Handle to the job referenced by the object.
diff --git a/sandbox/win/src/job_unittest.cc b/sandbox/win/src/job_unittest.cc index 536900dc..2af3f27c 100644 --- a/sandbox/win/src/job_unittest.cc +++ b/sandbox/win/src/job_unittest.cc
@@ -35,41 +35,9 @@ ASSERT_EQ(static_cast<DWORD>(ERROR_FILE_NOT_FOUND), ::GetLastError()); } -// Tests the method "Take". -TEST(JobTest, Take) { - base::win::ScopedHandle job_handle; - // Scope the creation of Job. - { - // Create the job. - Job job; - ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), - job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); - - job_handle = job.Take(); - ASSERT_TRUE(job_handle.IsValid()); - } - - // Check to be sure that the job is still alive even after the object is gone - // out of scope. - HANDLE job_handle_dup = - ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name"); - ASSERT_TRUE(job_handle_dup); - - // Remove all references. - if (job_handle_dup) - ::CloseHandle(job_handle_dup); - - job_handle.Close(); - - // Check if the jbo is really dead. - job_handle_dup = ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name"); - ASSERT_TRUE(!job_handle_dup); - ASSERT_EQ(static_cast<DWORD>(ERROR_FILE_NOT_FOUND), ::GetLastError()); -} - // Tests the ui exceptions TEST(JobTest, TestExceptions) { - base::win::ScopedHandle job_handle; + HANDLE job_handle; // Scope the creation of Job. { // Create the job. @@ -78,16 +46,15 @@ job.Init(JOB_LOCKDOWN, L"my_test_job_name", JOB_OBJECT_UILIMIT_READCLIPBOARD, 0)); - job_handle = job.Take(); - ASSERT_TRUE(job_handle.IsValid()); + job_handle = job.GetHandle(); + ASSERT_TRUE(job_handle != INVALID_HANDLE_VALUE); JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; DWORD size = sizeof(jbur); ASSERT_TRUE(::QueryInformationJobObject( - job_handle.Get(), JobObjectBasicUIRestrictions, &jbur, size, &size)); + job_handle, JobObjectBasicUIRestrictions, &jbur, size, &size)); ASSERT_EQ(0u, jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD); - job_handle.Close(); } // Scope the creation of Job. @@ -97,13 +64,13 @@ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); - job_handle = job.Take(); - ASSERT_TRUE(job_handle.IsValid()); + job_handle = job.GetHandle(); + ASSERT_TRUE(job_handle != INVALID_HANDLE_VALUE); JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; DWORD size = sizeof(jbur); ASSERT_TRUE(::QueryInformationJobObject( - job_handle.Get(), JobObjectBasicUIRestrictions, &jbur, size, &size)); + job_handle, JobObjectBasicUIRestrictions, &jbur, size, &size)); ASSERT_EQ(static_cast<DWORD>(JOB_OBJECT_UILIMIT_READCLIPBOARD), jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD); @@ -127,39 +94,36 @@ ASSERT_EQ(static_cast<DWORD>(ERROR_NO_DATA), job.UserHandleGrantAccess(nullptr)); ASSERT_EQ(static_cast<DWORD>(ERROR_NO_DATA), job.AssignProcessToJob(nullptr)); - ASSERT_FALSE(job.Take().IsValid()); + ASSERT_FALSE(job.GetHandle() == INVALID_HANDLE_VALUE); } -// Tests the initialization of the job with different security level. +// Tests the initialization of the job with different security levels. TEST(JobTest, SecurityLevel) { - Job job1; + Job job_lockdown; ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), - job1.Init(JOB_LOCKDOWN, L"job1", 0, 0)); + job_lockdown.Init(JOB_LOCKDOWN, L"job_lockdown", 0, 0)); - Job job2; + Job job_limited_user; ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), - job2.Init(JOB_RESTRICTED, L"job2", 0, 0)); + job_limited_user.Init(JOB_LIMITED_USER, L"job_limited_user", 0, 0)); - Job job3; + Job job_interactive; ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), - job3.Init(JOB_LIMITED_USER, L"job3", 0, 0)); + job_interactive.Init(JOB_INTERACTIVE, L"job_interactive", 0, 0)); - Job job4; + Job job_unprotected; ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), - job4.Init(JOB_INTERACTIVE, L"job4", 0, 0)); - - Job job5; - ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), - job5.Init(JOB_UNPROTECTED, L"job5", 0, 0)); + job_unprotected.Init(JOB_UNPROTECTED, L"job_unprotected", 0, 0)); // JOB_NONE means we run without a job object so Init should fail. - Job job6; + Job job_none; ASSERT_EQ(static_cast<DWORD>(ERROR_BAD_ARGUMENTS), - job6.Init(JOB_NONE, L"job6", 0, 0)); + job_none.Init(JOB_NONE, L"job_none", 0, 0)); - Job job7; + Job job_bad_args; ASSERT_EQ(static_cast<DWORD>(ERROR_BAD_ARGUMENTS), - job7.Init(static_cast<JobLevel>(JOB_NONE + 1), L"job7", 0, 0)); + job_bad_args.Init(static_cast<JobLevel>(JOB_NONE + 1), + L"job_bad_args", 0, 0)); } // Tests the method "AssignProcessToJob". @@ -179,13 +143,13 @@ job.AssignProcessToJob(pi.process_handle())); // Get the job handle. - base::win::ScopedHandle job_handle = job.Take(); + HANDLE job_handle = job.GetHandle(); // Check if the process is in the job. JOBOBJECT_BASIC_PROCESS_ID_LIST jbpidl = {0}; DWORD size = sizeof(jbpidl); EXPECT_TRUE(::QueryInformationJobObject( - job_handle.Get(), JobObjectBasicProcessIdList, &jbpidl, size, &size)); + job_handle, JobObjectBasicProcessIdList, &jbpidl, size, &size)); EXPECT_EQ(1u, jbpidl.NumberOfAssignedProcesses); EXPECT_EQ(1u, jbpidl.NumberOfProcessIdsInList);
diff --git a/sandbox/win/src/sandbox_policy.h b/sandbox/win/src/sandbox_policy.h index 2d7b176..dd3c1b1 100644 --- a/sandbox/win/src/sandbox_policy.h +++ b/sandbox/win/src/sandbox_policy.h
@@ -118,7 +118,7 @@ // length in: // http://msdn2.microsoft.com/en-us/library/ms684152.aspx // - // Note: the recommended level is JOB_RESTRICTED or JOB_LOCKDOWN. + // Note: the recommended level is JOB_LOCKDOWN. virtual ResultCode SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) = 0;
diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc index 9b770d9..77c0d1a 100644 --- a/sandbox/win/src/sandbox_policy_base.cc +++ b/sandbox/win/src/sandbox_policy_base.cc
@@ -110,7 +110,8 @@ lockdown_default_dacl_(false), add_restricting_random_sid_(false), effective_token_(nullptr), - allow_no_sandbox_job_(false) { + allow_no_sandbox_job_(false), + job_() { dispatcher_ = std::make_unique<TopLevelDispatcher>(this); } @@ -149,6 +150,9 @@ } ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) { + // Cannot set this after the job has been initialized. + if (job_.IsValid()) + return SBOX_ERROR_BAD_PARAMS; if (memory_limit_ && job_level == JOB_NONE) { return SBOX_ERROR_BAD_PARAMS; } @@ -390,28 +394,37 @@ return handles_to_share_; } -ResultCode PolicyBase::MakeJobObject(base::win::ScopedHandle* job) { - if (job_level_ == JOB_NONE) { - job->Close(); - return SBOX_ALL_OK; - } +ResultCode PolicyBase::InitJob() { + if (job_.IsValid()) + return SBOX_ERROR_BAD_PARAMS; - // Create the windows job object. - Job job_obj; - DWORD result = - job_obj.Init(job_level_, nullptr, ui_exceptions_, memory_limit_); + if (job_level_ == JOB_NONE) + return SBOX_ALL_OK; + + // Create the Windows job object. + DWORD result = job_.Init(job_level_, nullptr, ui_exceptions_, memory_limit_); if (ERROR_SUCCESS != result) return SBOX_ERROR_CANNOT_INIT_JOB; - *job = job_obj.Take(); return SBOX_ALL_OK; } -ResultCode PolicyBase::DropActiveProcessLimit(base::win::ScopedHandle* job) { +HANDLE PolicyBase::GetJobHandle() { + return job_.GetHandle(); +} + +bool PolicyBase::HasJob() { + return job_.IsValid(); +} + +ResultCode PolicyBase::DropActiveProcessLimit() { + if (!job_.IsValid()) + return SBOX_ERROR_BAD_PARAMS; + if (job_level_ >= JOB_INTERACTIVE) return SBOX_ALL_OK; - if (ERROR_SUCCESS != Job::SetActiveProcessLimit(job, 0)) + if (ERROR_SUCCESS != job_.SetActiveProcessLimit(0)) return SBOX_ERROR_CANNOT_UPDATE_JOB_PROCESS_LIMIT; return SBOX_ALL_OK; @@ -544,9 +557,9 @@ return SBOX_ALL_OK; } -bool PolicyBase::OnJobEmpty(HANDLE job) { - if (target_->Job() == job) - target_.reset(); +// Can only be called if a job was associated with this policy. +bool PolicyBase::OnJobEmpty() { + target_.reset(); return true; }
diff --git a/sandbox/win/src/sandbox_policy_base.h b/sandbox/win/src/sandbox_policy_base.h index a88a95b..137beb82 100644 --- a/sandbox/win/src/sandbox_policy_base.h +++ b/sandbox/win/src/sandbox_policy_base.h
@@ -23,6 +23,7 @@ #include "sandbox/win/src/app_container_base.h" #include "sandbox/win/src/handle_closer.h" #include "sandbox/win/src/ipc_tags.h" +#include "sandbox/win/src/job.h" #include "sandbox/win/src/policy_engine_opcodes.h" #include "sandbox/win/src/policy_engine_params.h" #include "sandbox/win/src/sandbox_policy.h" @@ -87,11 +88,18 @@ // Creates a Job object with the level specified in a previous call to // SetJobLevel(). - ResultCode MakeJobObject(base::win::ScopedHandle* job); + ResultCode InitJob(); - // Updates the active process limit on the job to zero. Has no effect - // if the job is allowed to spawn processes. - ResultCode DropActiveProcessLimit(base::win::ScopedHandle* job); + // Returns the handle for this policy's job, or INVALID_HANDLE_VALUE if the + // job is not initialized. + HANDLE GetJobHandle(); + + // Returns true if a job is associated with this policy. + bool HasJob(); + + // Updates the active process limit on the policy's job to zero. + // Has no effect if the job is allowed to spawn processes. + ResultCode DropActiveProcessLimit(); // Creates the two tokens with the levels specified in a previous call to // SetTokenLevel(). Also creates a lowbox token if specified based on the @@ -104,10 +112,9 @@ // call to TargetProcess::Init() is issued. ResultCode ApplyToTarget(std::unique_ptr<TargetProcess> target); - // Called when there are no more active processes in a Job. - // Removes a Job object associated with this policy and the target associated - // with the job. If a process is not in a job, call OnProcessFinished(). - bool OnJobEmpty(HANDLE job); + // Called when there are no more active processes in the policy's Job. + // If a process is not in a job, call OnProcessFinished(). + bool OnJobEmpty(); // Called when a process no longer needs to be tracked. Processes in jobs // should be notified via OnJobEmpty instead. @@ -186,6 +193,7 @@ HANDLE effective_token_; bool allow_no_sandbox_job_; + Job job_; }; } // namespace sandbox
diff --git a/sandbox/win/src/sandbox_policy_diagnostic.cc b/sandbox/win/src/sandbox_policy_diagnostic.cc index 19ed93f..8356419f 100644 --- a/sandbox/win/src/sandbox_policy_diagnostic.cc +++ b/sandbox/win/src/sandbox_policy_diagnostic.cc
@@ -77,8 +77,6 @@ switch (job) { case JOB_LOCKDOWN: return "Lockdown"; - case JOB_RESTRICTED: - return "Restricted"; case JOB_LIMITED_USER: return "Limited User"; case JOB_INTERACTIVE:
diff --git a/sandbox/win/src/security_level.h b/sandbox/win/src/security_level.h index 2965a8a..4110bc67 100644 --- a/sandbox/win/src/security_level.h +++ b/sandbox/win/src/security_level.h
@@ -114,27 +114,21 @@ // | *Forbid changes to the display | limit. | // | settings. | *Kill on Job close.| // -----------------|---------------------------------- |--------------------| -// JOB_RESTRICTED | Same as LIMITED_USER plus: | *One active process| +// JOB_LOCKDOWN | Same as LIMITED_USER plus: | *One active process| // | * No read/write to the clipboard. | limit. | // | * No access to User Handles that | *Kill on Job close.| -// | belong to other processes. | | -// | * Forbid message broadcasts. | | +// | belong to other processes. | *Kill on unhandled | +// | * Forbid message broadcasts. | exception. | // | * Forbid setting global hooks. | | // | * No access to the global atoms | | // | table. | | // -----------------|-----------------------------------|--------------------| -// JOB_LOCKDOWN | Same as RESTRICTED | *One active process| -// | | limit. | -// | | *Kill on Job close.| -// | | *Kill on unhandled | -// | | exception. | -// | | | +// // In the context of the above table, 'user handles' refers to the handles of // windows, bitmaps, menus, etc. Files, treads and registry handles are kernel // handles and are not affected by the job level settings. enum JobLevel { JOB_LOCKDOWN = 0, - JOB_RESTRICTED, JOB_LIMITED_USER, JOB_INTERACTIVE, JOB_UNPROTECTED,
diff --git a/services/network/restricted_cookie_manager.cc b/services/network/restricted_cookie_manager.cc index 6ada15b5..67f45a2 100644 --- a/services/network/restricted_cookie_manager.cc +++ b/services/network/restricted_cookie_manager.cc
@@ -21,6 +21,7 @@ #include "base/threading/sequenced_task_runner_handle.h" #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/remote.h" +#include "net/base/features.h" #include "net/base/isolation_info.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/cookies/canonical_cookie.h" @@ -399,6 +400,22 @@ return cookie_partition_key_.has_value(); } +namespace { + +bool PartitionedCookiesAllowed( + bool partitioned_cookies_runtime_feature_enabled, + const absl::optional<net::CookiePartitionKey>& cookie_partition_key) { + if (base::FeatureList::IsEnabled( + net::features::kPartitionedCookiesBypassOriginTrial) || + partitioned_cookies_runtime_feature_enabled) + return true; + // We allow partition keys which have a nonce since the Origin Trial is + // only meant to test cookies set with the Partitioned attribute. + return cookie_partition_key && cookie_partition_key->nonce(); +} + +} // namespace + void RestrictedCookieManager::GetAllForUrl( const GURL& url, const net::SiteForCookies& site_for_cookies, @@ -424,10 +441,8 @@ cookie_store_->GetCookieListWithOptionsAsync( url, net_options, - // We allow partition keys which have a nonce since the Origin Trial is - // only meant to test cookies set with the Partitioned attribute. - partitioned_cookies_runtime_feature_enabled || - (cookie_partition_key_ && cookie_partition_key_->nonce()) + PartitionedCookiesAllowed(partitioned_cookies_runtime_feature_enabled, + cookie_partition_key_) ? cookie_partition_key_collection_ : net::CookiePartitionKeyCollection(), base::BindOnce(&RestrictedCookieManager::CookieListToGetAllForUrlCallback, @@ -766,8 +781,8 @@ std::unique_ptr<net::CanonicalCookie> parsed_cookie = net::CanonicalCookie::Create( url, cookie, base::Time::Now(), absl::nullopt /* server_time */, - partitioned_cookies_runtime_feature_enabled || - (cookie_partition_key_ && cookie_partition_key_->nonce()) + PartitionedCookiesAllowed(partitioned_cookies_runtime_feature_enabled, + cookie_partition_key_) ? cookie_partition_key_ : absl::nullopt, &status);
diff --git a/services/network/restricted_cookie_manager_unittest.cc b/services/network/restricted_cookie_manager_unittest.cc index 50bfe674..9c94d92 100644 --- a/services/network/restricted_cookie_manager_unittest.cc +++ b/services/network/restricted_cookie_manager_unittest.cc
@@ -2260,6 +2260,43 @@ EXPECT_TRUE(cookies[0].IsPartitioned()); } +TEST_P(PartitionedCookiesRestrictedCookieManagerTest, + RuntimeEnabledFeature_BypassOriginTrial) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + {net::features::kPartitionedCookies, + net::features::kPartitionedCookiesBypassOriginTrial}, + {}); + + const GURL kCookieURL("https://example.com"); + const GURL kTopFrameURL("https://sub.foo.com"); + const net::SiteForCookies kSiteForCookies = + net::SiteForCookies::FromUrl(kTopFrameURL); + const url::Origin kTopFrameOrigin = url::Origin::Create(kTopFrameURL); + const net::IsolationInfo kIsolationInfo = + net::IsolationInfo::CreateForInternalRequest(kTopFrameOrigin); + + service_->OverrideIsolationInfoForTesting(kIsolationInfo); + + // Setting a partitioned cookie when the RuntimeEnabledFeature is disabled but + // the "bypass origin trial" feature is enabled should result in a partitioned + // cookie. + sync_service_->SetCookieFromString( + kCookieURL, kSiteForCookies, kTopFrameOrigin, + "__Host-foo=bar; Secure; SameSite=None; Path=/; Partitioned", + /*partitioned_cookies_runtime_feature_enabled=*/false); + + auto options = mojom::CookieManagerGetOptions::New(); + options->name = ""; + options->match_type = mojom::CookieMatchType::STARTS_WITH; + + auto cookies = sync_service_->GetAllForUrl( + kCookieURL, kSiteForCookies, kTopFrameOrigin, std::move(options), + /*partitioned_cookies_runtime_feature_enabled=*/true); + ASSERT_EQ(1u, cookies.size()); + EXPECT_TRUE(cookies[0].IsPartitioned()); +} + INSTANTIATE_TEST_SUITE_P( PartitionedCookies, PartitionedCookiesRestrictedCookieManagerTest,
diff --git a/services/network/websocket_factory.cc b/services/network/websocket_factory.cc index 3837c8e..98a1fb6 100644 --- a/services/network/websocket_factory.cc +++ b/services/network/websocket_factory.cc
@@ -24,7 +24,7 @@ WebSocketFactory::~WebSocketFactory() { // Subtle: This is important to avoid WebSocketFactory::Remove calls during // |connections_| destruction. - connections_.clear(); + WebSocketSet connections = std::move(connections_); } void WebSocketFactory::CreateWebSocket(
diff --git a/services/network/websocket_factory.h b/services/network/websocket_factory.h index a088489c..6a4e4a4 100644 --- a/services/network/websocket_factory.h +++ b/services/network/websocket_factory.h
@@ -77,8 +77,10 @@ void Remove(WebSocket* impl); private: + using WebSocketSet = + std::set<std::unique_ptr<WebSocket>, base::UniquePtrComparator>; // The connections held by this factory. - std::set<std::unique_ptr<WebSocket>, base::UniquePtrComparator> connections_; + WebSocketSet connections_; WebSocketThrottler throttler_;
diff --git a/storage/browser/quota/quota_internals.mojom b/storage/browser/quota/quota_internals.mojom index 3e882091..6b7a9ce5 100644 --- a/storage/browser/quota/quota_internals.mojom +++ b/storage/browser/quota/quota_internals.mojom
@@ -19,8 +19,16 @@ mojo_base.mojom.Time last_modified; }; +// Represents the Storage Type for a given host. +// This is a subset of blink::mojom::StorageType. +enum StorageType { + kTemporary = 0, + kPersistent = 1, + kSyncable = 2, +}; + // Interface for controlling Quota Internals. -// Hosted on chrome://quota-internals" for WebUI content::QuotaInternalsUI. +// Hosted on "chrome://quota-internals" for WebUI content::QuotaInternalsUI. interface QuotaInternalsHandler { // Returns the total and available disk space in bits for a user, // which is then converted to bytes and displayed on the Quota Internals UI. @@ -37,4 +45,8 @@ // Returns an array of Storage Bucket entries stored in the QuotaDatabase. RetrieveBucketsTable() => (array<BucketTableEntry> entries); + + // Returns a host's usage for a given storage type. + GetHostUsageForInternals(string host, StorageType storage_type) => + (int64 host_usage); }; \ No newline at end of file
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc index 7b09d41e..3ca7d8d 100644 --- a/storage/browser/quota/quota_manager_impl.cc +++ b/storage/browser/quota/quota_manager_impl.cc
@@ -117,6 +117,17 @@ } } +StorageType GetBlinkStorageType(storage::mojom::StorageType type) { + switch (type) { + case storage::mojom::StorageType::kTemporary: + return StorageType::kTemporary; + case storage::mojom::StorageType::kPersistent: + return StorageType::kPersistent; + case storage::mojom::StorageType::kSyncable: + return StorageType::kSyncable; + } +} + QuotaErrorOr<BucketInfo> GetOrCreateBucketOnDBThread( const StorageKey& storage_key, const std::string& bucket_name, @@ -1688,6 +1699,22 @@ usage_tracker->GetHostUsageWithBreakdown(host, std::move(callback)); } +void QuotaManagerImpl::GetHostUsageForInternals( + const std::string& host, + storage::mojom::StorageType storage_type, + GetHostUsageForInternalsCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + EnsureDatabaseOpened(); + + StorageType type = GetBlinkStorageType(storage_type); + UsageTracker* usage_tracker = GetUsageTracker(type); + DCHECK(usage_tracker); + + usage_tracker->GetHostUsageWithBreakdown( + host, base::BindOnce(&QuotaManagerImpl::OnGetHostUsageForInternals, + weak_factory_.GetWeakPtr(), std::move(callback))); +} + bool QuotaManagerImpl::IsStorageUnlimited(const StorageKey& storage_key, StorageType type) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -1897,6 +1924,16 @@ return nullptr; } +void QuotaManagerImpl::OnGetHostUsageForInternals( + GetHostUsageForInternalsCallback callback, + int64_t usage, + blink::mojom::UsageBreakdownPtr usage_breakdown) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_GE(usage, -1); + + std::move(callback).Run(usage); +} + void QuotaManagerImpl::NotifyStorageAccessed(const StorageKey& storage_key, StorageType type, base::Time access_time) {
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h index 5e8fff9..6d33518 100644 --- a/storage/browser/quota/quota_manager_impl.h +++ b/storage/browser/quota/quota_manager_impl.h
@@ -355,6 +355,10 @@ void GetDiskAvailability(GetDiskAvailabilityCallback callback) override; void GetStatistics(GetStatisticsCallback callback) override; void RetrieveBucketsTable(RetrieveBucketsTableCallback callback) override; + void GetHostUsageForInternals( + const std::string& host, + storage::mojom::StorageType storage_type, + GetHostUsageForInternalsCallback callback) override; // Called by UI and internal modules. void GetPersistentHostQuota(const std::string& host, QuotaCallback callback); @@ -544,6 +548,10 @@ void DumpBucketTable(DumpBucketTableCallback callback); void DidRetrieveBucketsTable(RetrieveBucketsTableCallback callback, const BucketTableEntries& entries); + void OnGetHostUsageForInternals( + GetHostUsageForInternalsCallback callback, + int64_t usage, + blink::mojom::UsageBreakdownPtr usage_breakdown); // Runs BucketDataDeleter which calls QuotaClients to clear data for the // bucket. Once the task is complete, calls the QuotaDatabase to delete the
diff --git a/storage/browser/quota/quota_manager_unittest.cc b/storage/browser/quota/quota_manager_unittest.cc index 33bad3a..242461ac 100644 --- a/storage/browser/quota/quota_manager_unittest.cc +++ b/storage/browser/quota/quota_manager_unittest.cc
@@ -2579,6 +2579,32 @@ EXPECT_EQ(0, usage()); } +TEST_F(QuotaManagerImplTest, GetHostUsageForInternals) { + static const ClientBucketData kData[] = { + {"http://example.com/", kDefaultBucketName, kTemp, 400}, + {"http://example.com/", kDefaultBucketName, kPerm, 2}, + }; + MockQuotaClient* client = + CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm}); + RegisterClientBucketData(client, kData); + + base::test::TestFuture<int64_t> temp_future; + quota_manager_impl()->GetHostUsageForInternals( + "example.com", storage::mojom::StorageType::kTemporary, + temp_future.GetCallback()); + int64_t temp_result = temp_future.Take(); + + EXPECT_EQ(400, temp_result); + + base::test::TestFuture<int64_t> perm_future; + quota_manager_impl()->GetHostUsageForInternals( + "example.com", storage::mojom::StorageType::kPersistent, + perm_future.GetCallback()); + int64_t perm_result = perm_future.Take(); + + EXPECT_EQ(2, perm_result); +} + TEST_F(QuotaManagerImplTest, NotifyAndLRUBucket) { static const ClientBucketData kData[] = { {"http://a.com/", kDefaultBucketName, kTemp, 0},
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json index a946e60..b0d9b1f 100644 --- a/testing/buildbot/chrome.json +++ b/testing/buildbot/chrome.json
@@ -30,6 +30,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 6 }, @@ -93,6 +94,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "named_caches": [ { "name": "cros_vm", @@ -340,6 +342,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "named_caches": [ { "name": "cros_vm", @@ -1323,6 +1326,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 6 }, @@ -1416,6 +1420,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 6 }, @@ -1616,6 +1621,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 6 },
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json index a47dfd6a..a06e04f 100644 --- a/testing/buildbot/chromium.android.fyi.json +++ b/testing/buildbot/chromium.android.fyi.json
@@ -1096,7 +1096,8 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android19.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android19.textpb", + "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_k.net_unittests.filter" ], "merge": { "args": [ @@ -2102,7 +2103,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2169,7 +2170,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2238,7 +2239,7 @@ "--test-launcher-batch-limit=1", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2305,7 +2306,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2372,7 +2373,7 @@ "args": [ "angle_unittests", "-v", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2425,7 +2426,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2492,7 +2493,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2559,7 +2560,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2626,7 +2627,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2693,7 +2694,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2762,7 +2763,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2829,7 +2830,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2896,7 +2897,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -2964,7 +2965,7 @@ "--gtest_filter=-*UsingRealWebcam*", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -3031,7 +3032,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -3098,7 +3099,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -3165,7 +3166,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb", + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb", "--git-revision=${got_revision}" ], "isolate_profile_data": true, @@ -3238,7 +3239,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -3306,7 +3307,7 @@ "--gtest_filter=-org.chromium.chrome.browser.contextualsearch.ContextualSearchManagerTest.test*ExternalNavigationWithUserGesture*", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb", + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb", "--git-revision=${got_revision}" ], "isolate_profile_data": true, @@ -3381,7 +3382,7 @@ "--gtest_filter=org.chromium.chrome.browser.contextualsearch.ContextualSearchManagerTest.test*ExternalNavigationWithUserGesture*", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb", + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb", "--git-revision=${got_revision}" ], "isolate_profile_data": true, @@ -3455,7 +3456,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb", + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb", "--git-revision=${got_revision}" ], "isolate_profile_data": true, @@ -3528,7 +3529,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -3595,7 +3596,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -3663,7 +3664,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -3731,7 +3732,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -3799,7 +3800,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -3867,7 +3868,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -3934,7 +3935,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4001,7 +4002,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4068,7 +4069,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4135,7 +4136,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4202,7 +4203,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4269,7 +4270,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4336,7 +4337,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4404,7 +4405,7 @@ "--use-cmd-decoder=validating", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4472,7 +4473,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4539,7 +4540,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4606,7 +4607,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4673,7 +4674,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4740,7 +4741,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4807,7 +4808,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4874,7 +4875,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -4941,7 +4942,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5008,7 +5009,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5075,7 +5076,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5142,7 +5143,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5209,7 +5210,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5276,7 +5277,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5343,7 +5344,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5410,7 +5411,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5478,7 +5479,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5545,7 +5546,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5612,7 +5613,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5679,7 +5680,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5746,7 +5747,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5813,7 +5814,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5880,7 +5881,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -5947,7 +5948,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6014,7 +6015,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6081,7 +6082,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6148,7 +6149,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6215,7 +6216,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6283,7 +6284,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6350,7 +6351,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6417,7 +6418,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6484,7 +6485,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6551,7 +6552,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6618,7 +6619,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6685,7 +6686,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6752,7 +6753,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6825,7 +6826,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6893,7 +6894,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -6960,7 +6961,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -7027,7 +7028,7 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "merge": { @@ -7095,7 +7096,7 @@ { "args": [ "--gtest-benchmark-name=components_perftests", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_name": "components_perftests", "isolate_profile_data": true, @@ -7149,7 +7150,7 @@ { "args": [ "--platform=android", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_name": "content_shell_crash_test", "isolate_profile_data": true, @@ -7200,7 +7201,7 @@ }, { "args": [ - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_name": "monochrome_public_apk_checker", "isolate_profile_data": true, @@ -7258,7 +7259,7 @@ "-v", "--passthrough", "--retry-limit=2", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_name": "telemetry_perf_unittests_android_chrome", "isolate_profile_data": true, @@ -7314,7 +7315,7 @@ "-v", "--passthrough", "--retry-limit=2", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_name": "telemetry_perf_unittests_android_monochrome", "isolate_profile_data": true, @@ -7366,7 +7367,7 @@ { "args": [ "--extra-browser-args=--enable-crashpad", - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_name": "telemetry_perf_unittests_android_chrome", "isolate_profile_data": true, @@ -7677,7 +7678,7 @@ "scripts": [ { "args": [ - "--avd-config=../../tools/android/avd/proto/generic_android25.textpb" + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb" ], "isolate_profile_data": true, "name": "check_network_annotations", @@ -7840,7 +7841,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M100", - "revision": "version:100.0.4896.46" + "revision": "version:100.0.4896.49" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -8260,7 +8261,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M99", - "revision": "version:99.0.4844.76" + "revision": "version:99.0.4844.80" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -8344,7 +8345,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M100", - "revision": "version:100.0.4896.46" + "revision": "version:100.0.4896.49" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -8764,7 +8765,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M99", - "revision": "version:99.0.4844.76" + "revision": "version:99.0.4844.80" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json index 6367699..7cc5d4b 100644 --- a/testing/buildbot/chromium.android.json +++ b/testing/buildbot/chromium.android.json
@@ -44881,7 +44881,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M100", - "revision": "version:100.0.4896.46" + "revision": "version:100.0.4896.49" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -45301,7 +45301,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M99", - "revision": "version:99.0.4844.76" + "revision": "version:99.0.4844.80" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -45385,7 +45385,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M100", - "revision": "version:100.0.4896.46" + "revision": "version:100.0.4896.49" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -45805,7 +45805,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M99", - "revision": "version:99.0.4844.76" + "revision": "version:99.0.4844.80" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -45893,7 +45893,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M100", - "revision": "version:100.0.4896.46" + "revision": "version:100.0.4896.49" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -46313,7 +46313,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M99", - "revision": "version:99.0.4844.76" + "revision": "version:99.0.4844.80" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -46397,7 +46397,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M100", - "revision": "version:100.0.4896.46" + "revision": "version:100.0.4896.49" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -46817,7 +46817,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M99", - "revision": "version:99.0.4844.76" + "revision": "version:99.0.4844.80" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -46972,7 +46972,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M100", - "revision": "version:100.0.4896.46" + "revision": "version:100.0.4896.49" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -47392,7 +47392,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M99", - "revision": "version:99.0.4844.76" + "revision": "version:99.0.4844.80" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -47476,7 +47476,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M100", - "revision": "version:100.0.4896.46" + "revision": "version:100.0.4896.49" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -47896,7 +47896,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M99", - "revision": "version:99.0.4844.76" + "revision": "version:99.0.4844.80" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -48051,7 +48051,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M100", - "revision": "version:100.0.4896.46" + "revision": "version:100.0.4896.49" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -48471,7 +48471,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M99", - "revision": "version:99.0.4844.76" + "revision": "version:99.0.4844.80" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -48555,7 +48555,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M100", - "revision": "version:100.0.4896.46" + "revision": "version:100.0.4896.49" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -48975,7 +48975,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M99", - "revision": "version:99.0.4844.76" + "revision": "version:99.0.4844.80" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 5c90a77..b43bd71 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -234,6 +234,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "named_caches": [ { "name": "cros_vm", @@ -1313,6 +1314,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 6 },
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 4a0b311..0b48a86 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -15747,6 +15747,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "named_caches": [ { "name": "cros_vm", @@ -16987,6 +16988,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "named_caches": [ { "name": "cros_vm",
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn index bc5ba530..239f560 100644 --- a/testing/buildbot/filters/BUILD.gn +++ b/testing/buildbot/filters/BUILD.gn
@@ -341,6 +341,7 @@ "//testing/buildbot/filters/android.emulator_10.net_unittests.filter", "//testing/buildbot/filters/android.emulator_11.net_unittests.filter", "//testing/buildbot/filters/android.emulator_l.net_unittests.filter", + "//testing/buildbot/filters/android.emulator_k.net_unittests.filter", "//testing/buildbot/filters/fuchsia.lsan.net_unittests.filter", "//testing/buildbot/filters/fuchsia.net_unittests.filter", ]
diff --git a/testing/buildbot/filters/android.emulator_k.net_unittests.filter b/testing/buildbot/filters/android.emulator_k.net_unittests.filter new file mode 100644 index 0000000..1a50ef31 --- /dev/null +++ b/testing/buildbot/filters/android.emulator_k.net_unittests.filter
@@ -0,0 +1,13 @@ +# crbug.com/1301957 +-TransportClientSocketPoolTest.Tag +-TransportClientSocketPoolTest.TagSSLDirect +-TransportClientSocketPoolTest.TagSSLDirectTwoSockets +-TransportClientSocketPoolTest.TagSSLDirectTwoSocketsFullPool +-TCPClientSocketTest.TagAfterConnect +-TCPClientSocketTest.Tag +-TCPSocketTest.Tag +-TCPSocketTest.TagAfterConnect +-TrafficStatsAndroidTest.* +-URLRequestTestHTTP.TestTagging +-SocketTagTest.Apply +-UDPSocketTest.Tag
diff --git a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/unit_tests.filter b/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/unit_tests.filter index 11e0dbe..816a9e5 100644 --- a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/unit_tests.filter +++ b/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/unit_tests.filter
@@ -917,7 +917,6 @@ -ChromeHintsManagerPushDisabledTest.PushManagerSetOnAndroid -ChromeHintsManagerPushEnabledTest.PushManagerSetOnAndroid -ChromeInfoMapTest.CheckPermissions --ChromeKeySystemsProviderTest.IsKeySystemsUpdateNeeded -ChromeLocalPresentationManagerFactoryTest.CreateForRegularProfile -ChromeLoggingTest.EnvironmentAndFlagLogFileName -ChromeLoggingTest.EnvironmentLogFileName
diff --git a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/unit_tests.filter b/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/unit_tests.filter index 11e0dbe..816a9e5 100644 --- a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/unit_tests.filter +++ b/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/unit_tests.filter
@@ -917,7 +917,6 @@ -ChromeHintsManagerPushDisabledTest.PushManagerSetOnAndroid -ChromeHintsManagerPushEnabledTest.PushManagerSetOnAndroid -ChromeInfoMapTest.CheckPermissions --ChromeKeySystemsProviderTest.IsKeySystemsUpdateNeeded -ChromeLocalPresentationManagerFactoryTest.CreateForRegularProfile -ChromeLoggingTest.EnvironmentAndFlagLogFileName -ChromeLoggingTest.EnvironmentLogFileName
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl index b5e23fd1..53f26db 100644 --- a/testing/buildbot/gn_isolate_map.pyl +++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1687,7 +1687,6 @@ "args": [ "--replace-system-package", "org.chromium.webview_shell,apks/SystemWebViewShell.apk", - "--use-apk-under-test-flags-file", ], "label": "//android_webview/tools/system_webview_shell:system_webview_shell_layout_test_apk", "type": "console_test_launcher",
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json index cc28eda..a9b025e3 100644 --- a/testing/buildbot/internal.chromeos.fyi.json +++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -186,6 +186,7 @@ } ], "idempotent": false, + "io_timeout": 3600, "named_caches": [ { "name": "cros_vm",
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl index 2a32b29..ac7de16 100644 --- a/testing/buildbot/mixins.pyl +++ b/testing/buildbot/mixins.pyl
@@ -953,7 +953,7 @@ 'nougat-x86-emulator': { '$mixin_append': { 'args': [ - '--avd-config=../../tools/android/avd/proto/generic_android25.textpb', + '--avd-config=../../tools/android/avd/proto/generic_android24.textpb', ], }, 'swarming': {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index 77d1e39..46adbd7 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2324,6 +2324,11 @@ '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_11.net_unittests.filter', ], }, + 'android-cronet-x86-dbg-kitkat-tests': { + 'args': [ + '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_k.net_unittests.filter', + ], + }, 'android-cronet-x86-dbg-lollipop-tests': { 'args': [ '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_l.net_unittests.filter',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 5d0943d1..87329ab 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -612,6 +612,8 @@ 'chrome_all_tast_tests': { 'swarming': { 'idempotent': False, # https://crbug.com/923426#c27 + # Tast test doesn't always output. See crbug.com/1306300 + 'io_timeout': 3600, 'shards': 6, }, 'resultdb': {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index 00723155e..7c490b7a 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -459,7 +459,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M100', - 'revision': 'version:100.0.4896.46', + 'revision': 'version:100.0.4896.49', } ], }, @@ -483,7 +483,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M99', - 'revision': 'version:99.0.4844.76', + 'revision': 'version:99.0.4844.80', } ], }, @@ -603,7 +603,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M100', - 'revision': 'version:100.0.4896.46', + 'revision': 'version:100.0.4896.49', } ], }, @@ -627,7 +627,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M99', - 'revision': 'version:99.0.4844.76', + 'revision': 'version:99.0.4844.80', } ], }, @@ -747,7 +747,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M100', - 'revision': 'version:100.0.4896.46', + 'revision': 'version:100.0.4896.49', } ], }, @@ -771,7 +771,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M99', - 'revision': 'version:99.0.4844.76', + 'revision': 'version:99.0.4844.80', } ], },
diff --git a/testing/test.gni b/testing/test.gni index a7682c6..b6c2bbf 100644 --- a/testing/test.gni +++ b/testing/test.gni
@@ -675,7 +675,7 @@ _generated_script = "$root_build_dir/bin/run_" + invoker.target_name if (is_skylab) { - generate_skylab_runner_script(_gen_runner_target) { + generate_skylab_deps(_gen_runner_target) { generated_script = _generated_script test_exe = invoker.target_name }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 64fc547c..46b36800 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -330,6 +330,37 @@ ] } ], + "AndroidTNotifications": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "RationaleShownAtFirstRequest", + "params": { + "always_show_rationale_before_requesting_permission": "true", + "notification_permission_dialog_text_variant_2": "false", + "permission_request_interval_days": "7" + }, + "enable_features": [ + "NotificationPermissionVariant" + ] + }, + { + "name": "TextVariant2", + "params": { + "always_show_rationale_before_requesting_permission": "false", + "notification_permission_dialog_text_variant_2": "true", + "permission_request_interval_days": "7" + }, + "enable_features": [ + "NotificationPermissionVariant" + ] + } + ] + } + ], "AppListLaunchRecorder": [ { "platforms": [ @@ -525,6 +556,32 @@ ] } ], + "AssistantVoiceSearch": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "ModalV2_20220310", + "params": { + "colorful_mic": "false", + "count": "2", + "enable_multi_account_check": "false", + "experiment_id": "48113782", + "min_agsa_version": "12.37" + }, + "enable_features": [ + "AssistantConsentModal", + "AssistantConsentV2", + "AssistantIntentExperimentId", + "OmniboxAssistantVoiceSearch", + "VoiceSearchAudioCapturePolicy" + ] + } + ] + } + ], "AsyncFontAccess": [ { "platforms": [ @@ -1348,31 +1405,6 @@ ] } ], - "BaseAssistantIntegration": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "colorful_mic": "false", - "count": "2", - "enable_multi_account_check": "false", - "experiment_id": "48101548", - "min_agsa_version": "12.37" - }, - "enable_features": [ - "AssistantConsentV2", - "AssistantIntentExperimentId", - "OmniboxAssistantVoiceSearch", - "VoiceSearchAudioCapturePolicy" - ] - } - ] - } - ], "BiometricAuthPwdFillAndroid": [ { "platforms": [ @@ -3498,6 +3530,21 @@ ] } ], + "HardwareToolbar": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "ToolbarUseHardwareBitmapDraw" + ] + } + ] + } + ], "HatsAudio": [ { "platforms": [ @@ -4492,23 +4539,6 @@ ] } ], - "NetworkServiceDedicatedThread": [ - { - "platforms": [ - "android", - "android_weblayer", - "android_webview" - ], - "experiments": [ - { - "name": "Disabled", - "disable_features": [ - "NetworkServiceDedicatedThread" - ] - } - ] - } - ], "NetworkServiceUsesDisplayThreadPriority": [ { "platforms": [ @@ -6509,7 +6539,6 @@ "platforms": [ "chromeos", "chromeos_lacros", - "linux", "mac", "windows" ], @@ -6522,42 +6551,6 @@ "enable_features": [ "SubframeShutdownDelay" ] - }, - { - "name": "EnabledConstantLong", - "params": { - "type": "constant-long" - }, - "enable_features": [ - "SubframeShutdownDelay" - ] - }, - { - "name": "EnabledHistoryBased", - "params": { - "type": "history-based" - }, - "enable_features": [ - "SubframeShutdownDelay" - ] - }, - { - "name": "EnabledHistoryBasedLong", - "params": { - "type": "history-based-long" - }, - "enable_features": [ - "SubframeShutdownDelay" - ] - }, - { - "name": "EnabledMemoryBased", - "params": { - "type": "memory-based" - }, - "enable_features": [ - "SubframeShutdownDelay" - ] } ] }
diff --git a/third_party/android_sdk/cipd/system_images/android-24/google_apis/x86.yaml b/third_party/android_sdk/cipd/system_images/android-24/google_apis/x86.yaml new file mode 100644 index 0000000..60bdbf5 --- /dev/null +++ b/third_party/android_sdk/cipd/system_images/android-24/google_apis/x86.yaml
@@ -0,0 +1,9 @@ +# Copyright 2022 The Chromium 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: chromium/third_party/android_sdk/public/system-images/android-24/google_apis/x86 +description: system_images;android-24;google_apis;x86 +root: ../../../../public/ +data: + - dir: system-images/android-24/google_apis/x86
diff --git a/third_party/android_sdk/cipd/system_images/android-24/google_apis_playstore/x86.yaml b/third_party/android_sdk/cipd/system_images/android-24/google_apis_playstore/x86.yaml new file mode 100644 index 0000000..dae6d22 --- /dev/null +++ b/third_party/android_sdk/cipd/system_images/android-24/google_apis_playstore/x86.yaml
@@ -0,0 +1,9 @@ +# Copyright 2022 The Chromium 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: chromium/third_party/android_sdk/public/system-images/android-24/google_apis_playstore/x86 +description: system_images;android-24;google_apis_playstore;x86 +root: ../../../../public/ +data: + - dir: system-images/android-24/google_apis_playstore/x86
diff --git a/third_party/blink/common/custom_handlers/protocol_handler_utils.cc b/third_party/blink/common/custom_handlers/protocol_handler_utils.cc index c9a75e1..bd72d33 100644 --- a/third_party/blink/common/custom_handlers/protocol_handler_utils.cc +++ b/third_party/blink/common/custom_handlers/protocol_handler_utils.cc
@@ -7,13 +7,19 @@ #include "base/containers/contains.h" #include "base/strings/string_piece.h" #include "base/strings/string_util.h" +#include "services/network/public/cpp/is_potentially_trustworthy.h" +#include "third_party/blink/public/common/scheme_registry.h" +#include "url/gurl.h" namespace blink { bool IsValidCustomHandlerScheme(const base::StringPiece scheme, - bool allow_ext_prefix, - bool& has_custom_scheme_prefix) { - has_custom_scheme_prefix = false; + ProtocolHandlerSecurityLevel security_level, + bool* has_custom_scheme_prefix) { + bool allow_scheme_prefix = + (security_level >= ProtocolHandlerSecurityLevel::kExtensionFeatures); + if (has_custom_scheme_prefix) + *has_custom_scheme_prefix = false; static constexpr const char kWebPrefix[] = "web+"; static constexpr const char kExtPrefix[] = "ext+"; @@ -21,10 +27,11 @@ static constexpr const size_t kPrefixLength = std::size(kWebPrefix) - 1; if (base::StartsWith(scheme, kWebPrefix, base::CompareCase::INSENSITIVE_ASCII) || - (allow_ext_prefix && + (allow_scheme_prefix && base::StartsWith(scheme, kExtPrefix, base::CompareCase::INSENSITIVE_ASCII))) { - has_custom_scheme_prefix = true; + if (has_custom_scheme_prefix) + *has_custom_scheme_prefix = true; // HTML5 requires that schemes with the |web+| prefix contain one or more // ASCII alphas after that prefix. auto scheme_name = scheme.substr(kPrefixLength); @@ -46,4 +53,13 @@ return base::Contains(kProtocolSafelist, base::ToLowerASCII(scheme)); } +bool IsAllowedCustomHandlerURL(const GURL& url, + ProtocolHandlerSecurityLevel security_level) { + bool has_valid_scheme = + url.SchemeIsHTTPOrHTTPS() || + (security_level == ProtocolHandlerSecurityLevel::kExtensionFeatures && + CommonSchemeRegistry::IsExtensionScheme(url.scheme())); + return has_valid_scheme && network::IsUrlPotentiallyTrustworthy(url); +} + } // namespace blink
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc index e8c1c486..f576c719 100644 --- a/third_party/blink/common/features.cc +++ b/third_party/blink/common/features.cc
@@ -333,11 +333,6 @@ // The feature is still used by virtual test suites exercising Plan B. const base::Feature kRTCUnifiedPlanByDefault{"RTCUnifiedPlanByDefault", base::FEATURE_ENABLED_BY_DEFAULT}; -// When enabled, throw an exception when an RTCPeerConnection is constructed -// with {sdpSemantics:"plan-b"} and the Deprecation Trial is not enabled. -const base::Feature kRTCDisallowPlanBOutsideDeprecationTrial{ - "RTCDisallowPlanBOutsideDeprecationTrial", - base::FEATURE_ENABLED_BY_DEFAULT}; // Determines if the SDP attrbute extmap-allow-mixed should be offered by // default or not. The default value can be overridden by passing @@ -1045,6 +1040,9 @@ // requesting contexts. const base::FeatureParam<int> kBrowsingTopicsNumberOfEpochsToExpose{ &kBrowsingTopics, "number_of_epochs_to_expose", 3}; +// The periodic topics calculation interval. +const base::FeatureParam<base::TimeDelta> kBrowsingTopicsTimePeriodPerEpoch{ + &kBrowsingTopics, "time_period_per_epoch", base::Days(7)}; // The number of top topics to derive and to keep for each epoch (week). const base::FeatureParam<int> kBrowsingTopicsNumberOfTopTopicsPerEpoch{ &kBrowsingTopics, "number_of_top_topics_per_epoch", 5}; @@ -1055,6 +1053,18 @@ // topic instead of one of the top topics. const base::FeatureParam<int> kBrowsingTopicsUseRandomTopicProbabilityPercent{ &kBrowsingTopics, "use_random_topic_probability_percent", 5}; +// How many epochs (weeks) of API usage data (i.e. topics observations) will be +// based off for the filtering of topics for a calling context. +const base::FeatureParam<int> + kBrowsingTopicsNumberOfEpochsOfObservationDataToUseForFiltering{ + &kBrowsingTopics, + "number_of_epochs_of_observation_data_to_use_for_filtering", 3}; +// The max number of observed-by context domains to keep for each top topic. +// The intent is to cap the in-use memory. +const base::FeatureParam<int> + kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToKeepPerTopic{ + &kBrowsingTopics, + "max_number_of_api_usage_context_domains_to_keep_per_topic", 1000}; // The max number of entries allowed to be retrieved from the // `BrowsingTopicsSiteDataStorage` database for each query for the API usage // contexts. The query will occur once per epoch (week) at topics calculation @@ -1063,18 +1073,15 @@ kBrowsingTopicsMaxNumberOfApiUsageContextEntriesToLoadPerEpoch{ &kBrowsingTopics, "max_number_of_api_usage_context_entries_to_load_per_epoch", 100000}; -// Encodes the rest of the configuration parameters. Each version number should -// only be mapped to one configuration set. In practice, this can be guaranteed -// by always bumping up the version number whenever parameters are updated. +// Encodes the configuration parameters above. Each version number should only +// be mapped to one configuration set. In practice, this can be guaranteed by +// always bumping up the version number whenever parameters are updated. const base::FeatureParam<int> kBrowsingTopicsConfigVersion{&kBrowsingTopics, "config_version", 1}; - -// Enable the ability to minimize processing in the WebRTC APM when all audio -// tracks are disabled. If disabled, the APM in WebRTC will ignore attempts to -// set it in a low-processing mode when all audio tracks are disabled. -const base::Feature kMinimizeAudioProcessingForUnusedOutput{ - "MinimizeAudioProcessingForUnusedOutput", - base::FEATURE_DISABLED_BY_DEFAULT}; +// The taxonomy version. This only affects the topics classification that occurs +// during this browser session, and doesn't affect the pre-existing epochs. +const base::FeatureParam<int> kBrowsingTopicsTaxonomyVersion{ + &kBrowsingTopics, "taxonomy_version", 1}; // When <dialog>s are closed, this focuses the "previously focused" element // which had focus when the <dialog> was first opened.
diff --git a/third_party/blink/public/common/custom_handlers/protocol_handler_utils.h b/third_party/blink/public/common/custom_handlers/protocol_handler_utils.h index b9889e3d..51f4892b 100644 --- a/third_party/blink/public/common/custom_handlers/protocol_handler_utils.h +++ b/third_party/blink/public/common/custom_handlers/protocol_handler_utils.h
@@ -7,6 +7,9 @@ #include "base/strings/string_piece_forward.h" #include "third_party/blink/public/common/common_export.h" +#include "third_party/blink/public/common/security/protocol_handler_security_level.h" + +class GURL; namespace blink { @@ -24,8 +27,16 @@ // insensitive match to the string "web+" (or alternatively "ext+" if allowed). bool BLINK_COMMON_EXPORT IsValidCustomHandlerScheme(const base::StringPiece scheme, - bool allow_ext_prefix, - bool& has_custom_scheme_prefix); + ProtocolHandlerSecurityLevel security_level, + bool* has_custom_scheme_prefix = nullptr); + +// This function returns whether the specified URL is allowed as a protocol +// handler parameter, as described in steps 6 and 7 (except same origin) of the +// HTML specification: +// https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters +bool BLINK_COMMON_EXPORT +IsAllowedCustomHandlerURL(const GURL& url, + ProtocolHandlerSecurityLevel security_level); } // namespace blink
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h index 16aa2edf..d203df37 100644 --- a/third_party/blink/public/common/features.h +++ b/third_party/blink/public/common/features.h
@@ -153,8 +153,6 @@ kPurgeRendererMemoryWhenBackgrounded; BLINK_COMMON_EXPORT extern const base::Feature kWindowOpenNewPopupBehavior; BLINK_COMMON_EXPORT extern const base::Feature kRTCUnifiedPlanByDefault; -BLINK_COMMON_EXPORT extern const base::Feature - kRTCDisallowPlanBOutsideDeprecationTrial; BLINK_COMMON_EXPORT extern const base::Feature kRTCOfferExtmapAllowMixed; BLINK_COMMON_EXPORT extern const base::Feature kRTCGpuCodecSupportWaiter; BLINK_COMMON_EXPORT extern const base::FeatureParam<int> @@ -467,6 +465,8 @@ BLINK_COMMON_EXPORT bool IsAllowURNsInIframeEnabled(); BLINK_COMMON_EXPORT extern const base::Feature kBrowsingTopics; +BLINK_COMMON_EXPORT extern const base::FeatureParam<base::TimeDelta> + kBrowsingTopicsTimePeriodPerEpoch; BLINK_COMMON_EXPORT extern const base::FeatureParam<int> kBrowsingTopicsNumberOfEpochsToExpose; BLINK_COMMON_EXPORT extern const base::FeatureParam<int> @@ -474,14 +474,15 @@ BLINK_COMMON_EXPORT extern const base::FeatureParam<int> kBrowsingTopicsUseRandomTopicProbabilityPercent; BLINK_COMMON_EXPORT extern const base::FeatureParam<int> + kBrowsingTopicsNumberOfEpochsOfObservationDataToUseForFiltering; +BLINK_COMMON_EXPORT extern const base::FeatureParam<int> + kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToKeepPerTopic; +BLINK_COMMON_EXPORT extern const base::FeatureParam<int> kBrowsingTopicsMaxNumberOfApiUsageContextEntriesToLoadPerEpoch; BLINK_COMMON_EXPORT extern const base::FeatureParam<int> kBrowsingTopicsConfigVersion; - -// Control switch for minimizing processing in the WebRTC APM when all audio -// tracks are disabled. -BLINK_COMMON_EXPORT extern const base::Feature - kMinimizeAudioProcessingForUnusedOutput; +BLINK_COMMON_EXPORT extern const base::FeatureParam<int> + kBrowsingTopicsTaxonomyVersion; // When <dialog>s are closed, this focuses the "previously focused" element // which had focus when the <dialog> was first opened.
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl index f202340..8abe2c7 100644 --- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl +++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -6940,6 +6940,7 @@ ambient-light-sensor attribution-reporting autoplay + browsing-topics camera ch-dpr ch-device-memory @@ -6980,6 +6981,7 @@ gyroscope hid idle-detection + interest-cohort join-ad-interest-group keyboard-map magnetometer
diff --git a/third_party/blink/public/mojom/loader/same_document_navigation_type.mojom b/third_party/blink/public/mojom/loader/same_document_navigation_type.mojom index 3f3d72e..8252702 100644 --- a/third_party/blink/public/mojom/loader/same_document_navigation_type.mojom +++ b/third_party/blink/public/mojom/loader/same_document_navigation_type.mojom
@@ -11,6 +11,6 @@ kFragment = 0, // Navigating with window.history kHistoryApi = 1, - // Navigating with AppHistoryNavigateEvent's transitionWhile() - kAppHistoryTransitionWhile = 2, + // Navigating with the navigation API NavigateEvent's transitionWhile() + kNavigationApiTransitionWhile = 2, };
diff --git a/third_party/blink/public/mojom/navigation/navigation_params.mojom b/third_party/blink/public/mojom/navigation/navigation_params.mojom index 926c92d..d8fe330e 100644 --- a/third_party/blink/public/mojom/navigation/navigation_params.mojom +++ b/third_party/blink/public/mojom/navigation/navigation_params.mojom
@@ -482,4 +482,11 @@ // When URL is about:srcdoc, this carries the srcdoc attribute's value. string srcdoc_value; + + // Whether the navigation should be treated as a "loadDataWithBaseURL" + // navigation, where the "document URL" is set to the supplied base URL + // instead of the data URL set in CommonNavigationParams' `url`. If this + // returns false, the data: URL will still be loaded, but we won't try to use + // the supplied base URL and history URL. + bool is_load_data_with_base_url = false; };
diff --git a/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom b/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom index cf48d85..e552e65 100644 --- a/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom +++ b/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom
@@ -208,6 +208,14 @@ // cookies. kClientHintPartitionedCookies = 96, + // "browsing-topics" permissions policy that controls the use of Topics API. + // https://github.com/jkarlin/topics + kBrowsingTopics = 97, + + // "interest-cohort" permissions policy that controls the use of Topics API. + // https://github.com/jkarlin/topics + kBrowsingTopicsBackwardCompatible = 98, + // Don't change assigned numbers of any item, and don't reuse removed slots. // Add new features at the end of the enum. // Also, run update_permissions_policy_enum.py in
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom index 6bfe8c9..75cf522 100644 --- a/third_party/blink/public/mojom/web_feature/web_feature.mojom +++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3116,7 +3116,7 @@ kCSSAtRuleCounterStyle = 3809, kCanvasUseColorSpace = 3810, kSelectMenuElement = 3811, - kRTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial = 3812, + kOBSOLETE_RTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial = 3812, kWebAppManifestCaptureLinks = 3813, kSanitizerAPICreated = 3814, kSanitizerAPIDefaultConfiguration = 3815, @@ -3500,6 +3500,7 @@ kV8FunctionPrototypeArguments = 4179, kV8FunctionPrototypeCaller = 4180, kBluetoothDeviceForget = 4181, + kTopicsAPI_BrowsingTopics_Method = 4182, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/DEPS b/third_party/blink/renderer/DEPS index 80469c65..299ff841 100644 --- a/third_party/blink/renderer/DEPS +++ b/third_party/blink/renderer/DEPS
@@ -68,6 +68,7 @@ "+cc/paint", "+components/crash/core/common/crash_key.h", "+components/power_scheduler", + "+net/base/features.h", "+net/cookies", "+net/http/structured_headers.h", "+services/network/public/mojom",
diff --git a/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc b/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc index 035e906..a2509e5d 100644 --- a/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc +++ b/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc
@@ -8,6 +8,7 @@ #include "third_party/blink/renderer/platform/bindings/callback_function_base.h" #include "third_party/blink/renderer/platform/bindings/callback_interface_base.h" #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" namespace blink { @@ -89,6 +90,12 @@ callback_this_ = callback_this.V8Value(callback_->CallbackRelevantScriptState()); } + if (auto* tracker = + ThreadScheduler::Current()->GetTaskAttributionTracker()) { + task_attribution_scope_ = + tracker->CreateTaskScope(callback_->CallbackRelevantScriptState(), + callback_->GetParentTaskId()); + } } return true;
diff --git a/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.h b/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.h index d9c1dafe7..3872b1b 100644 --- a/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.h +++ b/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.h
@@ -10,6 +10,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/v8_value_or_script_wrappable_adapter.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "v8/include/v8.h" namespace blink { @@ -94,6 +95,8 @@ ScriptState::Scope callback_relevant_context_scope_; v8::Context::BackupIncumbentScope backup_incumbent_scope_; + std::unique_ptr<scheduler::TaskAttributionTracker::TaskScope> + task_attribution_scope_; }; extern template class CORE_EXTERN_TEMPLATE_EXPORT
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni index 857cf93..4974f81 100644 --- a/third_party/blink/renderer/bindings/generated_in_core.gni +++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -1562,6 +1562,8 @@ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_boolean_eventlisteneroptions.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_boolean_scrollintoviewoptions.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_boolean_scrollintoviewoptions.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_boolean_string_unrestricteddouble_null.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_boolean_string_unrestricteddouble_null.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_bytestringbytestringrecord_bytestringsequencesequence.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_bytestringbytestringrecord_bytestringsequencesequence.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_compositeoperationorauto_compositeoperationorautosequence.cc",
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni index 201fed4..7bdaadd6 100644 --- a/third_party/blink/renderer/bindings/generated_in_modules.gni +++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -203,6 +203,8 @@ "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_le_scan_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_manufacturer_data_filter_init.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_manufacturer_data_filter_init.h", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_browsing_topic.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_browsing_topic.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_cable_authentication_data.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_cable_authentication_data.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_cable_registration_data.cc", @@ -859,6 +861,8 @@ "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_save_file_picker_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_scheduler_post_task_options.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_scheduler_post_task_options.h", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_script_wrappable_task_id.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_script_wrappable_task_id.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_secure_payment_confirmation_request.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_secure_payment_confirmation_request.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_sensor_error_event_init.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni index e3e3ef61..d8548bc 100644 --- a/third_party/blink/renderer/bindings/idl_in_modules.gni +++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -74,6 +74,8 @@ "//third_party/blink/renderer/modules/breakout_box/media_stream_track_processor_init.idl", "//third_party/blink/renderer/modules/breakout_box/video_track_generator.idl", "//third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.idl", + "//third_party/blink/renderer/modules/browsing_topics/browsing_topic.idl", + "//third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.idl", "//third_party/blink/renderer/modules/buckets/navigator_storage_buckets.idl", "//third_party/blink/renderer/modules/buckets/storage_bucket.idl", "//third_party/blink/renderer/modules/buckets/storage_bucket_manager.idl", @@ -637,6 +639,7 @@ "//third_party/blink/renderer/modules/scheduler/scheduler.idl", "//third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl", "//third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl", + "//third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.idl", "//third_party/blink/renderer/modules/scheduler/task_controller.idl", "//third_party/blink/renderer/modules/scheduler/task_controller_init.idl", "//third_party/blink/renderer/modules/scheduler/task_priority_change_event.idl",
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn index 01736c7..5c2f8dc5 100644 --- a/third_party/blink/renderer/core/BUILD.gn +++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1284,6 +1284,7 @@ "frame/ad_tracker_test.cc", "frame/anchor_element_listener_test.cc", "frame/attribution_response_parsing_test.cc", + "frame/attribution_src_loader_test.cc", "frame/browser_controls_test.cc", "frame/child_frame_compositing_helper_test.cc", "frame/csp/content_security_policy_test.cc",
diff --git a/third_party/blink/renderer/core/animation/BUILD.gn b/third_party/blink/renderer/core/animation/BUILD.gn index 61bb910..dae77c3 100644 --- a/third_party/blink/renderer/core/animation/BUILD.gn +++ b/third_party/blink/renderer/core/animation/BUILD.gn
@@ -56,8 +56,6 @@ "css/css_animation_data.h", "css/css_animation_update.cc", "css/css_animation_update.h", - "css/css_animation_update_scope.cc", - "css/css_animation_update_scope.h", "css/css_animations.cc", "css/css_animations.h", "css/css_keyframe_effect_model.cc",
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc index dab0ddb..14f074f2 100644 --- a/third_party/blink/renderer/core/animation/css/css_animations.cc +++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -40,7 +40,6 @@ #include "third_party/blink/renderer/core/animation/compositor_animations.h" #include "third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h" #include "third_party/blink/renderer/core/animation/css/css_animation.h" -#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h" #include "third_party/blink/renderer/core/animation/css/css_keyframe_effect_model.h" #include "third_party/blink/renderer/core/animation/css/css_scroll_timeline.h" #include "third_party/blink/renderer/core/animation/css/css_transition.h" @@ -62,6 +61,7 @@ #include "third_party/blink/renderer/core/css/css_property_equality.h" #include "third_party/blink/renderer/core/css/css_value_list.h" #include "third_party/blink/renderer/core/css/parser/css_variable_parser.h" +#include "third_party/blink/renderer/core/css/post_style_update_scope.h" #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h" #include "third_party/blink/renderer/core/css/properties/css_property.h" #include "third_party/blink/renderer/core/css/properties/css_property_ref.h" @@ -1495,7 +1495,7 @@ const ComputedStyle* old_style = animating_element.GetComputedStyle(); if (RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled()) { - if (auto* data = CSSAnimationUpdateScope::CurrentData()) + if (auto* data = PostStyleUpdateScope::CurrentAnimationData()) old_style = data->GetOldStyle(animating_element); }
diff --git a/third_party/blink/renderer/core/animation/document_animations.h b/third_party/blink/renderer/core/animation/document_animations.h index 17c0980c..9490a6f 100644 --- a/third_party/blink/renderer/core/animation/document_animations.h +++ b/third_party/blink/renderer/core/animation/document_animations.h
@@ -103,7 +103,7 @@ // ApplyPendingElementUpdates. // // It's invalid to call this function if there is no current - // CSSAnimationUpdateScope. + // PostStyleUpdateScope. void AddElementWithPendingAnimationUpdate(Element&); // Apply pending updates for any elements previously added during AddElement-
diff --git a/third_party/blink/renderer/core/css/affected_by_pseudo_test.cc b/third_party/blink/renderer/core/css/affected_by_pseudo_test.cc index 9e844ff..6e9d2441 100644 --- a/third_party/blink/renderer/core/css/affected_by_pseudo_test.cc +++ b/third_party/blink/renderer/core/css/affected_by_pseudo_test.cc
@@ -2516,4 +2516,135 @@ {kSiblingsAffectedByHas, false}}); } +TEST_F(AffectedByPseudoTest, AffectedBySelectorQuery) { + SetHtmlInnerHTML(R"HTML( + <div id=div1> + <div id=div2 class='a'> + <div id=div3></div> + </div> + <div id=div4 class='e'> + <div id=div5> + <div id=div6></div> + </div> + </div> + <div id=div7 class='b'> + <div id=div8 class='c'> + <div id=div9 class='d'> + <div id=div10></div> + </div> + </div> + </div> + </div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + CheckAffectedByFlagsForHas( + "div1", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div2", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div3", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div4", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div5", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div6", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div7", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div8", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div9", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div10", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + + StaticElementList* result = + GetDocument().QuerySelectorAll(".a:has(~ .b > .c > .d) ~ .e"); + ASSERT_EQ(1U, result->length()); + EXPECT_EQ(result->item(0)->GetIdAttribute(), "div4"); + + UpdateAllLifecyclePhasesForTest(); + CheckAffectedByFlagsForHas( + "div1", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div2", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div3", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div4", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div5", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div6", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div7", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div8", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div9", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); + CheckAffectedByFlagsForHas( + "div10", {{kAffectedBySubjectHas, false}, + {kAffectedByNonSubjectHas, false}, + {kAncestorsOrAncestorSiblingsAffectedByHas, false}, + {kSiblingsAffectedByHas, false}}); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/css/build.gni b/third_party/blink/renderer/core/css/build.gni index ce29f22..e686570 100644 --- a/third_party/blink/renderer/core/css/build.gni +++ b/third_party/blink/renderer/core/css/build.gni
@@ -500,6 +500,8 @@ "part_names.cc", "part_names.h", "pending_sheet_type.h", + "post_style_update_scope.cc", + "post_style_update_scope.h", "properties/computed_style_utils.cc", "properties/computed_style_utils.h", "properties/css_bitset.h", @@ -708,6 +710,7 @@ "font_size_functions_test.cc", "font_update_invalidation_test.cc", "force_dark_test.cc", + "has_argument_match_context_test.cc", "invalidation/invalidation_set_test.cc", "invalidation/pending_invalidations_test.cc", "invalidation/style_invalidator_test.cc",
diff --git a/third_party/blink/renderer/core/css/container_query_test.cc b/third_party/blink/renderer/core/css/container_query_test.cc index 276f19d..ed051d7 100644 --- a/third_party/blink/renderer/core/css/container_query_test.cc +++ b/third_party/blink/renderer/core/css/container_query_test.cc
@@ -5,11 +5,11 @@ #include "third_party/blink/renderer/core/css/container_query.h" #include "third_party/abseil-cpp/absl/types/optional.h" -#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h" #include "third_party/blink/renderer/core/animation/document_animations.h" #include "third_party/blink/renderer/core/animation/element_animations.h" #include "third_party/blink/renderer/core/css/css_container_rule.h" #include "third_party/blink/renderer/core/css/css_test_helpers.h" +#include "third_party/blink/renderer/core/css/post_style_update_scope.h" #include "third_party/blink/renderer/core/css/properties/css_property_ref.h" #include "third_party/blink/renderer/core/css/style_engine.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -114,12 +114,12 @@ } size_t GetOldStylesCount(String html) { - // Creating a CSSAnimationUpdateScope prevents old styles from being - // cleared until this function completes. - CSSAnimationUpdateScope animation_update_scope(GetDocument()); + // Creating a PostStyleUpdateScope prevents old styles from being cleared + // until this function completes. + PostStyleUpdateScope post_style_update_scope(GetDocument()); SetBodyInnerHTML(html); - DCHECK(CSSAnimationUpdateScope::CurrentData()); - return CSSAnimationUpdateScope::CurrentData()->old_styles_.size(); + DCHECK(PostStyleUpdateScope::CurrentAnimationData()); + return PostStyleUpdateScope::CurrentAnimationData()->old_styles_.size(); } }; @@ -575,7 +575,7 @@ // Simulate a style and layout pass with multiple rounds of style recalc. { - CSSAnimationUpdateScope animation_update_scope(GetDocument()); + PostStyleUpdateScope post_style_update_scope(GetDocument()); // Should transition between [10px, 20px]. (Intermediate round). GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer( @@ -596,7 +596,7 @@ EXPECT_EQ(0u, GetAnimationsCount(target)); } - // CSSAnimationUpdateScope going out of scope applies the update. + // PostStyleUpdateScope going out of scope applies the update. EXPECT_EQ(1u, GetAnimationsCount(target)); // Verify that the newly-updated Animation produces the correct value. @@ -648,7 +648,7 @@ // Simulate a style and layout pass with multiple rounds of style recalc. { - CSSAnimationUpdateScope animation_update_scope(GetDocument()); + PostStyleUpdateScope post_style_update_scope(GetDocument()); // No transition property present. (Intermediate round). GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer( @@ -669,7 +669,7 @@ EXPECT_EQ(0u, GetAnimationsCount(target)); } - // CSSAnimationUpdateScope going out of scope applies the update. + // PostStyleUpdateScope going out of scope applies the update. EXPECT_EQ(1u, GetAnimationsCount(target)); // Verify that the newly-updated Animation produces the correct value. @@ -721,7 +721,7 @@ // Simulate a style and layout pass with multiple rounds of style recalc. { - CSSAnimationUpdateScope animation_update_scope(GetDocument()); + PostStyleUpdateScope post_style_update_scope(GetDocument()); // No transition property present yet. (Intermediate round). GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer( @@ -742,7 +742,7 @@ EXPECT_EQ(0u, GetAnimationsCount(target)); } - // CSSAnimationUpdateScope going out of scope applies the update. + // PostStyleUpdateScope going out of scope applies the update. // We ultimately ended up with no transition, hence we should have no // Animations on the element. EXPECT_EQ(0u, GetAnimationsCount(target)); @@ -794,7 +794,7 @@ // Simulate a style and layout pass with multiple rounds of style recalc. { - CSSAnimationUpdateScope animation_update_scope(GetDocument()); + PostStyleUpdateScope post_style_update_scope(GetDocument()); // Animation at 20%. (Intermediate round). GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer( @@ -815,7 +815,7 @@ EXPECT_EQ(0u, GetAnimationsCount(target)); } - // CSSAnimationUpdateScope going out of scope applies the update. + // PostStyleUpdateScope going out of scope applies the update. EXPECT_EQ(1u, GetAnimationsCount(target)); // Verify that the newly-updated Animation produces the correct value. @@ -865,7 +865,7 @@ // Simulate a style and layout pass with multiple rounds of style recalc. { - CSSAnimationUpdateScope animation_update_scope(GetDocument()); + PostStyleUpdateScope post_style_update_scope(GetDocument()); // Animation should appear to be canceled. (Intermediate round). GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer( @@ -880,7 +880,7 @@ EXPECT_EQ(1u, GetAnimationsCount(target)); } - // CSSAnimationUpdateScope going out of scope applies the update. + // PostStyleUpdateScope going out of scope applies the update. // (Although since we didn't cancel, there is nothing to update). EXPECT_EQ(1u, GetAnimationsCount(target)); @@ -893,7 +893,7 @@ // Change width such that container query matches, and cancel the animation // for real this time. Note that since we no longer have a - // CSSAnimationUpdateScope above us, the CSSAnimationUpdateScope within + // PostStyleUpdateScope above us, the PostStyleUpdateScope within // UpdateAllLifecyclePhasesForTest will apply the update. container->SetInlineStyleProperty(CSSPropertyID::kWidth, "130px"); UpdateAllLifecyclePhasesForTest();
diff --git a/third_party/blink/renderer/core/css/has_argument_match_context.cc b/third_party/blink/renderer/core/css/has_argument_match_context.cc index e20afe2..0462245 100644 --- a/third_party/blink/renderer/core/css/has_argument_match_context.cc +++ b/third_party/blink/renderer/core/css/has_argument_match_context.cc
@@ -64,10 +64,9 @@ sibling_combinator_between_child_or_descendant_combinator_ = true; } contains_child_or_descendant_combinator = true; - if (DepthFixed()) { + if (DepthFixed()) depth_limit_++; - adjacent_distance_limit_ = 0; - } + adjacent_distance_limit_ = 0; break; case CSSSelector::kRelativeDirectAdjacent: @@ -98,6 +97,7 @@ return; } } + DCHECK_NE(leftmost_relation_, CSSSelector::kSubSelector); } HasArgumentSubtreeIterator::HasArgumentSubtreeIterator(
diff --git a/third_party/blink/renderer/core/css/has_argument_match_context.h b/third_party/blink/renderer/core/css/has_argument_match_context.h index 5ec9e41..fe9ca585 100644 --- a/third_party/blink/renderer/core/css/has_argument_match_context.h +++ b/third_party/blink/renderer/core/css/has_argument_match_context.h
@@ -10,7 +10,7 @@ namespace blink { -class HasArgumentMatchContext { +class CORE_EXPORT HasArgumentMatchContext { STACK_ALLOCATED(); public: @@ -49,13 +49,13 @@ // Case 1: (kDescendant, 0, max) // - Argument selector conditions // - Starts with descendant combinator. - // - E.g. ':has(.a)', ':has(.a)', ':has(.a ~ .b > .c)' + // - E.g. ':has(.a)', ':has(.a ~ .b)', ':has(.a ~ .b > .c)' // - Traverse all descendants of the :has scope element. // Case 2: (kChild, 0, max) // - Argument selector conditions // - Starts with child combinator. // - At least one descendant combinator. - // - E.g. ':has(> .a .b)', ':has(> .a ~ .b .c)' + // - E.g. ':has(> .a .b)', ':has(> .a ~ .b .c)', ':has(> .a + .b .c)' // - Traverse all descendants of the :has scope element. // Case 3: (kChild, 0, n) // - Argument selector conditions @@ -95,7 +95,7 @@ // - At least one subsequent-sibling combinator to the left of every // descendant or child combinator. // - At least 1 descendant combinator. - // - E.g. ':has(+ .a ~ .b .c)', ':has(+ .a ~ .b > .c + .e .f)' + // - E.g. ':has(+ .a ~ .b .c)', ':has(+ .a ~ .b > .c + .d .e)' // - Traverse all the subsequent sibling subtrees of the :has scope element. // (all subsequent siblings and it's descendants) // Case 8: (kDirectAdjacent, max, 0) @@ -114,7 +114,7 @@ // - No descendant combinator. // - E.g. // - ':has(+ .a ~ .b > .c)' : (kDirectAdjacent, max, 1) - // - ':has(+ .a ~ .b > .c + .e >.f)' : (kDirectAdjacent, max, 2) + // - ':has(+ .a ~ .b > .c + .d >.e)' : (kDirectAdjacent, max, 2) // - Traverse depth n elements of all subsequent sibling subtree of the // :has scope element. // Case 10: (kDirectAdjacent, n, max) @@ -155,7 +155,7 @@ // - Traverse the depth m elements of the distance n sibling subtree of // the :has scope element. (elements at depth m of the descendant subtree // of the sibling element at distance n) - CSSSelector::RelationType leftmost_relation_; + CSSSelector::RelationType leftmost_relation_{CSSSelector::kSubSelector}; int adjacent_distance_limit_; int depth_limit_;
diff --git a/third_party/blink/renderer/core/css/has_argument_match_context_test.cc b/third_party/blink/renderer/core/css/has_argument_match_context_test.cc new file mode 100644 index 0000000..7476cd8 --- /dev/null +++ b/third_party/blink/renderer/core/css/has_argument_match_context_test.cc
@@ -0,0 +1,124 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/has_argument_match_context.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/css/css_test_helpers.h" +#include "third_party/blink/renderer/core/css/parser/css_parser.h" +#include "third_party/blink/renderer/core/css/parser/css_parser_context.h" + +namespace blink { + +namespace { + +const int kMax = std::numeric_limits<int>::max(); + +void RunTest(const String& selector_text, + CSSSelector::RelationType expected_leftmost_relation, + int expected_adjacent_distance_limit, + int expected_depth_limit) { + CSSSelectorList selector_list = + css_test_helpers::ParseSelectorList(selector_text); + HasArgumentMatchContext context( + selector_list.First()->SelectorList()->First()); + + EXPECT_EQ(expected_leftmost_relation, context.LeftmostRelation()) + << "Failed : " << selector_text; + EXPECT_EQ(expected_adjacent_distance_limit, context.AdjacentDistanceLimit()) + << "Failed : " << selector_text; + EXPECT_EQ(expected_depth_limit, context.DepthLimit()) + << "Failed : " << selector_text; +} + +} // namespace + +TEST(HasArgumentMatchContextTest, TestArgumentMatchContext) { + RunTest(":has(.a)", CSSSelector::kRelativeDescendant, + /* expected_adjacent_distance_limit */ 0, + /* expected_depth_limit */ kMax); + RunTest(":has(.a ~ .b)", CSSSelector::kRelativeDescendant, + /* expected_adjacent_distance_limit */ 0, + /* expected_depth_limit */ kMax); + RunTest(":has(.a ~ .b > .c)", CSSSelector::kRelativeDescendant, + /* expected_adjacent_distance_limit */ 0, + /* expected_depth_limit */ kMax); + RunTest(":has(> .a .b)", CSSSelector::kRelativeChild, + /* expected_adjacent_distance_limit */ 0, + /* expected_depth_limit */ kMax); + RunTest(":has(> .a ~ .b .c)", CSSSelector::kRelativeChild, + /* expected_adjacent_distance_limit */ 0, + /* expected_depth_limit */ kMax); + RunTest(":has(> .a + .b .c)", CSSSelector::kRelativeChild, + /* expected_adjacent_distance_limit */ 0, + /* expected_depth_limit */ kMax); + RunTest(":has(> .a)", CSSSelector::kRelativeChild, + /* expected_adjacent_distance_limit */ 0, + /* expected_depth_limit */ 1); + RunTest(":has(> .a ~ .b > .c)", CSSSelector::kRelativeChild, + /* expected_adjacent_distance_limit */ 0, + /* expected_depth_limit */ 2); + RunTest(":has(~ .a .b)", CSSSelector::kRelativeIndirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ kMax); + RunTest(":has(~ .a + .b > .c ~ .d .e)", + CSSSelector::kRelativeIndirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ kMax); + RunTest(":has(~ .a)", CSSSelector::kRelativeIndirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ 0); + RunTest(":has(~ .a + .b ~ .c)", CSSSelector::kRelativeIndirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ 0); + RunTest(":has(~ .a > .b)", CSSSelector::kRelativeIndirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ 1); + RunTest(":has(~ .a + .b > .c ~ .d > .e)", + CSSSelector::kRelativeIndirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ 2); + RunTest(":has(+ .a ~ .b .c)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ kMax); + RunTest(":has(+ .a ~ .b > .c + .d .e)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ kMax); + RunTest(":has(+ .a ~ .b)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ 0); + RunTest(":has(+ .a + .b ~ .c)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ 0); + RunTest(":has(+ .a ~ .b > .c)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ 1); + RunTest(":has(+ .a ~ .b > .c + .d > .e)", + CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ kMax, + /* expected_depth_limit */ 2); + RunTest(":has(+ .a .b)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 1, + /* expected_depth_limit */ kMax); + RunTest(":has(+ .a > .b + .c .d)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 1, + /* expected_depth_limit */ kMax); + RunTest(":has(+ .a + .b > .c .d)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 2, + /* expected_depth_limit */ kMax); + RunTest(":has(+ .a)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 1, + /* expected_depth_limit */ 0); + RunTest(":has(+ .a + .b + .c)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 3, + /* expected_depth_limit */ 0); + RunTest(":has(+ .a > .b)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 1, + /* expected_depth_limit */ 1); + RunTest(":has(+ .a + .b > .c ~ .d > .e)", + CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 2, + /* expected_depth_limit */ 2); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css/css_animation_update_scope.cc b/third_party/blink/renderer/core/css/post_style_update_scope.cc similarity index 70% rename from third_party/blink/renderer/core/animation/css/css_animation_update_scope.cc rename to third_party/blink/renderer/core/css/post_style_update_scope.cc index af6e843..5a437826 100644 --- a/third_party/blink/renderer/core/animation/css/css_animation_update_scope.cc +++ b/third_party/blink/renderer/core/css/post_style_update_scope.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h" +#include "third_party/blink/renderer/core/css/post_style_update_scope.h" #include "third_party/blink/renderer/core/animation/css/css_animations.h" #include "third_party/blink/renderer/core/animation/document_animations.h" @@ -15,34 +15,36 @@ namespace blink { -CSSAnimationUpdateScope* CSSAnimationUpdateScope::current_ = nullptr; +PostStyleUpdateScope* PostStyleUpdateScope::current_ = nullptr; -CSSAnimationUpdateScope::Data* CSSAnimationUpdateScope::CurrentData() { +PostStyleUpdateScope::AnimationData* +PostStyleUpdateScope::CurrentAnimationData() { if (!RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled()) return nullptr; - return current_ ? ¤t_->data_ : nullptr; + return current_ ? ¤t_->animation_data_ : nullptr; } -CSSAnimationUpdateScope::CSSAnimationUpdateScope(Document& document) +PostStyleUpdateScope::PostStyleUpdateScope(Document& document) : document_(document) { if (!current_) current_ = this; } -CSSAnimationUpdateScope::~CSSAnimationUpdateScope() { +PostStyleUpdateScope::~PostStyleUpdateScope() { if (current_ == this) { if (RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled()) Apply(); + document_.ClearFocusedElementIfNeeded(); current_ = nullptr; } } -void CSSAnimationUpdateScope::Apply() { +void PostStyleUpdateScope::Apply() { StyleEngine::InApplyAnimationUpdateScope in_apply_animation_update_scope( document_.GetStyleEngine()); HeapHashSet<Member<Element>> pending; - std::swap(pending, data_.elements_with_pending_updates_); + std::swap(pending, animation_data_.elements_with_pending_updates_); for (auto& element : pending) { ElementAnimations* element_animations = element->GetElementAnimations(); @@ -51,23 +53,24 @@ element_animations->CssAnimations().MaybeApplyPendingUpdate(element.Get()); } - DCHECK(data_.elements_with_pending_updates_.IsEmpty()) + DCHECK(animation_data_.elements_with_pending_updates_.IsEmpty()) << "MaybeApplyPendingUpdate must not set further pending updates"; } -void CSSAnimationUpdateScope::Data::SetPendingUpdate( +void PostStyleUpdateScope::AnimationData::SetPendingUpdate( Element& element, const CSSAnimationUpdate& update) { element.EnsureElementAnimations().CssAnimations().SetPendingUpdate(update); elements_with_pending_updates_.insert(&element); } -void CSSAnimationUpdateScope::Data::StoreOldStyleIfNeeded(Element& element) { +void PostStyleUpdateScope::AnimationData::StoreOldStyleIfNeeded( + Element& element) { old_styles_.insert( &element, scoped_refptr<const ComputedStyle>(element.GetComputedStyle())); } -const ComputedStyle* CSSAnimationUpdateScope::Data::GetOldStyle( +const ComputedStyle* PostStyleUpdateScope::AnimationData::GetOldStyle( Element& element) const { auto iter = old_styles_.find(&element); if (iter == old_styles_.end())
diff --git a/third_party/blink/renderer/core/animation/css/css_animation_update_scope.h b/third_party/blink/renderer/core/css/post_style_update_scope.h similarity index 68% rename from third_party/blink/renderer/core/animation/css/css_animation_update_scope.h rename to third_party/blink/renderer/core/css/post_style_update_scope.h index 557a4154..b088640 100644 --- a/third_party/blink/renderer/core/animation/css/css_animation_update_scope.h +++ b/third_party/blink/renderer/core/css/post_style_update_scope.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_ANIMATION_UPDATE_SCOPE_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_ANIMATION_UPDATE_SCOPE_H_ +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_POST_STYLE_UPDATE_SCOPE_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_POST_STYLE_UPDATE_SCOPE_H_ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" @@ -18,24 +18,25 @@ class ComputedStyle; class CSSAnimationUpdate; -// CSSAnimationUpdateScope applies pending animation on destruction, if it -// is the *current* scope. A CSSAnimationUpdateScope becomes the current -// scope upon construction if there isn't one already. -class CORE_EXPORT CSSAnimationUpdateScope { +// PostStyleUpdateScope applies pending animation, and initiates clearing of the +// focused element, on destruction, if it is the *current* scope. A +// PostStyleUpdateScope becomes the current scope upon construction if there +// isn't one already. +class CORE_EXPORT PostStyleUpdateScope { STACK_ALLOCATED(); public: - explicit CSSAnimationUpdateScope(Document&); - ~CSSAnimationUpdateScope(); + explicit PostStyleUpdateScope(Document&); + ~PostStyleUpdateScope(); - class Data { + class AnimationData { STACK_ALLOCATED(); public: // Set a pending CSSAnimationUpdate for a given Element. // // The update will be automatically applied when the owning - // CSSAnimationUpdateScope object goes out of scope. + // PostStyleUpdateScope object goes out of scope. void SetPendingUpdate(Element&, const CSSAnimationUpdate&); // When calculating transition updates, we need the old style of the element @@ -49,8 +50,8 @@ // it as the old style. If an old style was already stored for this Element, // this function does nothing. // - // The old styles remain until the CSSAnimationUpdateScope/Data object goes - // out of scope. + // The old styles remain until the PostStyleUpdateScope object goes out of + // scope. void StoreOldStyleIfNeeded(Element&); // If an old-style was previously stored using StoreOldStyleIfNeeded, @@ -59,7 +60,7 @@ const ComputedStyle* GetOldStyle(Element&) const; private: - friend class CSSAnimationUpdateScope; + friend class PostStyleUpdateScope; friend class ContainerQueryTest; HeapHashSet<Member<Element>> elements_with_pending_updates_; @@ -67,19 +68,19 @@ old_styles_; }; - static Data* CurrentData(); + static AnimationData* CurrentAnimationData(); private: Document& document_; - // Note that |data_| is only used if the CSSAnimationUpdateScope is the + // Note that |animation_data_| is only used if the PostStyleUpdateScope is the // current scope. Otherwise it will remain empty. - Data data_; + AnimationData animation_data_; void Apply(); - static CSSAnimationUpdateScope* current_; + static PostStyleUpdateScope* current_; }; } // namespace blink -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_ANIMATION_UPDATE_SCOPE_H_ +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_POST_STYLE_UPDATE_SCOPE_H_
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc index 7ca965f6b..656807dd 100644 --- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc +++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -32,7 +32,6 @@ #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h" #include "third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h" -#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h" #include "third_party/blink/renderer/core/animation/css/css_animations.h" #include "third_party/blink/renderer/core/animation/document_animations.h" #include "third_party/blink/renderer/core/animation/element_animations.h" @@ -55,6 +54,7 @@ #include "third_party/blink/renderer/core/css/font_face.h" #include "third_party/blink/renderer/core/css/page_rule_collector.h" #include "third_party/blink/renderer/core/css/part_names.h" +#include "third_party/blink/renderer/core/css/post_style_update_scope.h" #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h" #include "third_party/blink/renderer/core/css/properties/css_property.h" #include "third_party/blink/renderer/core/css/properties/css_property_ref.h" @@ -148,7 +148,7 @@ StyleResolverState& state, Element& element) { if (RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled()) { - if (auto* data = CSSAnimationUpdateScope::CurrentData()) { + if (auto* data = PostStyleUpdateScope::CurrentAnimationData()) { if (ShouldStoreOldStyle(style_recalc_context, state)) data->StoreOldStyleIfNeeded(element); } @@ -161,7 +161,7 @@ return; if (RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled()) { - if (auto* data = CSSAnimationUpdateScope::CurrentData()) + if (auto* data = PostStyleUpdateScope::CurrentAnimationData()) data->SetPendingUpdate(element, state.AnimationUpdate()); } else { element.EnsureElementAnimations().CssAnimations().SetPendingUpdate(
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc index 3f28d54b..ad7fe57c 100644 --- a/third_party/blink/renderer/core/css/selector_checker.cc +++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -748,33 +748,36 @@ auto cache_result = map.insert(has_scope_element, false); // Mark as checked if (!cache_result.is_new_entry) { // Was already marked as checked - - // The SiblingsAffectedByHas flag is set only when an element is a - // sibling of the :has() scope element during the subselector matching. - // But during the matching, the matching status of some elements are - // cached and those element cannot be a sibling of the :has() scope - // element in case that the subselector starts with adjacent combinator. - // 1. MatchSelector() gives some possibly matched elements, and those - // are cached as matched. - // 2. If an element is not matched, then it is cached as not matched. - // We need to set missing SiblingAffectedByHas flags for the cached - // elements before returning early. - switch (leftmost_relation) { - case CSSSelector::kRelativeDirectAdjacent: - if (Element* next_sibling = - ElementTraversal::NextSibling(*has_scope_element)) - next_sibling->SetSiblingsAffectedByHas(); - break; - case CSSSelector::kRelativeIndirectAdjacent: - for (Element* next_sibling = - ElementTraversal::NextSibling(*has_scope_element); - next_sibling && !next_sibling->SiblingsAffectedByHas(); - next_sibling = ElementTraversal::NextSibling(*next_sibling)) { - next_sibling->SetSiblingsAffectedByHas(); - } - break; - default: - break; + if (mode_ == kResolvingStyle) { + // The SiblingsAffectedByHas flag is set only when an element is a + // sibling of the :has() scope element during the subselector + // matching. But during the matching, the matching status of some + // elements are cached and those element cannot be a sibling of the + // :has() scope element in case that the subselector starts with + // adjacent combinator. + // 1. MatchSelector() gives some possibly matched elements, and those + // are cached as matched. + // 2. If an element is not matched, then it is cached as not matched. + // We need to set missing SiblingAffectedByHas flags for the cached + // elements before returning early. + switch (leftmost_relation) { + case CSSSelector::kRelativeDirectAdjacent: + if (Element* next_sibling = + ElementTraversal::NextSibling(*has_scope_element)) + next_sibling->SetSiblingsAffectedByHas(); + break; + case CSSSelector::kRelativeIndirectAdjacent: + for (Element* next_sibling = + ElementTraversal::NextSibling(*has_scope_element); + next_sibling && !next_sibling->SiblingsAffectedByHas(); + next_sibling = + ElementTraversal::NextSibling(*next_sibling)) { + next_sibling->SetSiblingsAffectedByHas(); + } + break; + default: + break; + } } if (cache_result.stored_value->value) // Was already marked as matched @@ -784,64 +787,13 @@ } sub_context.selector = selector; - - bool depth_fixed = has_argument_match_context.DepthFixed(); - - // To prevent incorrect 'NotChecked' status while matching ':has' pseudo - // class, change the argument matching context scope when the ':has' - // argument matching traversal cannot be fixed with a certain depth and - // adjacent distance. - // - // For example, When we tries to match '.a:has(.b .c) .d' on below DOM, - // <div id=d1 class="a"> - // <div id=d2 class="b"> - // <div id=d3 class="a"> - // <div id=d4 class="c"> - // <div id=d5 class="d"></div> - // </div> - // </div> - // </div> - // </div> - // the ':has(.b .c)' selector will be checked on the #d3 element first - // because the selector '.a:has(.b .c) .d' will be matched upward from - // the #d5 element. - // 1) '.d' will be matched first on #d5 - // 2) move to the #d3 until the '.a' matched - // 3) match the ':has(.b .c)' on the #d3 - // 3.1) match the argument selector '.b .c' on the descendants of #d3 - // 4) move to the #d1 until the '.a' matched - // 5) match the ':has(.b .c)' on the #d1 - // 5.1) match the argument selector '.b .c' on the descendants of #d1 - // - // The argument selector '.b .c' will not be matched on the #d4 at this - // step if the argument matching scope is limited to #d3. But the '.b .c' - // can be matched on the #d4 if the argument matching scope is #d1. - // To prevent duplicated argument matching operation, the #d1 should be - // marked as 'Matched' at the step 3. - // - // TODO(blee@igalia.com) Need to clarify the :scope dependency in relative - // selector definition. - // - spec : https://www.w3.org/TR/selectors-4/#relative - // - csswg issue : https://github.com/w3c/csswg-drafts/issues/6399 - if (!depth_fixed) { - sub_context.relative_leftmost_element = - &has_scope_element->GetTreeScope().RootNode(); - } else if (has_argument_match_context.AdjacentDistanceFixed()) { - if (ContainerNode* parent_node = has_scope_element->parentNode()) { - sub_context.relative_leftmost_element = - Traversal<Element>::FirstChild(*parent_node); - } else { - sub_context.relative_leftmost_element = has_scope_element; - } - } else { - sub_context.relative_leftmost_element = has_scope_element; - } + sub_context.relative_leftmost_element = has_scope_element; bool selector_matched = false; for (HasArgumentSubtreeIterator iterator(*has_scope_element, has_argument_match_context); !iterator.AtEnd(); ++iterator) { - if (depth_fixed && !iterator.AtFixedDepth()) { + if (has_argument_match_context.DepthFixed() && !iterator.AtFixedDepth()) { // We can skip subselector matching on some elements at a certain depth // when the subselector doesn't have descendant combinator. (e.g. For // the style rule '.a:has(> .b > .c) {}', we don't need to match the @@ -851,7 +803,7 @@ // flags of the skipped elements will not set. // To prevent this, marks the flags of skipped elements if those are // under the depth limit. - if (iterator.UnderDepthLimit()) { + if (mode_ == kResolvingStyle && iterator.UnderDepthLimit()) { SetAffectedByHasFlag(iterator.CurrentElement(), iterator.AtSiblingOfHasScope()); } @@ -896,36 +848,45 @@ } if (selector_matched) { - // Need to walk up to set 'AncestorsOrAncestorSiblingsAffectedByHas' - // or 'SiblingsAffectedByHas' flag so that the StyleEngine can walk up - // to find the elements affected by subject or non-subject :has(). - // - // StyleEngine tries to find elements affected by :has() by walking up - // siblings or ancestors a mutated element only when an element has the - // flags set. If an element doesn't have those flags set, then the - // StyleEngine will stop the upward tree walk at the element. - // - // HasArgumentSubtreeIterator traverses the sub-tree in the reversed - // DOM tree walk order for preventing O(n^2) matching problem of - // multiple elements affected by :has(). Due to this traversal order, - // this early returning can break the upward tree walk. - // - // To prevent the problem, walks up until reach to the scope element - // and marks elements as 'AncestorsOrAncestorSiblingsAffectedByHas' or - // 'SiblingsAffectedByHas' before returning. - // - // Similar to the DynamicRestyleFlags in the ContainerNode, these flags - // will never be reset. - for (AffectedByHasIterator affected_by_has_iterator(iterator); - !affected_by_has_iterator.AtEnd(); ++affected_by_has_iterator) { - SetAffectedByHasFlag(affected_by_has_iterator.CurrentElement(), - affected_by_has_iterator.AtSiblingOfHasScope()); + if (mode_ == kResolvingStyle) { + // Need to traverse to ancestors or siblings to set the affected-by- + // has flags ('AncestorsOrAncestorSiblingsAffectedByHas' or + // 'SiblingsAffectedByHas') so that the StyleEngine can traverse to + // ancestors or siblings to find the elements affected by subject or + // non-subject :has(). + // + // StyleEngine tries to find elements affected by :has() by traversing + // siblings or ancestors of a mutated element only when an element has + // the flags set. If an element doesn't have those flags set, then the + // StyleEngine will stop the traversal at the element. + // + // HasArgumentSubtreeIterator traverses the subtree in the reversed + // DOM tree walk order to prevent duplicated subtree traversal caused + // by the multiple elements affected by :has(). Due to this traversal + // order, this early returning can break the traversal to ancestors or + // siblings. + // + // To prevent the problem, traverse to ancestors or siblings until + // reach to the scope element and marks elements as + // 'AncestorsOrAncestorSiblingsAffectedByHas' or + // 'SiblingsAffectedByHas' before returning. + // + // Similar to the DynamicRestyleFlags in the ContainerNode, these + // flags will never be reset. + for (AffectedByHasIterator affected_by_has_iterator(iterator); + !affected_by_has_iterator.AtEnd(); ++affected_by_has_iterator) { + SetAffectedByHasFlag( + affected_by_has_iterator.CurrentElement(), + affected_by_has_iterator.AtSiblingOfHasScope()); + } } return true; } - SetAffectedByHasFlag(iterator.CurrentElement(), - iterator.AtSiblingOfHasScope()); + if (mode_ == kResolvingStyle) { + SetAffectedByHasFlag(iterator.CurrentElement(), + iterator.AtSiblingOfHasScope()); + } } } return false;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc index 0db584e5..9ea03f4 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc
@@ -84,7 +84,6 @@ #include "third_party/blink/renderer/bindings/core/v8/window_proxy.h" #include "third_party/blink/renderer/core/accessibility/ax_context.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" -#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h" #include "third_party/blink/renderer/core/animation/document_animations.h" #include "third_party/blink/renderer/core/animation/document_timeline.h" #include "third_party/blink/renderer/core/animation/pending_animations.h" @@ -102,6 +101,7 @@ #include "third_party/blink/renderer/core/css/media_query_matcher.h" #include "third_party/blink/renderer/core/css/media_values.h" #include "third_party/blink/renderer/core/css/parser/css_parser.h" +#include "third_party/blink/renderer/core/css/post_style_update_scope.h" #include "third_party/blink/renderer/core/css/properties/css_property.h" #include "third_party/blink/renderer/core/css/property_registry.h" #include "third_party/blink/renderer/core/css/resolver/font_builder.h" @@ -1003,15 +1003,18 @@ if (!element) element = MakeGarbageCollected<SVGUnknownElement>(qname, *this); saw_elements_in_known_namespaces_ = true; - } else if (RuntimeEnabledFeatures::MathMLCoreEnabled() && - qname.NamespaceURI() == mathml_names::kNamespaceURI) { - element = MathMLElementFactory::Create(qname.LocalName(), *this, flags); - // An unknown MathML element is treated like an <mrow> element. - // TODO(crbug.com/1021837): Determine if we need to introduce a - // MathMLUnknownElement IDL. - if (!element) - element = MakeGarbageCollected<MathMLRowElement>(qname, *this); - saw_elements_in_known_namespaces_ = true; + } else if (qname.NamespaceURI() == mathml_names::kNamespaceURI) { + if (RuntimeEnabledFeatures::MathMLCoreEnabled()) { + element = MathMLElementFactory::Create(qname.LocalName(), *this, flags); + // An unknown MathML element is treated like an <mrow> element. + // TODO(crbug.com/1021837): Determine if we need to introduce a + // MathMLUnknownElement IDL. + if (!element) + element = MakeGarbageCollected<MathMLRowElement>(qname, *this); + saw_elements_in_known_namespaces_ = true; + } else { + element = MakeGarbageCollected<MathMLElement>(qname, *this); + } } else { element = MakeGarbageCollected<Element>(qname, this); } @@ -2029,7 +2032,7 @@ owner->GetDocument().UpdateStyleAndLayoutTree(parent_upgrade); } - CSSAnimationUpdateScope animation_update_scope(*this); + PostStyleUpdateScope post_style_update_scope(*this); // This call has to happen even if UpdateStyleAndLayout below will be called. // This is because the subsequent call to ShouldUpgrade may depend on the @@ -2142,21 +2145,6 @@ View()->MarkOrthogonalWritingModeRootsForLayout(); } - // TODO(crbug.com/1298921): If style is layout-dependent, we have to - // delay this until after layout. - if (focused_element_) { - bool focusable = false; - if (RuntimeEnabledFeatures::CSSContainerQueriesEnabled() && - GetStyleEngine().StyleMayRequireLayout()) { - const ComputedStyle* style = focused_element_->GetComputedStyle(); - focusable = style && style->IsFocusable(); - } else { - focusable = focused_element_->IsFocusable(); - } - if (!focusable) - ClearFocusedElementSoon(); - } - GetLayoutView()->ClearHitTestCache(); DCHECK(!document_animations_->NeedsAnimationTimingUpdate()); @@ -2585,9 +2573,11 @@ GetFrame()); } -void Document::ClearFocusedElementSoon() { - if (!clear_focused_element_timer_.IsActive()) +void Document::ClearFocusedElementIfNeeded() { + if (!clear_focused_element_timer_.IsActive() && focused_element_ && + !focused_element_->IsFocusable()) { clear_focused_element_timer_.StartOneShot(base::TimeDelta(), FROM_HERE); + } } void Document::ClearFocusedElementTimerFired(TimerBase*) {
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h index 45e3053..c9718f6 100644 --- a/third_party/blink/renderer/core/dom/document.h +++ b/third_party/blink/renderer/core/dom/document.h
@@ -949,6 +949,7 @@ bool SetFocusedElement(Element*, const FocusParams&); void ClearFocusedElement(); Element* FocusedElement() const { return focused_element_.Get(); } + void ClearFocusedElementIfNeeded(); UserActionElementSet& UserActionElements() { return user_action_elements_; } const UserActionElementSet& UserActionElements() const { return user_action_elements_; @@ -1971,7 +1972,6 @@ void DidAssociateFormControlsTimerFired(TimerBase*); - void ClearFocusedElementSoon(); void ClearFocusedElementTimerFired(TimerBase*); bool HaveScriptBlockingStylesheetsLoaded() const;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index 87de615..ca8d042ba 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2886,7 +2886,7 @@ } if (ElementAnimations* element_animations = GetElementAnimations()) { - // See also CSSAnimationUpdateScope. + // See also PostStyleUpdateScope. if (!RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled()) element_animations->CssAnimations().MaybeApplyPendingUpdate(this); }
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc index eab967e..a32ab682 100644 --- a/third_party/blink/renderer/core/dom/node.cc +++ b/third_party/blink/renderer/core/dom/node.cc
@@ -1696,22 +1696,6 @@ } } -// StyledElements allow inline style (style="border: 1px"), presentational -// attributes (ex. color), class names (ex. class="foo bar") and other non-basic -// styling features. They also control if this element can participate in style -// sharing. -// -// FIXME: The only things that ever go through StyleResolver that aren't -// StyledElements are PseudoElements and VTTElements. It's possible we can just -// eliminate all the checks since those elements will never have class names, -// inline style, or other things that this apparently guards against. -bool Node::IsStyledElement() const { - auto* this_element = DynamicTo<Element>(this); - return IsHTMLElement() || IsSVGElement() || IsMathMLElement() || - (!RuntimeEnabledFeatures::MathMLCoreEnabled() && this_element && - this_element->namespaceURI() == mathml_names::kNamespaceURI); -} - bool Node::IsActiveSlot() const { return ToHTMLSlotElementIfSupportsAssignmentOrNull(*this); }
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h index f961b121..eb26a46 100644 --- a/third_party/blink/renderer/core/dom/node.h +++ b/third_party/blink/renderer/core/dom/node.h
@@ -364,7 +364,20 @@ // either a MediaControlElement or MediaControls. bool HasMediaControlAncestor() const; - bool IsStyledElement() const; + // StyledElements allow inline style (style="border: 1px"), presentational + // attributes (ex. color), class names (ex. class="foo bar") and other + // non-basic styling features. They also control if this element can + // participate in style sharing. + // + // TODO(crbug.com/1305488): The only things that ever go through StyleResolver + // that aren't StyledElements are PseudoElements and VTTElements. It's + // possible we can just remove this function entirely, and replace it at the + // callsites with a DCHECK, since those elements will never have class + // names, inline style, or other things that this apparently guards + // against. + bool IsStyledElement() const { + return IsHTMLElement() || IsSVGElement() || IsMathMLElement(); + } bool IsDocumentNode() const; bool IsTreeScope() const;
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc index 45d45e2..7327187 100644 --- a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc +++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
@@ -774,12 +774,14 @@ text_node_to_trim, start_offset, downstream_end_.ComputeOffsetInContainerNode() - start_offset); } else { + RelocatablePosition relocatable_downstream_end(downstream_end_); RemoveChildrenInRange(start_node, start_offset, downstream_end_.ComputeEditingOffset(), editing_state); if (editing_state->IsAborted()) return; ending_position_ = upstream_start_; + downstream_end_ = relocatable_downstream_end.GetPosition(); } // We should update layout to associate |start_node| to layout object. GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc index 1bec958..7ea14ed2 100644 --- a/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc +++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc
@@ -149,4 +149,19 @@ EXPECT_EQ("|x", GetSelectionTextFromBody()); } +// This is a regression test for https://crbug.com/1307391 +TEST_F(DeleteSelectionCommandTest, FloatingInputsWithTrailingSpace) { + GetDocument().setDesignMode("on"); + InsertStyleElement("input { float: left; }"); + Selection().SetSelection(SetSelectionTextToBody("<input>^<input><input>| "), + SetSelectionOptions()); + + DeleteSelectionCommand& command = + *MakeGarbageCollected<DeleteSelectionCommand>( + GetDocument(), DeleteSelectionOptions::NormalDelete()); + // Should not crash. + EXPECT_TRUE(command.Apply()); + EXPECT_EQ("<input>| ", GetSelectionTextFromBody()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc index 21495e2..0373942 100644 --- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc +++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -9,6 +9,7 @@ #include "base/check.h" #include "base/check_op.h" #include "base/memory/scoped_refptr.h" +#include "base/metrics/histogram_functions.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/network/public/mojom/referrer_policy.mojom-blink.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -46,6 +47,15 @@ namespace { +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum class AttributionSrcRequestStatus { + kRequested = 0, + kReceived = 1, + kFailed = 2, + kMaxValue = kFailed, +}; + bool ContainsTriggerHeaders(const HTTPHeaderMap& headers) { return headers.Contains( http_names::kAttributionReportingRegisterEventTrigger) || @@ -56,6 +66,11 @@ http_names::kAttributionReportingRegisterAggregatableValues)); } +void RecordAttributionSrcRequestStatus(AttributionSrcRequestStatus status) { + base::UmaHistogramEnumeration("Conversions.AttributionSrcRequestStatus", + status); +} + } // namespace class AttributionSrcLoader::ResourceClient @@ -243,6 +258,9 @@ this, src_type, associated_with_navigation); resource_clients_.insert(client); RawResource::Fetch(params, local_frame_->DomWindow()->Fetcher(), client); + + RecordAttributionSrcRequestStatus(AttributionSrcRequestStatus::kRequested); + return client; } @@ -374,6 +392,12 @@ DCHECK(loader_->resource_clients_.Contains(this)); loader_->resource_clients_.erase(this); + + if (resource->ErrorOccurred()) { + RecordAttributionSrcRequestStatus(AttributionSrcRequestStatus::kFailed); + } else { + RecordAttributionSrcRequestStatus(AttributionSrcRequestStatus::kReceived); + } } void AttributionSrcLoader::ResourceClient::HandleResponseHeaders(
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader_test.cc b/third_party/blink/renderer/core/frame/attribution_src_loader_test.cc new file mode 100644 index 0000000..57de7413 --- /dev/null +++ b/third_party/blink/renderer/core/frame/attribution_src_loader_test.cc
@@ -0,0 +1,82 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/frame/attribution_src_loader.h" + +#include <memory> + +#include "base/test/metrics/histogram_tester.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/web_url_loader_mock_factory.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/execution_context/security_context.h" +#include "third_party/blink/renderer/core/frame/local_dom_window.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/testing/dummy_page_holder.h" +#include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" +#include "third_party/blink/renderer/platform/testing/url_test_helpers.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" +#include "third_party/blink/renderer/platform/weborigin/security_origin.h" + +namespace blink { + +namespace { + +using blink::url_test_helpers::RegisterMockedErrorURLLoad; +using blink::url_test_helpers::RegisterMockedURLLoad; +using blink::url_test_helpers::ToKURL; + +} // namespace + +class AttributionSrcLoaderTest : public testing::Test { + public: + void SetUp() override { + dummy_page_holder_ = std::make_unique<DummyPageHolder>(); + + SecurityContext& security_context = dummy_page_holder_->GetDocument() + .GetFrame() + ->DomWindow() + ->GetSecurityContext(); + security_context.SetSecurityOriginForTesting(nullptr); + security_context.SetSecurityOrigin( + SecurityOrigin::CreateFromString("https://example.com")); + + attribution_src_loader_ = MakeGarbageCollected<AttributionSrcLoader>( + dummy_page_holder_->GetDocument().GetFrame()); + } + + protected: + Persistent<AttributionSrcLoader> attribution_src_loader_; + std::unique_ptr<DummyPageHolder> dummy_page_holder_; +}; + +TEST_F(AttributionSrcLoaderTest, AttributionSrcRequestStatusHistogram) { + base::HistogramTester histograms; + + KURL url1 = ToKURL("https://example1.com/foo.html"); + RegisterMockedURLLoad(url1, test::CoreTestDataPath("foo.html")); + + attribution_src_loader_->Register(url1, /*element=*/nullptr); + + KURL url2 = ToKURL("https://example2.com/foo.html"); + RegisterMockedErrorURLLoad(url2); + + attribution_src_loader_->Register(url2, /*element=*/nullptr); + + // kRequested = 0. + histograms.ExpectUniqueSample("Conversions.AttributionSrcRequestStatus", 0, + 2); + + url_test_helpers::ServeAsynchronousRequests(); + + // kReceived = 1. + histograms.ExpectBucketCount("Conversions.AttributionSrcRequestStatus", 1, 1); + + // kFailed = 2. + histograms.ExpectBucketCount("Conversions.AttributionSrcRequestStatus", 2, 1); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/frame/deprecation/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation/deprecation.cc index 184a68a..81990b5 100644 --- a/third_party/blink/renderer/core/frame/deprecation/deprecation.cc +++ b/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
@@ -557,27 +557,13 @@ "RTCPeerConnectionSdpSemanticsPlanB", kM93, "Plan B SDP semantics, which is used when constructing an " "RTCPeerConnection with {sdpSemantics:\"plan-b\"}, is a legacy " - "version of the Session Description Protocol that has severe " - "compatibility issues on modern browsers. The standardized SDP " - "format, \"unified-plan\", has been used by default since M72 " - "(January, 2019). Dropping support for Plan B is targeted for M93, " - "but it's possible to register for a Deprecation Trial in order to " - "extend the Plan B deprecation deadline for a limited amount of " - "time.", + "non-standard version of the Session Description Protocol that has " + "been permanently deleted from the Web Platform. It is still " + "available when building with IS_FUCHSIA, but we intend to delete it " + "as soon as possible. Stop depending on it. See " + "https://crbug.com/1302249 for status.", "5823036655665152"); - case WebFeature::kRTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial: - return DeprecationInfo::WithDetails( - "RTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial", kM96, - "Plan B SDP semantics, which is used when constructing an " - "RTCPeerConnection with {sdpSemantics:\"plan-b\"}, is a legacy " - "version of the Session Description Protocol that has severe " - "compatibility issues on modern browsers. The standardized SDP " - "format, \"unified-plan\", has been used by default since M72 " - "(January, 2019). Dropping support for Plan B is targeted for M93, " - "but this page may extend the deadline until the End Date of the " - "'RTCPeerConnection Plan B SDP Semantics' deprecation trial."); - case WebFeature::kAddressSpacePublicNonSecureContextEmbeddedPrivate: case WebFeature::kAddressSpacePublicNonSecureContextEmbeddedLocal: case WebFeature::kAddressSpacePrivateNonSecureContextEmbeddedLocal:
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc index f0789f8..e77d640 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.cc +++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -51,9 +51,9 @@ #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_into_view_options.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" -#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h" #include "third_party/blink/renderer/core/animation/document_animations.h" #include "third_party/blink/renderer/core/css/font_face_set_document.h" +#include "third_party/blink/renderer/core/css/post_style_update_scope.h" #include "third_party/blink/renderer/core/css/style_change_reason.h" #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h" #include "third_party/blink/renderer/core/document_transition/document_transition_supplement.h" @@ -291,8 +291,7 @@ // it's not useful to generate mobile friendliness metrics for // devtools. // - GetFrame().Client()->IsLocalFrameClientImpl() && - GetFrame().Client()->IsLocalFrameClientImpl() + GetFrame().Client()->IsLocalFrameClientImpl() ? MakeGarbageCollected<MobileFriendlinessChecker>(*this) : nullptr) #if DCHECK_IS_ON() @@ -3249,7 +3248,7 @@ } bool LocalFrameView::UpdateStyleAndLayoutInternal() { - CSSAnimationUpdateScope animation_update_scope(*frame_->GetDocument()); + PostStyleUpdateScope post_style_update_scope(*frame_->GetDocument()); { frame_->GetDocument()->UpdateStyleAndLayoutTreeForThisDocument();
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc index 800eddc..bfa1cbff 100644 --- a/third_party/blink/renderer/core/html/html_element.cc +++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1080,6 +1080,65 @@ "'plaintext-only', or 'inherit'."); } +V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull* HTMLElement::hidden() const { + const AtomicString& attribute = FastGetAttribute(html_names::kHiddenAttr); + + if (!RuntimeEnabledFeatures::BeforeMatchEventEnabled(GetExecutionContext())) { + return MakeGarbageCollected< + V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull>(attribute != + g_null_atom); + } + + if (attribute == g_null_atom) { + return MakeGarbageCollected< + V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull>(false); + } + if (attribute == "until-found") { + return MakeGarbageCollected< + V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull>( + String("until-found")); + } + return MakeGarbageCollected<V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull>( + true); +} + +void HTMLElement::setHidden( + const V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull* value) { + switch (value->GetContentType()) { + case V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull::ContentType:: + kBoolean: + if (value->GetAsBoolean()) { + setAttribute(html_names::kHiddenAttr, ""); + } else { + removeAttribute(html_names::kHiddenAttr); + } + break; + case V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull::ContentType::kString: + if (RuntimeEnabledFeatures::BeforeMatchEventEnabled( + GetExecutionContext()) && + EqualIgnoringASCIICase(value->GetAsString(), "until-found")) { + setAttribute(html_names::kHiddenAttr, "until-found"); + } else if (value->GetAsString() == "") { + removeAttribute(html_names::kHiddenAttr); + } else { + setAttribute(html_names::kHiddenAttr, ""); + } + break; + case V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull::ContentType::kNull: + removeAttribute(html_names::kHiddenAttr); + break; + case V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull::ContentType:: + kUnrestrictedDouble: + double double_value = value->GetAsUnrestrictedDouble(); + if (double_value && !std::isnan(double_value)) { + setAttribute(html_names::kHiddenAttr, ""); + } else { + removeAttribute(html_names::kHiddenAttr); + } + break; + } +} + const AtomicString& HTMLElement::autocapitalize() const { DEFINE_STATIC_LOCAL(const AtomicString, kOff, ("off")); DEFINE_STATIC_LOCAL(const AtomicString, kNone, ("none"));
diff --git a/third_party/blink/renderer/core/html/html_element.h b/third_party/blink/renderer/core/html/html_element.h index 0daa4216..8d81cfd9 100644 --- a/third_party/blink/renderer/core/html/html_element.h +++ b/third_party/blink/renderer/core/html/html_element.h
@@ -24,6 +24,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_ELEMENT_H_ #include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_union_boolean_string_unrestricteddouble_null.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/events/simulated_click_options.h" @@ -173,6 +174,9 @@ void AdjustDirectionalityIfNeededAfterShadowRootChanged(); void BeginParsingChildren() override; + V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull* hidden() const; + void setHidden(const V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull*); + protected: enum AllowPercentage { kDontAllowPercentageValues, kAllowPercentageValues }; enum AllowZero { kDontAllowZeroValues, kAllowZeroValues };
diff --git a/third_party/blink/renderer/core/html/html_element.idl b/third_party/blink/renderer/core/html/html_element.idl index 243bb1f..8ae18ce 100644 --- a/third_party/blink/renderer/core/html/html_element.idl +++ b/third_party/blink/renderer/core/html/html_element.idl
@@ -30,7 +30,8 @@ [CEReactions] attribute DOMString dir; // user interaction - [CEReactions, Reflect] attribute boolean hidden; + [CEReactions] attribute (boolean or unrestricted double or DOMString?) hidden; + [RuntimeCallStatsCounter=HTMLElementClick] void click(); [CEReactions, RuntimeEnabled=InertAttribute, Reflect] attribute boolean inert; [CEReactions, Reflect] attribute DOMString accessKey;
diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css index 9f62d3a..dc34c8c 100644 --- a/third_party/blink/renderer/core/html/resources/html.css +++ b/third_party/blink/renderer/core/html/resources/html.css
@@ -664,7 +664,7 @@ border: 2px outset ButtonBorder; box-sizing: border-box; background-color: -internal-light-dark(#efefef, #3B3B3B); - color: -internal-light-dark(black, white); + color: ButtonText; } input[type="checkbox" i]:disabled,
diff --git a/third_party/blink/renderer/core/layout/hit_test_result.h b/third_party/blink/renderer/core/layout/hit_test_result.h index 3a610c6b..5105121 100644 --- a/third_party/blink/renderer/core/layout/hit_test_result.h +++ b/third_party/blink/renderer/core/layout/hit_test_result.h
@@ -191,6 +191,10 @@ void Append(const HitTestResult&); + bool HasListBasedResult() const { + return GetHitTestRequest().ListBased() && InnerNode(); + } + // If m_listBasedTestResult is 0 then set it to a new NodeSet. Return // *m_listBasedTestResult. Lazy allocation makes sense because the NodeSet is // seldom necessary, and it's somewhat expensive to allocate and initialize.
diff --git a/third_party/blink/renderer/core/layout/layout_theme.cc b/third_party/blink/renderer/core/layout/layout_theme.cc index 7e4f3253..694f2763 100644 --- a/third_party/blink/renderer/core/layout/layout_theme.cc +++ b/third_party/blink/renderer/core/layout/layout_theme.cc
@@ -624,7 +624,7 @@ case CSSValueID::kButtonshadow: return 0xFF888888; case CSSValueID::kButtontext: - return color_scheme == mojom::blink::ColorScheme::kDark ? 0xFFAAAAAA + return color_scheme == mojom::blink::ColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000; case CSSValueID::kCaptiontext: return color_scheme == mojom::blink::ColorScheme::kDark ? 0xFFFFFFFF
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h index e621bc79..59677476 100644 --- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h +++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
@@ -28,7 +28,6 @@ } HeapVector<NGFlexLine> flex_lines; - // |row_break_between| is only used in the case of row flex containers. Vector<EBreakBetween> row_break_between; LayoutUnit intrinsic_block_size; // |broke_before_row| is only used in the case of row flex containers. If this
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc index 6b5a281..3adfce4 100644 --- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -815,7 +815,7 @@ // break at that location. DCHECK(result->GetEarlyBreak()); return RelayoutAndBreakEarlier<NGFlexLayoutAlgorithm>( - *result->GetEarlyBreak()); + *result->GetEarlyBreak(), &column_early_breaks_); case NGLayoutResult::kNeedsRelayoutWithNoChildScrollbarChanges: return RelayoutIgnoringChildScrollbarChanges(); case NGLayoutResult::kDisableFragmentation: @@ -946,9 +946,11 @@ } // For rows, the break-before of the first row and the break-after of the - // last row are propagated to the container. - if (is_horizontal_flow_ && - ConstraintSpace().ShouldPropagateChildBreakValues()) { + // last row are propagated to the container. For columns, treat the set + // of columns as a single row and propagate the combined break-before rules + // for the first items in each column and break-after rules for last items in + // each column. + if (ConstraintSpace().ShouldPropagateChildBreakValues()) { DCHECK(!row_break_between_outputs.IsEmpty()); container_builder_.SetInitialBreakBefore(row_break_between_outputs.front()); container_builder_.SetPreviousBreakAfter(row_break_between_outputs.back()); @@ -1145,14 +1147,21 @@ } bool should_propagate_row_break_values = - is_horizontal_flow_ && ConstraintSpace().ShouldPropagateChildBreakValues(); if (should_propagate_row_break_values) { DCHECK(row_break_between_outputs); // The last row break between will store the final break-after to be // propagated to the container. - *row_break_between_outputs = Vector<EBreakBetween>( - flex_line_outputs->size() + 1, EBreakBetween::kAuto); + if (is_horizontal_flow_) { + *row_break_between_outputs = Vector<EBreakBetween>( + flex_line_outputs->size() + 1, EBreakBetween::kAuto); + } else { + // For flex columns, we only need to store two values - one for + // the break-before value of all combined columns, and the second for + // for the break-after values for all combined columns. + *row_break_between_outputs = + Vector<EBreakBetween>(2, EBreakBetween::kAuto); + } } absl::optional<LayoutUnit> fallback_baseline; @@ -1185,12 +1194,6 @@ flex_item.has_descendant_that_depends_on_percentage_block_size = layout_result->HasDescendantThatDependsOnPercentageBlockSize(); - // The break-before and break-after values of flex items in a flex row are - // propagated to the row itself. Accumulate the BreakBetween values for - // each row ahead of time so that they can be stored on the break token - // for future use. - // - // https://drafts.csswg.org/css-flexbox-1/#pagination if (should_propagate_row_break_values) { const auto& item_style = flex_item.Style(); auto item_break_before = JoinFragmentainerBreakValues( @@ -1198,13 +1201,34 @@ auto item_break_after = JoinFragmentainerBreakValues( item_style.BreakAfter(), layout_result->FinalBreakAfter()); - (*row_break_between_outputs)[flex_line_idx] = - JoinFragmentainerBreakValues( - (*row_break_between_outputs)[flex_line_idx], item_break_before); - (*row_break_between_outputs)[flex_line_idx + 1] = - JoinFragmentainerBreakValues( - (*row_break_between_outputs)[flex_line_idx + 1], - item_break_after); + // The break-before and break-after values of flex items in a flex row + // are propagated to the row itself. Accumulate the BreakBetween values + // for each row ahead of time so that they can be stored on the break + // token for future use. + // + // https://drafts.csswg.org/css-flexbox-1/#pagination + if (is_horizontal_flow_) { + (*row_break_between_outputs)[flex_line_idx] = + JoinFragmentainerBreakValues( + (*row_break_between_outputs)[flex_line_idx], + item_break_before); + (*row_break_between_outputs)[flex_line_idx + 1] = + JoinFragmentainerBreakValues( + (*row_break_between_outputs)[flex_line_idx + 1], + item_break_after); + } else { + // Treat all columns as a "row" of columns, and accumulate the initial + // and final break values for all columns, which will be propagated to + // the container. + if (flex_item_idx == 0) { + (*row_break_between_outputs)[0] = JoinFragmentainerBreakValues( + (*row_break_between_outputs)[0], item_break_before); + } + if (flex_item_idx == line_output.line_items.size() - 1) { + (*row_break_between_outputs)[1] = JoinFragmentainerBreakValues( + (*row_break_between_outputs)[1], item_break_after); + } + } } const auto& physical_fragment = @@ -1267,6 +1291,13 @@ is_horizontal_flow_); Vector<bool> has_inflow_child_break_inside_line(flex_line_outputs->size(), false); + bool needs_earlier_break_in_column = false; + + HeapVector<NGFlexColumnBreakInfo> column_break_info; + if (!is_horizontal_flow_) { + column_break_info = + HeapVector<NGFlexColumnBreakInfo>(flex_line_outputs->size()); + } for (auto entry = item_iterator.NextItem(*broke_before_row); NGFlexItem* flex_item = entry.flex_item; @@ -1285,6 +1316,16 @@ has_inflow_child_break_inside_line[flex_line_idx - 1]) break; } else { + // If we are relaying out as a result of an early break, and we have early + // breaks for more than one column, they will be stored in + // |additional_early_breaks_|. Keep |early_break_| consistent with that of + // the current column. + if (additional_early_breaks_ && + flex_line_idx < additional_early_breaks_->size()) + early_break_ = (*additional_early_breaks_)[flex_line_idx]; + else if (early_break_ && flex_line_idx != 0) + early_break_ = nullptr; + if (has_inflow_child_break_inside_line[flex_line_idx]) { if (!last_item_in_line) item_iterator.NextLine(); @@ -1348,10 +1389,17 @@ *broke_before_row = true; ConsumeRemainingFragmentainerSpace(previously_consumed_block_size, &line_output); + // For column flex containers, continue to the next column. For rows, + // continue until we've processed all items in the current row. has_inflow_child_break_inside_line[flex_line_idx] = true; - if (!last_item_in_line && !is_horizontal_flow_) - item_iterator.NextLine(); - return NGLayoutResult::kSuccess; + if (!is_horizontal_flow_) { + if (!last_item_in_line) + item_iterator.NextLine(); + } else if (last_item_in_line) { + return NGLayoutResult::kSuccess; + } + last_line_idx_to_process_first_child_ = flex_line_idx; + continue; } else { early_break_in_child = EnterEarlyBreakInChild(flex_item->ng_input_node, *early_break_); @@ -1375,6 +1423,7 @@ child_space, item_break_token, early_break_in_child); NGBreakStatus break_status = NGBreakStatus::kContinue; + NGFlexColumnBreakInfo* current_column_break_info = nullptr; if (!early_break_ && ConstraintSpace().HasBlockFragmentation()) { bool has_container_separation = false; if (is_horizontal_flow_) { @@ -1403,13 +1452,42 @@ } } else { has_container_separation = - last_line_idx_to_process_first_child_ == flex_line_idx || + (last_line_idx_to_process_first_child_ != kNotFound && + last_line_idx_to_process_first_child_ >= flex_line_idx) || (!item_break_token && offset.block_offset > LayoutUnit()); + + // We may switch back and forth between columns, so we need to make sure + // to use the break-after for the current column. + if (flex_line_outputs->size() > 1) { + current_column_break_info = &column_break_info[flex_line_idx]; + container_builder_.SetPreviousBreakAfter( + current_column_break_info->break_after); + } } break_status = BreakBeforeChildIfNeeded( ConstraintSpace(), flex_item->ng_input_node, *layout_result, ConstraintSpace().FragmentainerOffsetAtBfc() + offset.block_offset, - has_container_separation, &container_builder_, is_horizontal_flow_); + has_container_separation, &container_builder_, is_horizontal_flow_, + current_column_break_info); + } + + if (break_status == NGBreakStatus::kNeedsEarlierBreak) { + if (current_column_break_info) { + DCHECK(!is_horizontal_flow_); + DCHECK(current_column_break_info->early_break); + if (!needs_earlier_break_in_column) { + needs_earlier_break_in_column = true; + container_builder_.SetEarlyBreak( + current_column_break_info->early_break); + } + // Keep track of the early breaks for each column. + AddColumnEarlyBreak(current_column_break_info->early_break, + flex_line_idx); + if (!last_item_in_line) + item_iterator.NextLine(); + continue; + } + return NGLayoutResult::kNeedsEarlierBreak; } if (break_status == NGBreakStatus::kBrokeBefore) { @@ -1427,9 +1505,6 @@ last_line_idx_to_process_first_child_ = flex_line_idx; continue; } - if (break_status == NGBreakStatus::kNeedsEarlierBreak) { - return NGLayoutResult::kNeedsEarlierBreak; - } const auto& physical_fragment = To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment()); @@ -1461,13 +1536,25 @@ } } + if (!is_horizontal_flow_) { + line_output.line_intrinsic_block_size = + line_output.line_intrinsic_block_size.ClampIndefiniteToZero(); + line_output.line_intrinsic_block_size += + offset.block_offset + fragment.BlockSize(); + } + // TODO(almaher): What to do in the case where the line extends past // the last item? Should that be included when fragmenting? intrinsic_block_size_ += (offset.block_offset + fragment.BlockSize() - intrinsic_block_size_) .ClampNegativeToZero(); - container_builder_.AddResult(*layout_result, offset); + container_builder_.AddResult(*layout_result, offset, + /* relative_offset */ absl::nullopt, + /* inline_container */ nullptr, + current_column_break_info + ? ¤t_column_break_info->break_after + : nullptr); // Only propagate baselines from children on the first flex-line. if (&line_output == flex_line_outputs->begin()) { @@ -1499,6 +1586,9 @@ last_line_idx_to_process_first_child_ = flex_line_idx; } + if (needs_earlier_break_in_column) + return NGLayoutResult::kNeedsEarlierBreak; + if (!container_builder_.HasInflowChildBreakInside() && !item_iterator.NextItem(*broke_before_row).flex_item) { container_builder_.SetHasSeenAllChildren(); @@ -1988,8 +2078,15 @@ // This will be further adjusted by the total consumed block size once we // handle the break before in the next fragmentainer. This ensures that the // expansion is properly handled in the column balancing pass. + LayoutUnit intrinsic_block_size = intrinsic_block_size_; + if (flex_line->line_intrinsic_block_size != kIndefiniteSize) { + DCHECK(!is_horizontal_flow_); + intrinsic_block_size = (flex_line->line_intrinsic_block_size - + previously_consumed_block_size) + .ClampNegativeToZero(); + } flex_line->item_offset_adjustment -= - intrinsic_block_size_ + previously_consumed_block_size; + intrinsic_block_size + previously_consumed_block_size; } if (!ConstraintSpace().HasKnownFragmentainerBlockSize()) @@ -2096,6 +2193,14 @@ return true; } +void NGFlexLayoutAlgorithm::AddColumnEarlyBreak(NGEarlyBreak* breakpoint, + wtf_size_t index) { + DCHECK(!is_horizontal_flow_); + while (column_early_breaks_.size() <= index) + column_early_breaks_.push_back(nullptr); + column_early_breaks_[index] = breakpoint; +} + #if DCHECK_IS_ON() void NGFlexLayoutAlgorithm::CheckFlexLines( const HeapVector<NGFlexLine>& flex_line_outputs) const {
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h index d421a30..205c294 100644 --- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h +++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
@@ -150,6 +150,9 @@ LayoutUnit row_block_size, wtf_size_t row_index); + // Add an early break for the column at the provided |index|. + void AddColumnEarlyBreak(NGEarlyBreak* breakpoint, wtf_size_t index); + #if DCHECK_IS_ON() void CheckFlexLines(const HeapVector<NGFlexLine>& flex_line_outputs) const; #endif @@ -184,6 +187,11 @@ // fragmenting, |total_intrinsic_block_size| and |intrinsic_block_size_| will // be equivalent. LayoutUnit total_intrinsic_block_size_; + + // Only one early break is supported per container. However, we may need to + // return to an early break within multiple flex columns. This stores the + // early breaks per column to be used when aborting layout. + HeapVector<Member<NGEarlyBreak>> column_early_breaks_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h index b4840861..5fc0923 100644 --- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h +++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
@@ -43,6 +43,8 @@ LayoutUnit line_cross_size; LayoutUnit cross_axis_offset; LayoutUnit item_offset_adjustment; + // This is only used for columns during fragmentation. + LayoutUnit line_intrinsic_block_size = kIndefiniteSize; bool has_seen_all_children = false; HeapVector<NGFlexItem> line_items; };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc index c83e3e0..88f1fe0 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -85,7 +85,8 @@ const NGLayoutResult& child_layout_result, const LogicalOffset offset, absl::optional<LogicalOffset> relative_offset, - const NGInlineContainer<LogicalOffset>* inline_container) { + const NGInlineContainer<LogicalOffset>* inline_container, + EBreakBetween* flex_column_break_after) { const auto& fragment = child_layout_result.PhysicalFragment(); // We'll normally propagate info from child_layout_result here, but if that's @@ -129,7 +130,7 @@ PropagateBreakInfo(*result_for_propagation, offset); if (UNLIKELY(ConstraintSpace() && ConstraintSpace()->ShouldPropagateChildBreakValues())) - PropagateChildBreakValues(*result_for_propagation); + PropagateChildBreakValues(*result_for_propagation, flex_column_break_after); } void NGBoxFragmentBuilder::AddChild( @@ -444,7 +445,8 @@ } void NGBoxFragmentBuilder::PropagateChildBreakValues( - const NGLayoutResult& child_layout_result) { + const NGLayoutResult& child_layout_result, + EBreakBetween* flex_column_break_after) { if (child_layout_result.Status() != NGLayoutResult::kSuccess) return; @@ -474,6 +476,8 @@ EBreakBetween break_after = JoinFragmentainerBreakValues( child_layout_result.FinalBreakAfter(), child_style.BreakAfter()); SetPreviousBreakAfter(break_after); + if (flex_column_break_after) + *flex_column_break_after = break_after; } const NGLayoutResult* NGBoxFragmentBuilder::ToBoxFragment(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h index 0c93eb4..1a3fd79 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h +++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -197,12 +197,15 @@ // computed ahead of time. If so, a |relative_offset| will be passed // in. Otherwise, the relative offset will be calculated as normal. // |inline_container| is passed when adding an OOF that is contained by a - // non-atomic inline. + // non-atomic inline. If |flex_column_break_after| is supplied, we are running + // layout for a column flex container, in which case, we need to update the + // break-after value for the column itself. void AddResult( const NGLayoutResult&, const LogicalOffset, absl::optional<LogicalOffset> relative_offset = absl::nullopt, - const NGInlineContainer<LogicalOffset>* inline_container = nullptr); + const NGInlineContainer<LogicalOffset>* inline_container = nullptr, + EBreakBetween* flex_column_break_after = nullptr); // Add a child fragment and propagate info from it. Called by AddResult(). // Other callers should call AddResult() instead of this when possible, since @@ -669,7 +672,10 @@ } // Propagate the break-before/break-after of the child (if applicable). - void PropagateChildBreakValues(const NGLayoutResult& child_layout_result); + // Update the break-after value for |flex_column_break_after|, if supplied. + void PropagateChildBreakValues( + const NGLayoutResult& child_layout_result, + EBreakBetween* flex_column_break_after = nullptr); private: // Propagate fragmentation details. This includes checking whether we have
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc index 13149c3f..3222e7a 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
@@ -593,13 +593,15 @@ return NGBreakStatus::kContinue; } -NGBreakStatus BreakBeforeChildIfNeeded(const NGConstraintSpace& space, - NGLayoutInputNode child, - const NGLayoutResult& layout_result, - LayoutUnit fragmentainer_block_offset, - bool has_container_separation, - NGBoxFragmentBuilder* builder, - bool is_row_item) { +NGBreakStatus BreakBeforeChildIfNeeded( + const NGConstraintSpace& space, + NGLayoutInputNode child, + const NGLayoutResult& layout_result, + LayoutUnit fragmentainer_block_offset, + bool has_container_separation, + NGBoxFragmentBuilder* builder, + bool is_row_item, + NGFlexColumnBreakInfo* flex_column_break_info) { DCHECK(space.HasBlockFragmentation()); // Break-before and break-after are handled at the row level. @@ -608,8 +610,11 @@ CalculateBreakBetweenValue(child, layout_result, *builder); if (IsForcedBreakValue(space, break_between)) { BreakBeforeChild(space, child, &layout_result, fragmentainer_block_offset, - kBreakAppealPerfect, /* is_forced_break */ true, - builder); + kBreakAppealPerfect, /* is_forced_break */ true, builder, + /* block_size_override */ absl::nullopt, + flex_column_break_info + ? &flex_column_break_info->break_after + : nullptr); return NGBreakStatus::kBrokeBefore; } } @@ -622,14 +627,16 @@ // the appeal of breaking there, even if we didn't. if (MovePastBreakpoint(space, child, layout_result, fragmentainer_block_offset, appeal_before, builder, - is_row_item)) + is_row_item, flex_column_break_info)) return NGBreakStatus::kContinue; // Breaking inside the child isn't appealing, and we're out of space. Figure // out where to insert a soft break. It will either be before this child, or // before an earlier sibling, if there's a more appealing breakpoint there. if (!AttemptSoftBreak(space, child, &layout_result, - fragmentainer_block_offset, appeal_before, builder)) + fragmentainer_block_offset, appeal_before, builder, + /* block_size_override */ absl::nullopt, + flex_column_break_info)) return NGBreakStatus::kNeedsEarlierBreak; return NGBreakStatus::kBrokeBefore; @@ -642,7 +649,8 @@ absl::optional<NGBreakAppeal> appeal, bool is_forced_break, NGBoxFragmentBuilder* builder, - absl::optional<LayoutUnit> block_size_override) { + absl::optional<LayoutUnit> block_size_override, + EBreakBetween* flex_column_break_after) { #if DCHECK_IS_ON() DCHECK(layout_result || block_size_override); if (layout_result && layout_result->Status() == NGLayoutResult::kSuccess) { @@ -668,7 +676,7 @@ if (layout_result && space.ShouldPropagateChildBreakValues() && !is_forced_break) - builder->PropagateChildBreakValues(*layout_result); + builder->PropagateChildBreakValues(*layout_result, flex_column_break_after); // We'll drop the fragment (if any) on the floor and retry at the start of the // next fragmentainer. @@ -723,7 +731,8 @@ LayoutUnit fragmentainer_block_offset, NGBreakAppeal appeal_before, NGBoxFragmentBuilder* builder, - bool is_row_item) { + bool is_row_item, + NGFlexColumnBreakInfo* flex_column_break_info) { if (layout_result.Status() != NGLayoutResult::kSuccess) { // Layout aborted - no fragment was produced. There's nothing to move // past. We need to break before. @@ -819,10 +828,17 @@ // breaking before is impossible, break inside regardless of appeal. if (refuse_break_before) return true; - if (appeal_inside >= appeal_before && - (!builder || !builder->HasEarlyBreak() || - appeal_inside >= builder->EarlyBreak().BreakAppeal())) - return true; + if (appeal_inside >= appeal_before) { + if (flex_column_break_info && + (!flex_column_break_info->early_break || + appeal_inside >= + flex_column_break_info->early_break->BreakAppeal())) { + return true; + } else if (!builder || !builder->HasEarlyBreak() || + appeal_inside >= builder->EarlyBreak().BreakAppeal()) { + return true; + } + } } else { bool move_past = refuse_break_before; if (!move_past) { @@ -849,7 +865,8 @@ // orphans and widows, if at all possible. We also only do this for // non-row items since items in a row will be parallel to one another.) UpdateEarlyBreakAtBlockChild(space, To<NGBlockNode>(child), - layout_result, appeal_before, builder); + layout_result, appeal_before, builder, + flex_column_break_info); } return true; } @@ -859,11 +876,13 @@ return false; } -void UpdateEarlyBreakAtBlockChild(const NGConstraintSpace& space, - NGBlockNode child, - const NGLayoutResult& layout_result, - NGBreakAppeal appeal_before, - NGBoxFragmentBuilder* builder) { +void UpdateEarlyBreakAtBlockChild( + const NGConstraintSpace& space, + NGBlockNode child, + const NGLayoutResult& layout_result, + NGBreakAppeal appeal_before, + NGBoxFragmentBuilder* builder, + NGFlexColumnBreakInfo* flex_column_break_info) { // If the child already broke, it's a little too late to look for breakpoints. DCHECK(!layout_result.PhysicalFragment().BreakToken()); @@ -872,8 +891,19 @@ if (const NGEarlyBreak* breakpoint = layout_result.GetEarlyBreak()) { appeal_inside = CalculateBreakAppealInside(space, layout_result, breakpoint->BreakAppeal()); - if (!builder->HasEarlyBreak() || - builder->EarlyBreak().BreakAppeal() <= breakpoint->BreakAppeal()) { + if (flex_column_break_info) { + if (!flex_column_break_info->early_break || + flex_column_break_info->early_break->BreakAppeal() <= + breakpoint->BreakAppeal()) { + // Found a good breakpoint inside the child. Add the child to the early + // break chain for the current column. + auto* parent_break = MakeGarbageCollected<NGEarlyBreak>( + child, appeal_inside, breakpoint); + flex_column_break_info->early_break = parent_break; + } + } else if (!builder->HasEarlyBreak() || + builder->EarlyBreak().BreakAppeal() <= + breakpoint->BreakAppeal()) { // Found a good breakpoint inside the child. Add the child to the early // break container chain, and store it. auto* parent_break = @@ -889,6 +919,15 @@ if (appeal_before <= appeal_inside) return; + if (flex_column_break_info) { + if (flex_column_break_info->early_break && + flex_column_break_info->early_break->BreakAppeal() > appeal_before) + return; + flex_column_break_info->early_break = + MakeGarbageCollected<NGEarlyBreak>(child, appeal_before); + return; + } + if (builder->HasEarlyBreak() && builder->EarlyBreak().BreakAppeal() > appeal_before) return; @@ -903,12 +942,21 @@ LayoutUnit fragmentainer_block_offset, NGBreakAppeal appeal_before, NGBoxFragmentBuilder* builder, - absl::optional<LayoutUnit> block_size_override) { + absl::optional<LayoutUnit> block_size_override, + NGFlexColumnBreakInfo* flex_column_break_info) { DCHECK(layout_result || block_size_override); // If there's a breakpoint with higher appeal among earlier siblings, we need // to abort and re-layout to that breakpoint. - if (builder->HasEarlyBreak() && - builder->EarlyBreak().BreakAppeal() > appeal_before) { + bool found_earlier_break = false; + if (flex_column_break_info) { + found_earlier_break = + flex_column_break_info->early_break && + flex_column_break_info->early_break->BreakAppeal() > appeal_before; + } else { + found_earlier_break = builder->HasEarlyBreak() && + builder->EarlyBreak().BreakAppeal() > appeal_before; + } + if (found_earlier_break) { // Found a better place to break. Before aborting, calculate and report // space shortage from where we'd actually break. PropagateSpaceShortage(space, layout_result, fragmentainer_block_offset, @@ -919,9 +967,10 @@ // Break before the child. Note that there may be a better break further up // with higher appeal (but it's too early to tell), in which case this // breakpoint will be replaced. - BreakBeforeChild(space, child, layout_result, fragmentainer_block_offset, - appeal_before, /* is_forced_break */ false, builder, - block_size_override); + BreakBeforeChild( + space, child, layout_result, fragmentainer_block_offset, appeal_before, + /* is_forced_break */ false, builder, block_size_override, + flex_column_break_info ? &flex_column_break_info->break_after : nullptr); return true; }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h index b49e4e9..aae9d6b 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h +++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
@@ -22,6 +22,19 @@ class NGEarlyBreak; class NGLayoutResult; +// Each column in a flex container is fragmented independently, so we need to +// track early break and break-after info for each column separately. +struct NGFlexColumnBreakInfo { + DISALLOW_NEW(); + + NGFlexColumnBreakInfo() = default; + + void Trace(Visitor* visitor) const { visitor->Trace(early_break); } + + Member<NGEarlyBreak> early_break = nullptr; + EBreakBetween break_after = EBreakBetween::kAuto; +}; + // Join two adjacent break values specified on break-before and/or break- // after. avoid* values win over auto values, and forced break values win over // avoid* values. |first_value| is specified on an element earlier in the flow @@ -268,17 +281,24 @@ // class C breakpoints. Those occur if there's a positive gap between the // block-start content edge of the container and the block-start margin edge of // the first in-flow child. https://www.w3.org/TR/css-break-3/#possible-breaks -NGBreakStatus BreakBeforeChildIfNeeded(const NGConstraintSpace&, - NGLayoutInputNode child, - const NGLayoutResult&, - LayoutUnit fragmentainer_block_offset, - bool has_container_separation, - NGBoxFragmentBuilder*, - bool is_row_item = false); +// If |flex_column_break_info| is supplied, we are running layout for a column +// flex container, in which case, we may be tracking certain break behavior at +// the column level. +NGBreakStatus BreakBeforeChildIfNeeded( + const NGConstraintSpace&, + NGLayoutInputNode child, + const NGLayoutResult&, + LayoutUnit fragmentainer_block_offset, + bool has_container_separation, + NGBoxFragmentBuilder*, + bool is_row_item = false, + NGFlexColumnBreakInfo* flex_column_break_info = nullptr); // Insert a break before the child, and propagate space shortage if needed. // |block_size_override| should only be supplied when you wish to propagate a -// different block-size than that of the provided layout result. +// different block-size than that of the provided layout result. If +// |flex_column_break_after| is supplied, then we need to update the +// break-after value for the column, as well. void BreakBeforeChild( const NGConstraintSpace&, NGLayoutInputNode child, @@ -287,7 +307,8 @@ absl::optional<NGBreakAppeal> appeal, bool is_forced_break, NGBoxFragmentBuilder*, - absl::optional<LayoutUnit> block_size_override = absl::nullopt); + absl::optional<LayoutUnit> block_size_override = absl::nullopt, + EBreakBetween* flex_column_break_after = nullptr); // Propagate the block-size of unbreakable content. This is used to inflate the // initial minimal column block-size when balancing columns, before we calculate @@ -321,29 +342,36 @@ // Move past the breakpoint before the child, if possible, and return true. Also // update the appeal of breaking before or inside the child (if we're not going // to break before it). If false is returned, it means that we need to break -// before the child (or even earlier). -bool MovePastBreakpoint(const NGConstraintSpace& space, - NGLayoutInputNode child, - const NGLayoutResult& layout_result, - LayoutUnit fragmentainer_block_offset, - NGBreakAppeal appeal_before, - NGBoxFragmentBuilder* builder, - bool is_row_item = false); +// before the child (or even earlier). See BreakBeforeChildIfNeeded() for +// details on |flex_column_break_info|. +bool MovePastBreakpoint( + const NGConstraintSpace& space, + NGLayoutInputNode child, + const NGLayoutResult& layout_result, + LayoutUnit fragmentainer_block_offset, + NGBreakAppeal appeal_before, + NGBoxFragmentBuilder* builder, + bool is_row_item = false, + NGFlexColumnBreakInfo* flex_column_break_info = nullptr); // If the appeal of breaking before or inside the child is the same or higher // than any previous breakpoint we've found, set a new breakpoint in the -// builder, and update appeal accordingly. -void UpdateEarlyBreakAtBlockChild(const NGConstraintSpace&, - NGBlockNode child, - const NGLayoutResult&, - NGBreakAppeal appeal_before, - NGBoxFragmentBuilder*); +// builder, and update appeal accordingly. See BreakBeforeChildIfNeeded() for +// details on |flex_column_break_info|. +void UpdateEarlyBreakAtBlockChild( + const NGConstraintSpace&, + NGBlockNode child, + const NGLayoutResult&, + NGBreakAppeal appeal_before, + NGBoxFragmentBuilder*, + NGFlexColumnBreakInfo* flex_column_break_info = nullptr); // Attempt to insert a soft break before the child, and return true if we did. // If false is returned, it means that the desired breakpoint is earlier in the // container, and that we need to abort and re-layout to that breakpoint. // |block_size_override| should only be supplied when you wish to propagate a -// different block-size than that of the provided layout result. +// different block-size than that of the provided layout result. See +// BreakBeforeChildIfNeeded() for details on |flex_column_break_info|. bool AttemptSoftBreak( const NGConstraintSpace&, NGLayoutInputNode child, @@ -351,7 +379,8 @@ LayoutUnit fragmentainer_block_offset, NGBreakAppeal appeal_before, NGBoxFragmentBuilder*, - absl::optional<LayoutUnit> block_size_override = absl::nullopt); + absl::optional<LayoutUnit> block_size_override = absl::nullopt, + NGFlexColumnBreakInfo* flex_column_break_info = nullptr); // If we have an previously found break point, and we're entering an ancestor of // the node we're going to break before, return the early break inside. This can @@ -431,4 +460,6 @@ } // namespace blink +WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::NGFlexColumnBreakInfo) + #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENTATION_UTILS_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h index 4adffc7..7af8c85 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h
@@ -40,16 +40,19 @@ STACK_ALLOCATED(); public: - NGLayoutAlgorithmParams(NGBlockNode node, - const NGFragmentGeometry& fragment_geometry, - const NGConstraintSpace& space, - const NGBlockBreakToken* break_token = nullptr, - const NGEarlyBreak* early_break = nullptr) + NGLayoutAlgorithmParams( + NGBlockNode node, + const NGFragmentGeometry& fragment_geometry, + const NGConstraintSpace& space, + const NGBlockBreakToken* break_token = nullptr, + const NGEarlyBreak* early_break = nullptr, + const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks = nullptr) : node(node), fragment_geometry(fragment_geometry), space(space), break_token(break_token), - early_break(early_break) {} + early_break(early_break), + additional_early_breaks(additional_early_breaks) {} NGBlockNode node; const NGFragmentGeometry& fragment_geometry; @@ -57,6 +60,7 @@ const NGBlockBreakToken* break_token; const NGEarlyBreak* early_break; const NGLayoutResult* previous_result = nullptr; + const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks; }; // Base class for all LayoutNG algorithms. @@ -88,7 +92,8 @@ params.node, ¶ms.node.Style(), ¶ms.space, - {params.space.GetWritingMode(), params.space.Direction()}) { + {params.space.GetWritingMode(), params.space.Direction()}), + additional_early_breaks_(params.additional_early_breaks) { container_builder_.SetIsNewFormattingContext( params.space.IsNewFormattingContext()); container_builder_.SetInitialFragmentGeometry(params.fragment_geometry); @@ -138,13 +143,16 @@ // such as orphans, widows, break-before:avoid or break-after:avoid. template <typename Algorithm> const NGLayoutResult* RelayoutAndBreakEarlier( - const NGEarlyBreak& breakpoint) { + const NGEarlyBreak& breakpoint, + const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks = + nullptr) { // Not allowed to recurse! DCHECK(!early_break_); + DCHECK(!additional_early_breaks_ || additional_early_breaks_->IsEmpty()); NGLayoutAlgorithmParams params( Node(), container_builder_.InitialFragmentGeometry(), ConstraintSpace(), - BreakToken(), &breakpoint); + BreakToken(), &breakpoint, additional_early_breaks); Algorithm algorithm_with_break(params); auto& new_builder = algorithm_with_break.container_builder_; new_builder.SetBoxType(container_builder_.BoxType()); @@ -191,6 +199,11 @@ const NGBreakTokenType* break_token_; NGBoxFragmentBuilderType container_builder_; + + // There are cases where we may need more than one early break per fragment. + // For example, there may be an early break within multiple flex columns. This + // can be used to pass additional early breaks to the next layout pass. + const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc index 0899a90c..fdd51ac 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -240,27 +240,33 @@ column_locations->resize(column_constraints.data.size()); if (column_locations->IsEmpty()) return; + bool is_first_non_collpased_column = true; LayoutUnit column_offset = inline_border_spacing; for (wtf_size_t i = 0; i < column_constraints.data.size(); ++i) { auto& column_location = (*column_locations)[i]; auto& column_constraint = column_constraints.data[i]; - *has_collapsed_columns = - *has_collapsed_columns || column_constraint.is_collapsed; - column_location.offset = column_offset; + *has_collapsed_columns |= column_constraint.is_collapsed; if (column_constraints.data[i].is_mergeable && (column_sizes[i] == kIndefiniteSize || column_sizes[i] == LayoutUnit())) { // Empty mergeable columns are treated as collapsed. + column_location.offset = column_offset; column_location.size = LayoutUnit(); column_location.is_collapsed = true; } else if (shrink_collapsed && column_constraint.is_collapsed) { - column_location.is_collapsed = true; + column_location.offset = column_offset; column_location.size = LayoutUnit(); + column_location.is_collapsed = true; } else { - column_location.is_collapsed = false; + if (is_first_non_collpased_column) + is_first_non_collpased_column = false; + else + column_offset += inline_border_spacing; + column_location.offset = column_offset; column_location.size = column_sizes[i] != kIndefiniteSize ? column_sizes[i] : LayoutUnit(); - column_offset += column_location.size + inline_border_spacing; + column_location.is_collapsed = false; + column_offset += column_location.size; } } } @@ -1068,7 +1074,7 @@ grid_converter.ToPhysical(table_grid_rect), border_spacing, column_block_size); - if (Node().GetDOMNode() && + if (RuntimeEnabledFeatures::MathMLCoreEnabled() && Node().GetDOMNode() && Node().GetDOMNode()->HasTagName(mathml_names::kMtableTag)) table_baseline = MathTableBaseline(Style(), child_block_offset); if (table_baseline)
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc index 90887f1..b37e75d 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -171,37 +171,6 @@ row.Style().GetWritingDirection(); const bool has_collapsed_borders = table_borders.IsCollapsed(); - auto CreateCellConstraintSpace = [&column_locations, &table_writing_direction, - &is_table_block_size_specified, - &has_collapsed_borders, - &cell_percentage_inline_size]( - const NGBlockNode& cell, - wtf_size_t start_column_index, - const NGBoxStrut& cell_borders) { - const wtf_size_t start_column = start_column_index; - DCHECK_LT(start_column, column_locations.size()); - const wtf_size_t end_column = - std::min(start_column + cell.TableCellColspan() - 1, - column_locations.size() - 1); - const LayoutUnit cell_inline_size = column_locations[end_column].offset + - column_locations[end_column].size - - column_locations[start_column].offset; - - // Typically we want these values to match the "layout" pass as close as - // possible. The one exception is "is_hidden_for_paint". This is set to - // true if a cell should be hidden within a collapsed column. If this is - // the case, the size is almost certainly different causing a second layout. - return NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder( - table_writing_direction, cell, cell_borders, - {cell_inline_size, kIndefiniteSize}, cell_percentage_inline_size, - /* alignment_baseline */ absl::nullopt, start_column, - /* is_initial_block_size_indefinite */ true, - is_table_block_size_specified, - /* is_hidden_for_paint */ false, has_collapsed_borders, - NGCacheSlot::kMeasure) - .ToConstraintSpace(); - }; - // TODO(layout-ng) Scrollbars should be frozen when computing row sizes. // This cannot be done today, because fragments with frozen scrollbars // will be cached. Needs to be fixed in NG framework. @@ -221,9 +190,20 @@ const NGBoxStrut cell_borders = table_borders.CellBorder( cell, row_index, colspan_cell_tabulator->CurrentColumn(), section_index, table_writing_direction); - const NGConstraintSpace cell_constraint_space = CreateCellConstraintSpace( - cell, colspan_cell_tabulator->CurrentColumn(), cell_borders); - const NGLayoutResult* layout_result = cell.Layout(cell_constraint_space); + + // We want these values to match the "layout" pass as close as possible. + const auto cell_space = + NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder( + table_writing_direction, cell, cell_borders, column_locations, + /* cell_block_size */ kIndefiniteSize, cell_percentage_inline_size, + /* alignment_baseline */ absl::nullopt, + colspan_cell_tabulator->CurrentColumn(), + /* is_initial_block_size_indefinite */ true, + is_table_block_size_specified, has_collapsed_borders, + NGCacheSlot::kMeasure) + .ToConstraintSpace(); + const NGLayoutResult* layout_result = cell.Layout(cell_space); + const NGBoxFragment fragment( table_writing_direction, To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment())); @@ -255,8 +235,7 @@ cell_css_percent = cell_specified_block_length.Percent(); } else if (cell_specified_block_length.IsFixed()) { // NOTE: Ignore min/max-height for determining the |cell_css_block_size|. - NGBoxStrut cell_padding = - ComputePadding(cell_constraint_space, cell_style); + NGBoxStrut cell_padding = ComputePadding(cell_space, cell_style); NGBoxStrut border_padding = cell_borders + cell_padding; // https://quirks.spec.whatwg.org/#the-table-cell-height-box-sizing-quirk if (cell.GetDocument().InQuirksMode() || @@ -455,17 +434,32 @@ const WritingDirectionMode table_writing_direction, const NGBlockNode cell, const NGBoxStrut& cell_borders, - LogicalSize cell_size, + const Vector<NGTableColumnLocation>& column_locations, + LayoutUnit cell_block_size, LayoutUnit percentage_inline_size, absl::optional<LayoutUnit> alignment_baseline, - wtf_size_t column_index, + wtf_size_t start_column, bool is_initial_block_size_indefinite, bool is_table_block_size_specified, - bool is_hidden_for_paint, bool has_collapsed_borders, NGCacheSlot cache_slot) { const auto& cell_style = cell.Style(); const auto table_writing_mode = table_writing_direction.GetWritingMode(); + const wtf_size_t end_column = std::min( + start_column + cell.TableCellColspan() - 1, column_locations.size() - 1); + const LayoutUnit cell_inline_size = column_locations[end_column].offset + + column_locations[end_column].size - + column_locations[start_column].offset; + + // A table-cell is hidden if all the columns it spans are collapsed. + const bool is_hidden_for_paint = [&]() -> bool { + for (wtf_size_t column = start_column; column <= end_column; ++column) { + if (!column_locations[column].is_collapsed) + return false; + } + return true; + }(); + NGConstraintSpaceBuilder builder(table_writing_mode, cell_style.GetWritingDirection(), /* is_new_fc */ true); @@ -478,21 +472,20 @@ : icb_size.width); } - builder.SetAvailableSize(cell_size); + builder.SetAvailableSize({cell_inline_size, cell_block_size}); builder.SetIsFixedInlineSize(true); - if (cell_size.block_size != kIndefiniteSize) + if (cell_block_size != kIndefiniteSize) builder.SetIsFixedBlockSize(true); builder.SetIsInitialBlockSizeIndefinite(is_initial_block_size_indefinite); - // Standard: - // https://www.w3.org/TR/css-tables-3/#computing-the-table-height "the - // computed height (if definite, percentages being considered 0px)" + // https://www.w3.org/TR/css-tables-3/#computing-the-table-height + // "the computed height (if definite, percentages being considered 0px)" builder.SetPercentageResolutionSize( {percentage_inline_size, kIndefiniteSize}); builder.SetTableCellBorders(cell_borders); builder.SetTableCellAlignmentBaseline(alignment_baseline); - builder.SetTableCellColumnIndex(column_index); + builder.SetTableCellColumnIndex(start_column); builder.SetIsRestrictedBlockSizeTableCell( is_table_block_size_specified || cell_style.LogicalHeight().IsFixed()); builder.SetIsTableCellHiddenForPaint(is_hidden_for_paint);
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h index d09a7ee3..0337dc7c 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
@@ -16,7 +16,6 @@ class NGConstraintSpaceBuilder; class NGTableBorders; enum class NGCacheSlot; -struct LogicalSize; // Table size distribution algorithms. class NGTableAlgorithmUtils { @@ -38,13 +37,13 @@ const WritingDirectionMode table_writing_direction, const NGBlockNode cell, const NGBoxStrut& cell_borders, - LogicalSize cell_size, + const Vector<NGTableColumnLocation>& column_locations, + LayoutUnit cell_block_size, LayoutUnit percentage_inline_size, absl::optional<LayoutUnit> alignment_baseline, - wtf_size_t column_index, + wtf_size_t start_column, bool is_initial_block_size_indefinite, bool is_restricted_block_size_table, - bool is_hidden_for_paint, bool has_collapsed_borders, NGCacheSlot);
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc index 2567d68..bc6d162 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
@@ -29,40 +29,7 @@ absl::optional<LayoutUnit> row_baseline, LayoutUnit* cell_inline_offset = nullptr, bool use_block_fragmentation = false) { - const wtf_size_t start_column = table_data.cells[cell_index].start_column; - const wtf_size_t end_column = - std::min(start_column + cell.TableCellColspan() - 1, - table_data.column_locations.size() - 1); - - // When columns spanned by the cell are collapsed, the cell geometry is - // defined by: - // - The start edge of the first non-collapsed column. - // - The end edge of the last non-collapsed column. - // - If all columns are collapsed, the |cell_inline_size| is defined by the - // edges of the last column. Picking last column is arbitrary, any - // spanned column would work, as all spanned columns define the same - // geometry: same location, zero width. - wtf_size_t cell_location_start_column = start_column; - while ( - table_data.column_locations[cell_location_start_column].is_collapsed && - cell_location_start_column < end_column) - cell_location_start_column++; - wtf_size_t cell_location_end_column = end_column; - while (table_data.column_locations[cell_location_end_column].is_collapsed && - cell_location_end_column > cell_location_start_column) - cell_location_end_column--; - - if (cell_inline_offset) { - *cell_inline_offset = - table_data.column_locations[cell_location_start_column].offset; - } - - const NGTableConstraintSpaceData::Cell& cell_data = - table_data.cells[cell_index]; - const LayoutUnit cell_inline_size = - table_data.column_locations[cell_location_end_column].offset + - table_data.column_locations[cell_location_end_column].size - - table_data.column_locations[cell_location_start_column].offset; + const auto& cell_data = table_data.cells[cell_index]; const LayoutUnit cell_block_size = cell_data.rowspan_block_size != kIndefiniteSize ? cell_data.rowspan_block_size @@ -74,17 +41,17 @@ cell_data.is_constrained || (cell_data.has_grown && table_data.is_table_block_size_specified); - const bool is_hidden_for_paint = - table_data.column_locations[cell_location_start_column].is_collapsed && - cell_location_start_column == cell_location_end_column; + const wtf_size_t start_column = cell_data.start_column; + if (cell_inline_offset) + *cell_inline_offset = table_data.column_locations[start_column].offset; NGConstraintSpaceBuilder builder = NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder( table_data.table_writing_direction, cell, cell_data.borders, - {cell_inline_size, cell_block_size}, + table_data.column_locations, cell_block_size, container_builder_.InlineSize(), row_baseline, start_column, !is_initial_block_size_definite, - table_data.is_table_block_size_specified, is_hidden_for_paint, + table_data.is_table_block_size_specified, table_data.has_collapsed_borders, NGCacheSlot::kLayout); if (use_block_fragmentation) {
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc index 4e9296a..35d0d465 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -700,8 +700,9 @@ // WebFrameLoadType::kBackForward. DCHECK(frame_load_type != WebFrameLoadType::kBackForward); return kSPANavTypeHistoryPushStateOrReplaceState; - case mojom::blink::SameDocumentNavigationType::kAppHistoryTransitionWhile: - return kSPANavTypeAppHistoryTransitionWhile; + case mojom::blink::SameDocumentNavigationType:: + kNavigationApiTransitionWhile: + return kSPANavTypeNavigationApiTransitionWhile; } NOTREACHED(); return kSPANavTypeSameDocumentBackwardOrForward; @@ -818,9 +819,9 @@ // NavigateEvent, the navigation will finish asynchronously, so // don't immediately call DidStopLoading() in that case. bool should_send_stop_notification = - !was_loading && - same_document_navigation_type != - mojom::blink::SameDocumentNavigationType::kAppHistoryTransitionWhile; + !was_loading && same_document_navigation_type != + mojom::blink::SameDocumentNavigationType:: + kNavigationApiTransitionWhile; if (should_send_stop_notification) GetFrameLoader().Progress().ProgressCompleted();
diff --git a/third_party/blink/renderer/core/loader/frame_loader_types.h b/third_party/blink/renderer/core/loader/frame_loader_types.h index 0f62d75..0f05ece 100644 --- a/third_party/blink/renderer/core/loader/frame_loader_types.h +++ b/third_party/blink/renderer/core/loader/frame_loader_types.h
@@ -53,7 +53,7 @@ kSPANavTypeHistoryPushStateOrReplaceState = 0, kSPANavTypeSameDocumentBackwardOrForward = 1, kSPANavTypeOtherFragmentNavigation = 2, - kSPANavTypeAppHistoryTransitionWhile = 3, + kSPANavTypeNavigationApiTransitionWhile = 3, kSPANavTypeCount };
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.cc b/third_party/blink/renderer/core/mathml/mathml_element.cc index 4f011986..bed7025 100644 --- a/third_party/blink/renderer/core/mathml/mathml_element.cc +++ b/third_party/blink/renderer/core/mathml/mathml_element.cc
@@ -38,6 +38,9 @@ } bool MathMLElement::IsPresentationAttribute(const QualifiedName& name) const { + if (!RuntimeEnabledFeatures::MathMLCoreEnabled()) + return Element::IsPresentationAttribute(name); + if (name == html_names::kDirAttr || name == mathml_names::kMathsizeAttr || name == mathml_names::kMathcolorAttr || name == mathml_names::kMathbackgroundAttr || @@ -76,6 +79,11 @@ const QualifiedName& name, const AtomicString& value, MutableCSSPropertyValueSet* style) { + if (!RuntimeEnabledFeatures::MathMLCoreEnabled()) { + Element::CollectStyleForPresentationAttribute(name, value, style); + return; + } + if (name == html_names::kDirAttr) { if (IsValidDirAttribute(value)) { AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kDirection, @@ -138,6 +146,11 @@ } void MathMLElement::ParseAttribute(const AttributeModificationParams& param) { + if (!RuntimeEnabledFeatures::MathMLCoreEnabled()) { + Element::ParseAttribute(param); + return; + } + const AtomicString& event_name = HTMLElement::EventNameForAttributeName(param.name); if (!event_name.IsNull()) {
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.h b/third_party/blink/renderer/core/mathml/mathml_element.h index b927336..8c88493 100644 --- a/third_party/blink/renderer/core/mathml/mathml_element.h +++ b/third_party/blink/renderer/core/mathml/mathml_element.h
@@ -32,6 +32,8 @@ bool IsMathMLElement() const = delete; // This will catch anyone doing an unnecessary check. + bool IsStyledElement() const = + delete; // This will catch anyone doing an unnecessary check. virtual bool IsGroupingElement() const { return false; }
diff --git a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc index 440103b6..7a6503d 100644 --- a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc +++ b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc
@@ -39,16 +39,14 @@ // prevent the user from scaling the page as if user-scalable=no was set. static constexpr double kMaximumScalePreventsZoomingThreshold = 1.2; -// Finding bad tap targets may takes too time for big page and should abort if -// it takes more than 5ms. +// Finding bad tap targets may costs too long time for big page and should abort +// if it takes more than 5ms. static constexpr base::TimeDelta kTimeBudgetForBadTapTarget = base::Milliseconds(5); // Extracting tap targets phase is the major part of finding bad tap targets. +// This phase will abort when it consumes more than 4ms. static constexpr base::TimeDelta kTimeBudgetForTapTargetExtraction = base::Milliseconds(4); -// Checking clock itself is heavy on excessive call, skip checking by this -// stride. -constexpr int kTimeBudgetCheckStride = 32; static constexpr base::TimeDelta kEvaluationDelay = base::Milliseconds(3000); static constexpr base::TimeDelta kEvaluationInterval = base::Minutes(1); @@ -77,8 +75,8 @@ namespace { -bool IsTimeBudgetExpired(const base::Time& from) { - return base::Time::Now() - from > kTimeBudgetForBadTapTarget; +bool IsTimeBudgetExpired(const base::TimeTicks& from) { + return base::TimeTicks::Now() - from > kTimeBudgetForBadTapTarget; } // Fenwick tree is a data structure which can efficiently update elements and @@ -219,21 +217,18 @@ const LocalFrameView& frame_view, int finger_radius, Vector<int>& x_positions, - const base::Time& started, + const base::TimeTicks& started, Vector<std::pair<int, EdgeOrCenter>>& vertices) { LayoutObject* const root = frame_view.GetFrame().GetDocument()->GetLayoutView(); WTF::HashSet<Member<const LayoutObject>> tap_targets; - int object_count = 0; // Simultaneously iterate front-to-back and back-to-front to consider // both page headers and footers using the same time budget. for (const LayoutObject *forward = root, *backward = root; forward && backward;) { - if ((++object_count % kTimeBudgetCheckStride) == 0 && - base::Time::Now() - started > kTimeBudgetForTapTargetExtraction) { + if (base::TimeTicks::Now() - started > kTimeBudgetForTapTargetExtraction) return static_cast<int>(tap_targets.size()); - } blink::GetRootNodeOptions options; if (forward->GetNode() != nullptr && @@ -309,7 +304,7 @@ // Returns kTimeBudgetExceeded if time limit exceeded. int CountBadTapTargets(wtf_size_t rightmost_position, const Vector<std::pair<int, EdgeOrCenter>>& vertices, - const base::Time& started) { + const base::TimeTicks& started) { FenwickTree tree(rightmost_position); int bad_tap_targets = 0; for (const auto& it : vertices) { @@ -348,7 +343,7 @@ // go/bad-tap-target-ukm int MobileFriendlinessChecker::ComputeBadTapTargetsRatio() { DCHECK(frame_view_->GetFrame().IsLocalRoot()); - base::Time started = base::Time::Now(); + base::TimeTicks started = base::TimeTicks::Now(); constexpr float kOneDipInMm = 0.15875; double initial_scale = frame_view_->GetPage() ->GetPageScaleConstraintsSet() @@ -381,7 +376,7 @@ all_tap_targets += got_tap_targets; - if (base::Time::Now() - started > kTimeBudgetForTapTargetExtraction) + if (base::TimeTicks::Now() - started > kTimeBudgetForTapTargetExtraction) break; } if (all_tap_targets == 0)
diff --git a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc index 0ff8fa1..72bd024 100644 --- a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc +++ b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc
@@ -4,7 +4,7 @@ #include "third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.h" -#include "base/time/time_override.h" +#include "base/time/time.h" #include "third_party/blink/public/common/mobile_metrics/mobile_friendliness.h" #include "third_party/blink/public/mojom/mobile_metrics/mobile_friendliness.mojom-shared.h" #include "third_party/blink/public/web/web_settings.h" @@ -44,11 +44,40 @@ "png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4/" "/8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; +class ScopedTimeTicksOverride { + public: + explicit ScopedTimeTicksOverride(bool fixed) { + if (fixed) { + time_clock_overrides_ = + std::make_unique<base::subtle::ScopedTimeClockOverrides>( + nullptr, &ScopedTimeTicksOverride::FixedTicks, nullptr); + } else { + time_clock_overrides_ = + std::make_unique<base::subtle::ScopedTimeClockOverrides>( + nullptr, &ScopedTimeTicksOverride::BoostedTicks, nullptr); + } + } + + static base::TimeTicks FixedTicks() { + static base::TimeTicks now = base::subtle::TimeTicksNowIgnoringOverride(); + return now; + } + static base::TimeTicks BoostedTicks() { + static base::TimeTicks now = base::subtle::TimeTicksNowIgnoringOverride(); + now += base::Microseconds(100); + return now; + } + + private: + std::unique_ptr<base::subtle::ScopedTimeClockOverrides> time_clock_overrides_; +}; + class MobileFriendlinessCheckerTest : public testing::Test { static void EvalMobileFriendliness(LocalFrameView* view, - int scroll_y_offset) { + int scroll_y_offset, + bool fixed_clock) { DCHECK(view->GetFrame().IsLocalRoot()); - + ScopedTimeTicksOverride clock(fixed_clock); for (const Frame* frame = &view->GetFrame(); frame; frame = frame->Tree().TraverseNext()) { if (const auto* local_frame = DynamicTo<LocalFrame>(frame)) { @@ -98,7 +127,8 @@ MobileFriendliness CalculateMetricsForHTMLString(const std::string& html, float device_scale = 1.0, - int scroll_y_offset = 0) { + int scroll_y_offset = 0, + bool fixed_clock = true) { MFTestWebFrameClient web_frame_client; { std::unique_ptr<frame_test_helpers::WebViewHelper> helper( @@ -108,14 +138,15 @@ url_test_helpers::ToKURL("about:blank")); EvalMobileFriendliness( helper->GetWebView()->MainFrameImpl()->GetFrameView(), - scroll_y_offset); + scroll_y_offset, fixed_clock); } return web_frame_client.GetMobileFriendliness(); } MobileFriendliness CalculateMetricsForFile(const std::string& path, float device_scale = 1.0, - int scroll_y_offset = 0) { + int scroll_y_offset = 0, + bool fixed_clock = true) { MFTestWebFrameClient web_frame_client; { std::unique_ptr<frame_test_helpers::WebViewHelper> helper( @@ -127,30 +158,12 @@ kBaseUrl + path); EvalMobileFriendliness( helper->GetWebView()->MainFrameImpl()->GetFrameView(), - scroll_y_offset); + scroll_y_offset, fixed_clock); } return web_frame_client.GetMobileFriendliness(); } }; -class ClockFixedMobileFriendlinessCheckerTest - : public MobileFriendlinessCheckerTest { - public: - void SetUp() override { - clock_override_ = std::make_unique<base::subtle::ScopedTimeClockOverrides>( - []() { - // Returns fixed mock time to avoid BadTapTargetRatio hits - // timeout. - static base::Time start = base::subtle::TimeNowIgnoringOverride(); - return start; - }, - nullptr, nullptr); - } - - protected: - std::unique_ptr<base::subtle::ScopedTimeClockOverrides> clock_override_; -}; - TEST_F(MobileFriendlinessCheckerTest, NoViewportSetting) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString("<body>bar</body>"); @@ -230,7 +243,7 @@ EXPECT_EQ(actual_mf.small_text_ratio, 100); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, NoText) { +TEST_F(MobileFriendlinessCheckerTest, NoText) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(<body></body>)"); EXPECT_EQ(actual_mf.viewport_device_width, false); @@ -857,7 +870,7 @@ EXPECT_EQ(actual_mf.text_content_outside_viewport_percentage, 0.0); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, SingleTapTarget) { +TEST_F(MobileFriendlinessCheckerTest, SingleTapTarget) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -873,7 +886,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, TwoImageTapTargetsClose) { +TEST_F(MobileFriendlinessCheckerTest, TwoImageTapTargetsClose) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString( base::StringPrintf(R"( <html> @@ -891,7 +904,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, TwoImageTapTargetsFar) { +TEST_F(MobileFriendlinessCheckerTest, TwoImageTapTargetsFar) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString( base::StringPrintf(R"( <html> @@ -910,7 +923,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, NoBadTapTarget) { +TEST_F(MobileFriendlinessCheckerTest, NoBadTapTarget) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -929,8 +942,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, - NoBadTapTargetWithDeviceScaleFactor) { +TEST_F(MobileFriendlinessCheckerTest, NoBadTapTargetWithDeviceScaleFactor) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> @@ -951,8 +963,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, - BadTapTargetWithDeviceScaleFactor) { +TEST_F(MobileFriendlinessCheckerTest, BadTapTargetWithDeviceScaleFactor) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> @@ -973,7 +984,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, BadTapTargetWithAutoZoomOut) { +TEST_F(MobileFriendlinessCheckerTest, BadTapTargetWithAutoZoomOut) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <body style="font-size: 18px"> @@ -990,7 +1001,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, TooCloseTapTargetsVertical) { +TEST_F(MobileFriendlinessCheckerTest, TooCloseTapTargetsVertical) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1013,8 +1024,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 50); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, - TooCloseTapTargetsVerticalEventListener) { +TEST_F(MobileFriendlinessCheckerTest, TooCloseTapTargetsVerticalEventListener) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1034,8 +1044,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 50); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, - TooCloseTapTargetsVerticalSamePoint) { +TEST_F(MobileFriendlinessCheckerTest, TooCloseTapTargetsVerticalSamePoint) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1063,7 +1072,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 34); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, TooCloseTapTargetsHorizontal) { +TEST_F(MobileFriendlinessCheckerTest, TooCloseTapTargetsHorizontal) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1086,8 +1095,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 50); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, - TooCloseTapTargetsHorizontalSamePoint) { +TEST_F(MobileFriendlinessCheckerTest, TooCloseTapTargetsHorizontalSamePoint) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1115,7 +1123,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 34); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, GridGoodTargets3X3) { +TEST_F(MobileFriendlinessCheckerTest, GridGoodTargets3X3) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1179,7 +1187,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, GridBadTargets3X3) { +TEST_F(MobileFriendlinessCheckerTest, GridBadTargets3X3) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1243,7 +1251,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, FormTapTargets) { +TEST_F(MobileFriendlinessCheckerTest, FormTapTargets) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1260,8 +1268,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 50); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, - InvisibleTapTargetWillBeIgnored) { +TEST_F(MobileFriendlinessCheckerTest, InvisibleTapTargetWillBeIgnored) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1280,8 +1287,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, - BadTapTargetWithPositionAbsolute) { +TEST_F(MobileFriendlinessCheckerTest, BadTapTargetWithPositionAbsolute) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1300,17 +1306,9 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, TapTargetTimeout) { - clock_override_.reset(); - clock_override_ = std::make_unique<base::subtle::ScopedTimeClockOverrides>( - []() { - // Time::Now() progress 1 ms stride for every check to force timeout. - static base::Time now = base::subtle::TimeNowIgnoringOverride(); - now += base::Milliseconds(1); - return now; - }, - nullptr, nullptr); - MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( +TEST_F(MobileFriendlinessCheckerTest, TapTargetTimeout) { + MobileFriendliness actual_mf = + CalculateMetricsForHTMLString(R"( <html> <head> <meta name="viewport" content="width=480, initial-scale=1"> @@ -1336,11 +1334,14 @@ </button> </body> </html> -)"); +)", + /*device_scale=*/1.0, + /*scroll_y_offset=*/0, + /*fixed_clock=*/false); EXPECT_EQ(actual_mf.bad_tap_targets_ratio, -2); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, TapTargetPositionFixed) { +TEST_F(MobileFriendlinessCheckerTest, TapTargetPositionFixed) { MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"( <html> <head> @@ -1355,7 +1356,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, IFrameTest) { +TEST_F(MobileFriendlinessCheckerTest, IFrameTest) { url_test_helpers::RegisterMockedURLLoadFromBase( WebString::FromUTF8(kBaseUrl), blink::test::CoreTestDataPath(), WebString::FromUTF8("visible_iframe.html")); @@ -1366,7 +1367,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, IFrameVieportDeviceWidth) { +TEST_F(MobileFriendlinessCheckerTest, IFrameVieportDeviceWidth) { url_test_helpers::RegisterMockedURLLoadFromBase( WebString::FromUTF8(kBaseUrl), blink::test::CoreTestDataPath(), WebString::FromUTF8("viewport/viewport-1.html")); @@ -1378,7 +1379,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, IFrameSmallTextRatio) { +TEST_F(MobileFriendlinessCheckerTest, IFrameSmallTextRatio) { url_test_helpers::RegisterMockedURLLoadFromBase( WebString::FromUTF8(kBaseUrl), blink::test::CoreTestDataPath(), WebString::FromUTF8("small_text_iframe.html")); @@ -1390,7 +1391,7 @@ EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0); } -TEST_F(ClockFixedMobileFriendlinessCheckerTest, IFrameBadTapTargetsRatio) { +TEST_F(MobileFriendlinessCheckerTest, IFrameBadTapTargetsRatio) { url_test_helpers::RegisterMockedURLLoadFromBase( WebString::FromUTF8(kBaseUrl), blink::test::CoreTestDataPath(), WebString::FromUTF8("bad_tap_targets_iframe.html"));
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.cc b/third_party/blink/renderer/core/navigation_api/navigation_api.cc index f00b509..d6050ca 100644 --- a/third_party/blink/renderer/core/navigation_api/navigation_api.cc +++ b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
@@ -470,7 +470,7 @@ if (options->hasState()) { ExceptionState exception_state( script_state->GetIsolate(), - ExceptionContext::Context::kOperationInvoke, "AppHistory", + ExceptionContext::Context::kOperationInvoke, "Navigation", "navigate"); serialized_state = SerializeState(options->state(), exception_state); if (exception_state.HadException()) { @@ -501,7 +501,7 @@ if (options->hasState()) { ExceptionState exception_state( script_state->GetIsolate(), - ExceptionContext::Context::kOperationInvoke, "AppHistory", "reload"); + ExceptionContext::Context::kOperationInvoke, "Navigation", "reload"); serialized_state = SerializeState(options->state(), exception_state); if (exception_state.HadException()) { NavigationResult* result = @@ -559,7 +559,7 @@ const String& key, NavigationOptions* options) { if (DOMException* maybe_ex = - PerformSharedNavigationChecks("goTo()/back()/forward()")) { + PerformSharedNavigationChecks("traverseTo()/back()/forward()")) { return EarlyErrorResult(script_state, maybe_ex); } @@ -778,7 +778,7 @@ // steps. Instead it does stuff like the loading spinner and use counters. GetSupplementable()->document()->Loader()->RunURLAndHistoryUpdateSteps( url, destination_item, - mojom::blink::SameDocumentNavigationType::kAppHistoryTransitionWhile, + mojom::blink::SameDocumentNavigationType::kNavigationApiTransitionWhile, state_object, type, is_browser_initiated, is_synchronously_committed); }
diff --git a/third_party/blink/renderer/core/page/focusgroup_controller_test.cc b/third_party/blink/renderer/core/page/focusgroup_controller_test.cc index 2d1a7a1c..d5771768 100644 --- a/third_party/blink/renderer/core/page/focusgroup_controller_test.cc +++ b/third_party/blink/renderer/core/page/focusgroup_controller_test.cc
@@ -42,53 +42,6 @@ event); } - void AssertForwardDoesntMoveFocusWhenOutOfFocusgroup(int key); - void AssertForwardDoesntMoveFocusWhenOnFocusgroupRoot(int key); - void AssertForwardDoesntMoveWhenOnNonFocusgroupItem(int key); - void AssertForwardMovesToNextItem(int key); - void AssertForwardDoesntMoveWhenOnlyOneItem(int key); - void AssertForwardDoesntMoveWhenOnlyOneItemAndWraps(int key); - void AssertForwardSkipsNonFocusableItems(int key); - void AssertForwardMovesInExtendingFocusgroup(int key); - void AssertForwardExitsExtendingFocusgroup(int key); - void AssertForwardMovesToNextElementWithinDescendants(int key); - void AssertForwardDoesntMoveFocusWhenAxisNotSupported(int key); - void AssertForwardMovesFocusWhenInArrowAxisOnlyFocusgroup(int key); - void AssertForwardSkipsExtendingFocusgroup(int key); - void AssertForwardDoesntWrapWhenNotSupported(int key); - void AssertForwardDoesntWrapEvenWhenOtherAxisSupported(int key); - void AssertForwardDoesntWrapInFocusgroupWithoutItems(int key); - void AssertForwardWrapsSuccessfully(int key); - void AssertForwardWrapsToParentFocusgroup(int key); - void AssertForwardWrapsInInnerFocusgroupOnly(int key); - void AssertForwardWrapsInExpectedScope(int key); - void AssertForwardWrapsAndGoesInInnerFocusgroup(int key); - void AssertForwardWrapsAndSkipsOrthogonalInnerFocusgroup(int key); - - void AssertBackwardDoesntMoveFocusWhenOutOfFocusgroup(int key); - void AssertBackwardDoesntMoveFocusWhenOnFocusgroupRoot(int key); - void AssertBackwardDoesntMoveWhenOnNonFocusgroupItem(int key); - void AssertBackwardMovesFocusToPreviousItem(int key); - void AssertBackwardSkipsNonFocusableItems(int key); - void AssertBackwardDoesntMoveWhenOnlyOneItem(int key); - void AssertBackwardDoesntMoveWhenOnlyOneItemAndWraps(int key); - void AssertBackwardDoesntMoveFocusAxisNotSupported(int key); - void AssertBackwardMovesFocusWhenInArrowAxisOnlyFocusgroup(int key); - void AssertBackwardDescendIntoExtendingFocusgroup(int key); - void AssertBackwardSkipsNonFocusgroupSubtree(int key); - void AssertBackwardSkipsOrthogonalFocusgroup(int key); - void AssertBackwardSkipsRootFocusgroup(int key); - void AssertBackwardSkipsEmptyWrappingFocusgroup(int key); - void AssertBackwardSkipsRootFocusgroupComplexCase(int key); - void AssertBackwardSkipsOrthogonalFocusgroupComplexCase(int key); - void AssertBackwardAscendsToParentFocusgroup(int key); - void AssertBackwardDoesntWrapWhenNotSupported(int key); - void AssertBackwardWrapsSuccessfully(int key); - void AssertBackwardWrapsSuccessfullyInAxis(int key); - void AssertBackwardDoesntWrapInOrthogonalAxis(int key); - void AssertBackwardWrapsSuccessfullyInExtendingFocusgroup(int key); - void AssertBackwardWrapsSuccessfullyInComplexCase(int key); - private: void SetUp() override { PageTestBase::SetUp(gfx::Size()); } @@ -671,11 +624,20 @@ // FORWARD NAVIGATION - DOWN ARROW & RIGHT ARROW // ***************************************************************************** +class FocusgroupControllerForwardNavigationTest + : public FocusgroupControllerTest, + public ::testing::WithParamInterface<int> {}; + +INSTANTIATE_TEST_SUITE_P(, + FocusgroupControllerForwardNavigationTest, + testing::Values(ui::DomKey::ARROW_DOWN, + ui::DomKey::ARROW_RIGHT)); + // When the focus is set on an element outside of the focusgroup, an arrow key // press shouldn't move the focus at all. -void FocusgroupControllerTest::AssertForwardDoesntMoveFocusWhenOutOfFocusgroup( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, + DoesntMoveFocusWhenOutOfFocusgroup) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <span id=out tabindex=-1></span> <div focusgroup> @@ -695,19 +657,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), out); } -TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveFocusWhenOutOfFocusgroup) { - AssertForwardDoesntMoveFocusWhenOutOfFocusgroup(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightDoesntMoveFocusWhenOutOfFocusgroup) { - AssertForwardDoesntMoveFocusWhenOutOfFocusgroup(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the root of a focusgroup element, an arrow key press // shouldn't move the focus at all. -void FocusgroupControllerTest::AssertForwardDoesntMoveFocusWhenOnFocusgroupRoot( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, + DoesntMoveFocusWhenOnFocusgroupRoot) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root tabindex=-1 focusgroup> <span id=item1 tabindex=0></span> @@ -726,20 +680,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), root); } -TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveFocusWhenOnFocusgroupRoot) { - AssertForwardDoesntMoveFocusWhenOnFocusgroupRoot(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, - ArrowRightDoesntMoveFocusWhenOnFocusgroupRoot) { - AssertForwardDoesntMoveFocusWhenOnFocusgroupRoot(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on a focusable element that isn't a focusgroup item, an // arrow key press shouldn't move the focus at all. -void FocusgroupControllerTest::AssertForwardDoesntMoveWhenOnNonFocusgroupItem( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, + DoesntMoveWhenOnNonFocusgroupItem) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div tabindex=-1 focusgroup> <div> @@ -761,18 +706,10 @@ ASSERT_EQ(GetDocument().FocusedElement(), nonitem1); } -TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveWhenOnNonFocusgroupItem) { - AssertForwardDoesntMoveWhenOnNonFocusgroupItem(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightDoesntMoveWhenOnNonFocusgroupItem) { - AssertForwardDoesntMoveWhenOnNonFocusgroupItem(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on a focusgroup item, an arrow key press should move // the focus to the next item. -void FocusgroupControllerTest::AssertForwardMovesToNextItem(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, MovesToNextItem) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup> <span id=item1 tabindex=0></span> @@ -793,17 +730,10 @@ ASSERT_EQ(GetDocument().FocusedElement(), item2); } -TEST_F(FocusgroupControllerTest, ArrowDownMovesToNextItem) { - AssertForwardMovesToNextItem(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightMovesToNextItem) { - AssertForwardMovesToNextItem(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the only focusgroup item, the focus shouldn't move // and we shouldn't get stuck in an infinite loop. -void FocusgroupControllerTest::AssertForwardDoesntMoveWhenOnlyOneItem(int key) { +TEST_P(FocusgroupControllerForwardNavigationTest, DoesntMoveWhenOnlyOneItem) { + int key = GetParam(); ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup> @@ -822,20 +752,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveWhenOnlyOneItem) { - AssertForwardDoesntMoveWhenOnlyOneItem(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightDoesntMoveWhenOnlyOneItem) { - AssertForwardDoesntMoveWhenOnlyOneItem(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the only focusgroup item and the focusgroup wraps in // the axis of the arrow key pressed, the focus shouldn't move and we shouldn't // get stuck in an infinite loop. -void FocusgroupControllerTest::AssertForwardDoesntMoveWhenOnlyOneItemAndWraps( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, + DoesntMoveWhenOnlyOneItemAndWraps) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup=wrap> <span id=item1 tabindex=0></span> @@ -853,18 +775,10 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveWhenOnlyOneItemAndWraps) { - AssertForwardDoesntMoveWhenOnlyOneItemAndWraps(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightDoesntMoveWhenOnlyOneItemAndWraps) { - AssertForwardDoesntMoveWhenOnlyOneItemAndWraps(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on a focusgroup item, an arrow key press should move // the focus to the next item and skip non-focusable items. -void FocusgroupControllerTest::AssertForwardSkipsNonFocusableItems(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, SkipsNonFocusableItems) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup> <span id=item1 tabindex=0></span> @@ -886,20 +800,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), item3); } -TEST_F(FocusgroupControllerTest, ArrowDownSkipsNonFocusableItems) { - AssertForwardSkipsNonFocusableItems(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightSkipsNonFocusableItems) { - AssertForwardSkipsNonFocusableItems(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on a focusgroup item which happens to also be an // extending focusgroup, an arrow key press should move the focus to the next // item within the extending focusgroup and skip non-focusable items. -void FocusgroupControllerTest::AssertForwardMovesInExtendingFocusgroup( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, MovesInExtendingFocusgroup) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup> <div id=item1 tabindex=0 focusgroup=extend> @@ -924,22 +829,14 @@ ASSERT_EQ(GetDocument().FocusedElement(), item3); } -TEST_F(FocusgroupControllerTest, ArrowDownMovesInExtendingFocusgroup) { - AssertForwardMovesInExtendingFocusgroup(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightMovesInExtendingFocusgroup) { - AssertForwardMovesInExtendingFocusgroup(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on a focusgroup item which happens to also be an // extending focusgroup, an arrow key press should move the focus to the next // item within the extending focusgroup and skip non-focusable items. If no // valid candidate is found within that extending focusgroup, the next element // (in pre-order traversal) should be considered. In this case, |item4| is the // valid next candidate. -void FocusgroupControllerTest::AssertForwardExitsExtendingFocusgroup(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, ExitsExtendingFocusgroup) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup> <div id=item1 tabindex=0 focusgroup=extend> @@ -963,20 +860,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item4); } -TEST_F(FocusgroupControllerTest, ArrowDownExitsExtendingFocusgroup) { - AssertForwardExitsExtendingFocusgroup(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightExitsExtendingFocusgroup) { - AssertForwardExitsExtendingFocusgroup(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on a focusgroup item that is an ancestor to an // extending focusgroup, the focus should move to the next element inside that // extending focusgroup even if it's not a direct child. -void FocusgroupControllerTest::AssertForwardMovesToNextElementWithinDescendants( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, + MovesToNextElementWithinDescendants) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup> <div id=item1 tabindex=0> @@ -1002,20 +891,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item2); } -TEST_F(FocusgroupControllerTest, ArrowDownMovesToNextElementWithinDescendants) { - AssertForwardMovesToNextElementWithinDescendants(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, - ArrowRightMovesToNextElementWithinDescendants) { - AssertForwardMovesToNextElementWithinDescendants(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on an item of a focusgroup that only supports the // orthogonal axis to the arrow key pressed, the arrow pressed shouldn't move // the focus. -void FocusgroupControllerTest::AssertForwardDoesntMoveFocusWhenAxisNotSupported( - int key) { +TEST_P(FocusgroupControllerForwardNavigationTest, + DoesntMoveFocusWhenAxisNotSupported) { + int key = GetParam(); ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); if (key == ui::DomKey::ARROW_DOWN) { // Arrow in the vertical axis, set the test to support only horizontal. @@ -1046,20 +927,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveFocusWhenAxisNotSupported) { - AssertForwardDoesntMoveFocusWhenAxisNotSupported(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, - ArrowRightDoesntMoveFocusWhenAxisNotSupported) { - AssertForwardDoesntMoveFocusWhenAxisNotSupported(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on an item of a focusgroup that only supports the // axis of the arrow key pressed the focus should move. -void FocusgroupControllerTest:: - AssertForwardMovesFocusWhenInArrowAxisOnlyFocusgroup(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, + MovesFocusWhenInArrowAxisOnlyFocusgroup) { + int key = GetParam(); if (key == ui::DomKey::ARROW_DOWN) { // The arrow is in the vertical axis, so the focusgroup should support only // the vertical axis. @@ -1093,21 +965,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), item2); } -TEST_F(FocusgroupControllerTest, - ArrowDownMovesFocusWhenInArrowAxisOnlyFocusgroup) { - AssertForwardMovesFocusWhenInArrowAxisOnlyFocusgroup(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, - ArrowRightMovesFocusWhenInArrowAxisOnlyFocusgroup) { - AssertForwardMovesFocusWhenInArrowAxisOnlyFocusgroup(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on an extending focusgroup element but that focusgroup // doesn't support the axis of the arrow key pressed, skip that subtree // altogether. -void FocusgroupControllerTest::AssertForwardSkipsExtendingFocusgroup(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, SkipsExtendingFocusgroup) { + int key = GetParam(); if (key == ui::DomKey::ARROW_DOWN) { // The arrow is in the vertical axis, so the extending focusgroup should // support only the horizontal axis. @@ -1146,19 +1008,10 @@ ASSERT_EQ(GetDocument().FocusedElement(), item3); } -TEST_F(FocusgroupControllerTest, ArrowDownSkipsExtendingFocusgroup) { - AssertForwardSkipsExtendingFocusgroup(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightSkipsExtendingFocusgroup) { - AssertForwardSkipsExtendingFocusgroup(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the last item of a focusgroup that doesn't support // wrapping in the axis of the arrow key pressed, the focus shouldn't move. -void FocusgroupControllerTest::AssertForwardDoesntWrapWhenNotSupported( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, DoesntWrapWhenNotSupported) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup> <span id=item1 tabindex=0></span> @@ -1177,20 +1030,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item2); } -TEST_F(FocusgroupControllerTest, ArrowDownDoesntWrapWhenNotSupported) { - AssertForwardDoesntWrapWhenNotSupported(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightDoesntWrapWhenNotSupported) { - AssertForwardDoesntWrapWhenNotSupported(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the last item of a focusgroup that doesn't support // wrapping in the axis of the arrow key pressed but supports wrapping in the // orthogonal axis, the focus shouldn't move. -void FocusgroupControllerTest:: - AssertForwardDoesntWrapEvenWhenOtherAxisSupported(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, + DoesntWrapEvenWhenOtherAxisSupported) { + int key = GetParam(); if (key == ui::DomKey::ARROW_DOWN) { // The arrow is in the vertical axis, so the focusgroup that wraps should be // in the horizontal axis only. @@ -1231,23 +1076,13 @@ ASSERT_EQ(GetDocument().FocusedElement(), item4); } -TEST_F(FocusgroupControllerTest, - ArrowDownDoesntWrapEvenWhenOtherAxisSupported) { - AssertForwardDoesntWrapEvenWhenOtherAxisSupported(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, - ArrowRightDoesntWrapEvenWhenOtherAxisSupported) { - AssertForwardDoesntWrapEvenWhenOtherAxisSupported(ui::DomKey::ARROW_RIGHT); -} - // This test validates that we don't get stuck in an infinite loop searching for // a focusable element in the extending focusgroup that wraps that doesn't // contain one. Wrapping should only be allowed in the focusgroup that contains // the focusable element we started on or in one of its ancestors. -void FocusgroupControllerTest::AssertForwardDoesntWrapInFocusgroupWithoutItems( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, + DoesntWrapInFocusgroupWithoutItems) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup> <span id=item1 tabindex=0></span> @@ -1274,19 +1109,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), item4); } -TEST_F(FocusgroupControllerTest, ArrowDownDoesntWrapInFocusgroupWithoutItems) { - AssertForwardDoesntWrapInFocusgroupWithoutItems(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightDoesntWrapInFocusgroupWithoutItems) { - AssertForwardDoesntWrapInFocusgroupWithoutItems(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the last item of a focusgroup that supports wrapping // in the axis of the arrow key pressed, the focus should move back to the first // item. -void FocusgroupControllerTest::AssertForwardWrapsSuccessfully(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, WrapsSuccessfully) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup=wrap> <span id=item1 tabindex=0></span> @@ -1307,19 +1134,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowDownWrapsSuccessfully) { - AssertForwardWrapsSuccessfully(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightWrapsSuccessfully) { - AssertForwardWrapsSuccessfully(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the last item of an inner focusgroup that supports // wrapping while its parent focusgroup also does, the focus should move to the // first item of the parent focusgroup. -void FocusgroupControllerTest::AssertForwardWrapsToParentFocusgroup(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, WrapsToParentFocusgroup) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup=wrap> <span id=item1 tabindex=0></span> @@ -1343,20 +1162,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowDownWrapsToParentFocusgroup) { - AssertForwardWrapsToParentFocusgroup(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightWrapsToParentFocusgroup) { - AssertForwardWrapsToParentFocusgroup(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the last item of an inner focusgroup that supports // wrapping while its parent focusgroup doesn't (in the axis of the arrow key // pressed), the focus should move to the first item of the inner focusgroup. -void FocusgroupControllerTest::AssertForwardWrapsInInnerFocusgroupOnly( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, WrapsInInnerFocusgroupOnly) { + int key = GetParam(); if (key == ui::DomKey::ARROW_DOWN) { // The arrow key is in the vertical axis, so the outer focusgroup should // only support the horizontal axis. @@ -1400,21 +1210,13 @@ ASSERT_EQ(GetDocument().FocusedElement(), item3); } -TEST_F(FocusgroupControllerTest, ArrowDownWrapsInInnerFocusgroupOnly) { - AssertForwardWrapsInInnerFocusgroupOnly(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightWrapsInInnerFocusgroupOnly) { - AssertForwardWrapsInInnerFocusgroupOnly(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the last item of an inner focusgroup that supports // wrapping while its parent focusgroup doesn't (in the axis of the arrow key // pressed), the focus should move to the first item of the inner focusgroup // even if there's another focusgroup supporting wrapping in the same axis as // the arrow key pressed in the hierarchy. -void FocusgroupControllerTest::AssertForwardWrapsInExpectedScope(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, WrapsInExpectedScope) { + int key = GetParam(); if (key == ui::DomKey::ARROW_DOWN) { // The arrow key supports the vertical axis, so the outer focusgroup should // only support horizontal wrapping. @@ -1462,21 +1264,13 @@ ASSERT_EQ(GetDocument().FocusedElement(), item3); } -TEST_F(FocusgroupControllerTest, ArrowDownWrapsInExpectedScope) { - AssertForwardWrapsInExpectedScope(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightWrapsInExpectedScope) { - AssertForwardWrapsInExpectedScope(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the last item of a focusgroup that supports // wrapping in the axis of the arrow key pressed and the first item is in an // inner focusgroup that supports it too, the focus moves to that item in the // inner focusgroup. -void FocusgroupControllerTest::AssertForwardWrapsAndGoesInInnerFocusgroup( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, + WrapsAndGoesInInnerFocusgroup) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root focusgroup=wrap> <div focusgroup=extend> @@ -1500,21 +1294,13 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowDownWrapsAndGoesInInnerFocusgroup) { - AssertForwardWrapsAndGoesInInnerFocusgroup(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, ArrowRightWrapsAndGoesInInnerFocusgroup) { - AssertForwardWrapsAndGoesInInnerFocusgroup(ui::DomKey::ARROW_RIGHT); -} - // When the focus is set on the last item of a focusgroup that supports // wrapping in the axis of the arrow key pressed and the first item is in an // inner focusgroup that doesn't support wrapping in the same axis, the focus // moves to the next item out of that inner focusgroup. -void FocusgroupControllerTest:: - AssertForwardWrapsAndSkipsOrthogonalInnerFocusgroup(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT); +TEST_P(FocusgroupControllerForwardNavigationTest, + WrapsAndSkipsOrthogonalInnerFocusgroup) { + int key = GetParam(); if (key == ui::DomKey::ARROW_DOWN) { // The arrow key is in the vertical axis, so the inner focusgroup should // only support the horizontal one. @@ -1557,25 +1343,24 @@ ASSERT_EQ(GetDocument().FocusedElement(), item3); } -TEST_F(FocusgroupControllerTest, - ArrowDownWrapsAndSkipsOrthogonalInnerFocusgroup) { - AssertForwardWrapsAndSkipsOrthogonalInnerFocusgroup(ui::DomKey::ARROW_DOWN); -} - -TEST_F(FocusgroupControllerTest, - ArrowRightWrapsAndSkipsOrthogonalInnerFocusgroup) { - AssertForwardWrapsAndSkipsOrthogonalInnerFocusgroup(ui::DomKey::ARROW_RIGHT); -} - // ***************************************************************************** -// FORWARD NAVIGATION - UP ARROW & LEFT ARROW +// BACKWARD NAVIGATION - UP ARROW & LEFT ARROW // ***************************************************************************** +class FocusgroupControllerBackwardNavigationTest + : public FocusgroupControllerTest, + public ::testing::WithParamInterface<int> {}; + +INSTANTIATE_TEST_SUITE_P(, + FocusgroupControllerBackwardNavigationTest, + testing::Values(ui::DomKey::ARROW_UP, + ui::DomKey::ARROW_LEFT)); + // When the focus is set on an element outside of the focusgroup, an arrow key // press shouldn't move the focus at all. -void FocusgroupControllerTest::AssertBackwardDoesntMoveFocusWhenOutOfFocusgroup( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + DoesntMoveFocusWhenOutOfFocusgroup) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <span id=out tabindex=-1></span> <div focusgroup> @@ -1595,19 +1380,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), out); } -TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveFocusWhenOutOfFocusgroup) { - AssertBackwardDoesntMoveFocusWhenOutOfFocusgroup(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveFocusWhenOutOfFocusgroup) { - AssertBackwardDoesntMoveFocusWhenOutOfFocusgroup(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the root of a focusgroup element, an arrow key press // shouldn't move the focus at all. -void FocusgroupControllerTest:: - AssertBackwardDoesntMoveFocusWhenOnFocusgroupRoot(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + DoesntMoveFocusWhenOnFocusgroupRoot) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div id=root tabindex=-1 focusgroup> <span id=item1 tabindex=0></span> @@ -1626,19 +1403,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), root); } -TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveFocusWhenOnFocusgroupRoot) { - AssertBackwardDoesntMoveFocusWhenOnFocusgroupRoot(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveFocusWhenOnFocusgroupRoot) { - AssertBackwardDoesntMoveFocusWhenOnFocusgroupRoot(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on a focusable element that isn't a focusgroup item, an // arrow key press shouldn't move the focus at all. -void FocusgroupControllerTest::AssertBackwardDoesntMoveWhenOnNonFocusgroupItem( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + DoesntMoveWhenOnNonFocusgroupItem) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div tabindex=-1 focusgroup> <div> @@ -1660,18 +1429,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), nonitem1); } -TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveWhenOnNonFocusgroupItem) { - AssertBackwardDoesntMoveWhenOnNonFocusgroupItem(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveWhenOnNonFocusgroupItem) { - AssertBackwardDoesntMoveWhenOnNonFocusgroupItem(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last element of a focusgroup, a backward key // press should move the focus to the previous item. -void FocusgroupControllerTest::AssertBackwardMovesFocusToPreviousItem(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + MovesFocusToPreviousItem) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup> <span id=item1 tabindex=0></span> @@ -1692,19 +1454,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpMovesFocusToPreviousItem) { - AssertBackwardMovesFocusToPreviousItem(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftMovesFocusToPreviousItem) { - AssertBackwardMovesFocusToPreviousItem(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last element of a focusgroup, a backward key // press should move the focus to the previous item, skipping any non-focusable // element. -void FocusgroupControllerTest::AssertBackwardSkipsNonFocusableItems(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + SkipsNonFocusableItems) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup> <span id=item1 tabindex=0></span> @@ -1727,20 +1482,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpSkipsNonFocusableItems) { - AssertBackwardSkipsNonFocusableItems(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftSkipsNonFocusableItems) { - AssertBackwardSkipsNonFocusableItems(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the only element of a focusgroup that doesn't wrap, // a backward key press shouldn't move the focus and we shouldn't get stuck in // an infinite loop. -void FocusgroupControllerTest::AssertBackwardDoesntMoveWhenOnlyOneItem( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + DoesntMoveWhenOnlyOneItem) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup> <span id=item1 tabindex=0></span> @@ -1758,20 +1505,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveWhenOnlyOneItem) { - AssertBackwardDoesntMoveWhenOnlyOneItem(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveWhenOnlyOneItem) { - AssertBackwardDoesntMoveWhenOnlyOneItem(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the only element of a focusgroup that wraps, a // backward key press shouldn't move the focus and we shouldn't get stuck in an // infinite loop. -void FocusgroupControllerTest::AssertBackwardDoesntMoveWhenOnlyOneItemAndWraps( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + DoesntMoveWhenOnlyOneItemAndWraps) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup=wrap> <span id=item1 tabindex=0></span> @@ -1789,19 +1528,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveWhenOnlyOneItemAndWraps) { - AssertBackwardDoesntMoveWhenOnlyOneItemAndWraps(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveWhenOnlyOneItemAndWraps) { - AssertBackwardDoesntMoveWhenOnlyOneItemAndWraps(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last element of a focusgroup that only supports // the orthogonal axis of the arrow key pressed, the focus shouldn't move. -void FocusgroupControllerTest::AssertBackwardDoesntMoveFocusAxisNotSupported( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + DoesntMoveFocusAxisNotSupported) { + int key = GetParam(); if (key == ui::DomKey::ARROW_UP) { // The arrow is in the vertical axis, so the focusgroup should only // support the horizontal axis. @@ -1833,20 +1564,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item2); } -TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveFocusAxisNotSupported) { - AssertBackwardDoesntMoveFocusAxisNotSupported(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveFocusAxisNotSupported) { - AssertBackwardDoesntMoveFocusAxisNotSupported(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last element of a focusgroup that only supports // the axis of the arrow key pressed, the focus should move to the previous // item. -void FocusgroupControllerTest:: - AssertBackwardMovesFocusWhenInArrowAxisOnlyFocusgroup(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + MovesFocusWhenInArrowAxisOnlyFocusgroup) { + int key = GetParam(); if (key == ui::DomKey::ARROW_UP) { // The arrow is in the vertical axis, so the focusgroup should only // support the vertical axis. @@ -1880,22 +1603,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, - ArrowUpMovesFocusWhenInArrowAxisOnlyFocusgroup) { - AssertBackwardMovesFocusWhenInArrowAxisOnlyFocusgroup(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, - ArrowLeftMovesFocusWhenInArrowAxisOnlyFocusgroup) { - AssertBackwardMovesFocusWhenInArrowAxisOnlyFocusgroup(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last item of a focusgroup and the previous item // is a descendant of a subtree, a backward arrow key press should move the // focus to that previous item within the subtree. -void FocusgroupControllerTest::AssertBackwardDescendIntoExtendingFocusgroup( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + DescendIntoExtendingFocusgroup) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup=wrap> <span id=item1 tabindex=0></span> @@ -1923,20 +1636,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item3); } -TEST_F(FocusgroupControllerTest, ArrowUpDescendIntoExtendingFocusgroup) { - AssertBackwardDescendIntoExtendingFocusgroup(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftDescendIntoExtendingFocusgroup) { - AssertBackwardDescendIntoExtendingFocusgroup(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last item of a focusgroup and the previous item // is located past a non-focusgroup subtree, a backward arrow key press should // move the focus to that previous item without getting stuck in the subtree. -void FocusgroupControllerTest::AssertBackwardSkipsNonFocusgroupSubtree( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + SkipsNonFocusgroupSubtree) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup=wrap> <span id=item1 tabindex=0></span> @@ -1961,22 +1666,14 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpSkipsNonFocusgroupSubtree) { - AssertBackwardSkipsNonFocusgroupSubtree(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftSkipsNonFocusgroupSubtree) { - AssertBackwardSkipsNonFocusgroupSubtree(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last item of a focusgroup and the previous item // is a descendant of a subtree, a backward arrow key press should move the // focus to that previous item within the subtree. However, if that subtree is // an extending focusgroup that supports only the orthogonal axis, it should be // skipped. -void FocusgroupControllerTest::AssertBackwardSkipsOrthogonalFocusgroup( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + SkipsOrthogonalFocusgroup) { + int key = GetParam(); if (key == ui::DomKey::ARROW_UP) { // The arrow is in the vertical axis, so the inner focusgroup should support // only the horizontal axis. @@ -2023,20 +1720,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpSkipsOrthogonalFocusgroup) { - AssertBackwardSkipsOrthogonalFocusgroup(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftSkipsOrthogonalFocusgroup) { - AssertBackwardSkipsOrthogonalFocusgroup(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last item of a focusgroup and the previous item // is located past an other (non-extending) focusgroup subtree, a backward arrow // key press should move the focus to that previous item without getting stuck // in the other focusgroup. -void FocusgroupControllerTest::AssertBackwardSkipsRootFocusgroup(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, SkipsRootFocusgroup) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup=wrap> <span id=item1 tabindex=0></span> @@ -2064,21 +1753,13 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpSkipsRootFocusgroup) { - AssertBackwardSkipsRootFocusgroup(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftSkipsRootFocusgroup) { - AssertBackwardSkipsRootFocusgroup(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last item of a focusgroup and the previous item // is located past an extending focusgroup that wraps but has no item in it, a // backward arrow key press should move the focus to that previous item without // getting stuck in the inner focusgroup. -void FocusgroupControllerTest::AssertBackwardSkipsEmptyWrappingFocusgroup( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + SkipsEmptyWrappingFocusgroup) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup=wrap> <span id=item1 tabindex=0></span> @@ -2106,22 +1787,14 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpSkipsEmptyWrappingFocusgroup) { - AssertBackwardSkipsEmptyWrappingFocusgroup(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftSkipsEmptyWrappingFocusgroup) { - AssertBackwardSkipsEmptyWrappingFocusgroup(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last item of a focusgroup and the previous item // is located past an other (non-extending) focusgroup subtree, a backward arrow // key press should move the focus to that previous item without getting stuck // in the other focusgroup. The same should still be true when inside a // focusgroup that extends a root focusgroup within the original focusgroup. -void FocusgroupControllerTest::AssertBackwardSkipsRootFocusgroupComplexCase( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + SkipsRootFocusgroupComplexCase) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup=wrap> <span id=item1 tabindex=0></span> @@ -2153,14 +1826,6 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpSkipsRootFocusgroupComplexCase) { - AssertBackwardSkipsRootFocusgroupComplexCase(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftSkipsRootFocusgroupComplexCase) { - AssertBackwardSkipsRootFocusgroupComplexCase(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the last item of a focusgroup and the previous item // is located past an extending focusgroup that only supports the orthogonal // axis, a backward arrow key press should move the focus to that previous item @@ -2168,9 +1833,9 @@ // The same should still be true when inside a focusgroup that extends another // extending focusgroup that supports only the orthogonal axis within the // original focusgroup. -void FocusgroupControllerTest:: - AssertBackwardSkipsOrthogonalFocusgroupComplexCase(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + SkipsOrthogonalFocusgroupComplexCase) { + int key = GetParam(); if (key == ui::DomKey::ARROW_UP) { // The arrow key is vertical, so the middle focusgroup should only support // the horizontal axis. @@ -2225,22 +1890,13 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpSkipsOrthogonalFocusgroupComplexCase) { - AssertBackwardSkipsOrthogonalFocusgroupComplexCase(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, - ArrowLeftSkipsOrthogonalFocusgroupComplexCase) { - AssertBackwardSkipsOrthogonalFocusgroupComplexCase(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the first item of an extending focusgroup that // doesn't support the axis of the arrow key pressed but the parent focusgroup // does, ascend to that focusgroup. This should work whether the extending // focusgroup is the child of the other focusgroup or a distant descendant. -void FocusgroupControllerTest::AssertBackwardAscendsToParentFocusgroup( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + AscendsToParentFocusgroup) { + int key = GetParam(); if (key == ui::DomKey::ARROW_UP) { // The arrow key is vertical, so the inner focusgroup should only support // the horizontal axis and the outer one should only support the vertical @@ -2292,18 +1948,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), item2); } -TEST_F(FocusgroupControllerTest, ArrowUpAscendsToParentFocusgroup) { - AssertBackwardAscendsToParentFocusgroup(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftAscendsToParentFocusgroup) { - AssertBackwardAscendsToParentFocusgroup(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the first item of a focusgroup, a backward arrow key // press shouldn't move the focus since there aren't any previous item. -void FocusgroupControllerTest::AssertBackwardDoesntWrapWhenNotSupported( - int key) { +TEST_P(FocusgroupControllerBackwardNavigationTest, + DoesntWrapWhenNotSupported) { + int key = GetParam(); ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup> @@ -2323,19 +1972,11 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpDoesntWrapWhenNotSupported) { - AssertBackwardDoesntWrapWhenNotSupported(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftDoesntWrapWhenNotSupported) { - AssertBackwardDoesntWrapWhenNotSupported(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the first item of a focusgroup that wraps, a // backward arrow key press should move the focus to the last item within the // focusgroup. -void FocusgroupControllerTest::AssertBackwardWrapsSuccessfully(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, WrapsSuccessfully) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup=wrap> <span id=item1 tabindex=0></span> @@ -2357,19 +1998,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item3); } -TEST_F(FocusgroupControllerTest, ArrowUpWrapsSuccessfully) { - AssertBackwardWrapsSuccessfully(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftWrapsSuccessfully) { - AssertBackwardWrapsSuccessfully(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the first item of a focusgroup that wraps and // supports only the axis of the pressed arrow key, a backward arrow key press // should move the focus to the last item within the focusgroup. -void FocusgroupControllerTest::AssertBackwardWrapsSuccessfullyInAxis(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + WrapsSuccessfullyInAxis) { + int key = GetParam(); if (key == ui::DomKey::ARROW_UP) { // The arrow key is in the vertical axis, so the focusgroup should only // support the vertical axis. @@ -2405,20 +2039,12 @@ ASSERT_EQ(GetDocument().FocusedElement(), item3); } -TEST_F(FocusgroupControllerTest, ArrowUpWrapsSuccessfullyInAxis) { - AssertBackwardWrapsSuccessfullyInAxis(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftWrapsSuccessfullyInAxis) { - AssertBackwardWrapsSuccessfullyInAxis(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the first item of a focusgroup that wraps and // supports only the orthogonal axis of the pressed arrow key, a backward arrow // key press shouldn't move the focus. -void FocusgroupControllerTest::AssertBackwardDoesntWrapInOrthogonalAxis( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + DoesntWrapInOrthogonalAxis) { + int key = GetParam(); if (key == ui::DomKey::ARROW_UP) { // The arrow key is in the vertical axis, so the focusgroup should only // support the horizontal axis. @@ -2452,21 +2078,13 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, ArrowUpDoesntWrapInOrthogonalAxis) { - AssertBackwardDoesntWrapInOrthogonalAxis(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftDoesntWrapInOrthogonalAxis) { - AssertBackwardDoesntWrapInOrthogonalAxis(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the first item of an extending focusgroup that // inherited its wrapping behavior, it should only wrap if the focused item is // also the first item of that parent focusgroup. If it is, then it should wrap // within the parent focusgroup, not within the extending focusgroup. -void FocusgroupControllerTest:: - AssertBackwardWrapsSuccessfullyInExtendingFocusgroup(int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + WrapsSuccessfullyInExtendingFocusgroup) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup=wrap> <div focusgroup=extend> @@ -2507,23 +2125,13 @@ ASSERT_EQ(GetDocument().FocusedElement(), item1); } -TEST_F(FocusgroupControllerTest, - ArrowUpWrapsSuccessfullyInExtendingFocusgroup) { - AssertBackwardWrapsSuccessfullyInExtendingFocusgroup(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, - ArrowLeftWrapsSuccessfullyInExtendingFocusgroup) { - AssertBackwardWrapsSuccessfullyInExtendingFocusgroup(ui::DomKey::ARROW_LEFT); -} - // When the focus is set on the first item of an extending focusgroup while // there are other non-item elements before, we should still be able to wrap to // the last item. Also, if the last item has other non-item elements after // itself, skipping these non-item elements shouldn't be an issue. -void FocusgroupControllerTest::AssertBackwardWrapsSuccessfullyInComplexCase( - int key) { - ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT); +TEST_P(FocusgroupControllerBackwardNavigationTest, + WrapsSuccessfullyInComplexCase) { + int key = GetParam(); GetDocument().body()->setInnerHTML(R"HTML( <div focusgroup=wrap> <div> @@ -2553,11 +2161,4 @@ ASSERT_EQ(GetDocument().FocusedElement(), item3); } -TEST_F(FocusgroupControllerTest, ArrowUpWrapsSuccessfullyInComplexCase) { - AssertBackwardWrapsSuccessfullyInComplexCase(ui::DomKey::ARROW_UP); -} - -TEST_F(FocusgroupControllerTest, ArrowLeftWrapsSuccessfullyInComplexCase) { - AssertBackwardWrapsSuccessfullyInComplexCase(ui::DomKey::ARROW_LEFT); -} } // namespace blink
diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc index 5947f89..2291543 100644 --- a/third_party/blink/renderer/core/paint/box_painter_base.cc +++ b/third_party/blink/renderer/core/paint/box_painter_base.cc
@@ -140,7 +140,7 @@ // introduces subpixel gaps along the corners. Those are avoided by // insetting the clipping path by one CSS pixel. if (has_opaque_background) - rect_to_clip_out.InflateWithRadii(-1); + rect_to_clip_out.Inset(1); if (!rect_to_clip_out.IsEmpty()) context.ClipOutRoundedRect(rect_to_clip_out);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc index 0f2ffb1..3e3b2fa2 100644 --- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc +++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -1961,8 +1961,22 @@ return false; // Now hit test ourselves. - if (hit_test_self && - IsVisibleToHitTest(box_fragment_, hit_test.result->GetHitTestRequest())) { + if (hit_test_self) { + if (UNLIKELY(!IsVisibleToHitTest(fragment, + hit_test.result->GetHitTestRequest()))) + return false; + if (UNLIKELY(fragment.IsOpaque())) + return false; + } else if (UNLIKELY(fragment.IsOpaque() && + hit_test.result->HasListBasedResult() && + IsVisibleToHitTest( + fragment, hit_test.result->GetHitTestRequest()))) { + // Opaque fragments should not hit, but they are still ancestors in the DOM + // tree. They should be added to the list-based result as ancestors if + // descendants hit. + hit_test_self = true; + } + if (hit_test_self) { PhysicalRect bounds_rect(physical_offset, size); if (UNLIKELY( hit_test.result->GetHitTestRequest().IsHitTestVisualOverflow())) { @@ -1988,10 +2002,6 @@ // See http://crbug.com/1043471 DCHECK(!box_item_ || box_item_->BoxFragment() == &fragment); if (box_item_ && box_item_->IsInlineBox()) { - // Opaque fragments should be included only for list-based hit-testing. - if (fragment.IsOpaque() && - !hit_test.result->GetHitTestRequest().ListBased()) - return false; DCHECK(inline_box_cursor_); if (hit_test.AddNodeToResultWithContentOffset( fragment.NodeForHitTest(),
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc index 9e9811ab..6eed59d 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc +++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -1506,6 +1506,11 @@ UpdateFilterEffect(object_, properties_->Filter(), state.filter); + // TODO(crbug.com/900241): Remove the setter when we can use + // state.direct_compositing_reasons to check for active animations. + state.has_active_filter_animation = + object_.StyleRef().HasCurrentFilterAnimation(); + // The CSS filter spec didn't specify how filters interact with overflow // clips. The implementation here mimics the old Blink/WebKit behavior for // backward compatibility. @@ -1524,9 +1529,7 @@ // On the other hand, "B" should not be clipped because the overflow clip // is not in its containing block chain, but as the filter output will be // clipped, so a blurred "B" may still be invisible. - if (!state.filter.IsEmpty() || - (full_context_.direct_compositing_reasons & - CompositingReason::kActiveFilterAnimation)) + if (!state.filter.IsEmpty() || state.has_active_filter_animation) state.output_clip = context_.current.clip; // TODO(trchen): A filter may contain spatial operations such that an @@ -1552,11 +1555,6 @@ state.compositor_element_id = GetCompositorElementId(CompositorElementIdNamespace::kEffectFilter); - // TODO(crbug.com/900241): Remove the setter when we can use - // state.direct_compositing_reasons to check for active animations. - state.has_active_filter_animation = - object_.StyleRef().HasCurrentFilterAnimation(); - EffectPaintPropertyNode::AnimationState animation_state; animation_state.is_running_filter_animation_on_compositor = object_.StyleRef().IsRunningFilterAnimationOnCompositor();
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc index aacc0ef..587491ca 100644 --- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc +++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -1020,8 +1020,10 @@ // missable child fragments. We may enter fragment traversal mode further // down in the subtree, and there may be a node that's a direct child of // |object|, fragment-wise, while it's further down in the tree, CSS - // box-tree-wise. - if (box && box->PhysicalFragmentCount()) { + // box-tree-wise. This is only an issue for OOF descendants, though, so only + // examine OOF containing blocks. + if (box && box->CanContainAbsolutePositionObjects() && + box->PhysicalFragmentCount()) { DCHECK_EQ(box->PhysicalFragmentCount(), 1u); fragment = box->GetPhysicalFragment(0); }
diff --git a/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5 b/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5 index 435f3bb..24f4a25 100644 --- a/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5 +++ b/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
@@ -67,6 +67,18 @@ permissions_policy_name: "autoplay", }, { + name: "BrowsingTopics", + permissions_policy_name: "browsing-topics", + feature_default: "EnableForAll", + depends_on: ["TopicsAPI"], + }, + { + name: "BrowsingTopicsBackwardCompatible", + permissions_policy_name: "interest-cohort", + feature_default: "EnableForAll", + depends_on: ["TopicsAPI"], + }, + { name: "Camera", permissions_policy_name: "camera", },
diff --git a/third_party/blink/renderer/core/script/pending_script.cc b/third_party/blink/renderer/core/script/pending_script.cc index f478790..a3fa931 100644 --- a/third_party/blink/renderer/core/script/pending_script.cc +++ b/third_party/blink/renderer/core/script/pending_script.cc
@@ -25,9 +25,11 @@ #include "third_party/blink/renderer/core/script/pending_script.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/mojom/script/script_type.mojom-shared.h" #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h" #include "third_party/blink/renderer/bindings/core/v8/script_controller.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document_parser_timing.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" @@ -36,6 +38,8 @@ #include "third_party/blink/renderer/core/script/script_element_base.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" namespace blink { @@ -136,7 +140,8 @@ return; } - if (!To<LocalDOMWindow>(context)->GetFrame()) { + LocalFrame* frame = To<LocalDOMWindow>(context)->GetFrame(); + if (!frame) { Dispose(); return; } @@ -154,6 +159,15 @@ return; } + std::unique_ptr<scheduler::TaskAttributionTracker::TaskScope> + task_attribution_scope; + DCHECK(ThreadScheduler::Current()); + ScriptState* script_state = ToScriptStateForMainWorld(frame); + if (auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker()) { + task_attribution_scope = + tracker->CreateTaskScope(script_state, absl::nullopt); + } + Script* script = GetSource(document_url); const bool was_canceled = WasCanceled();
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc index 5cc9848a..227c943 100644 --- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc +++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -524,6 +524,13 @@ if (!back_forward_cache_controller_host_.is_bound()) { return; } + if (!GetExecutionContext()->is_in_back_forward_cache()) { + // Don't send an eviction message unless the document associated with this + // DedicatedWorker is in back/forward cache. + // TODO(crbug.com/1163843): Maybe also check if eviction is already disabled + // for the document? + return; + } UMA_HISTOGRAM_ENUMERATION("BackForwardCache.Eviction.Renderer", reason); back_forward_cache_controller_host_->EvictFromBackForwardCache(reason); }
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn index b0e5356..a5d0c34 100644 --- a/third_party/blink/renderer/modules/BUILD.gn +++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -65,6 +65,7 @@ "//third_party/blink/renderer/modules/beacon", "//third_party/blink/renderer/modules/bluetooth", "//third_party/blink/renderer/modules/broadcastchannel", + "//third_party/blink/renderer/modules/browsing_topics", "//third_party/blink/renderer/modules/buckets", "//third_party/blink/renderer/modules/breakout_box", "//third_party/blink/renderer/modules/cache_storage", @@ -570,6 +571,7 @@ "push_messaging/push_subscription_test.cc", "remoteplayback/remote_playback_test.cc", "scheduler/dom_scheduler_test.cc", + "scheduler/task_attribution_tracker_impl_test.cc", "screen_orientation/screen_orientation_controller_test.cc", "sensor/sensor_test_utils.cc", "sensor/sensor_test_utils.h",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc index 2a23298..f2192581 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1095,22 +1095,13 @@ if (GetNode()->HasTagName(html_names::kDtTag)) return ax::mojom::blink::Role::kDescriptionListTerm; - // MathMLElement instances are not created when MathMLCore is disabled, so one - // cannot rely on Node::HasTagName(const MathMLQualifiedName&) to test the - // <math> tag. See crbug.com/1272556. - if (!RuntimeEnabledFeatures::MathMLCoreEnabled()) { - if (auto* element = DynamicTo<Element>(GetNode())) { - if (element->namespaceURI() == mathml_names::kNamespaceURI && - element->nodeName() == mathml_names::kMathTag.LocalName()) { - return ax::mojom::blink::Role::kMath; - } - } - } - // Mapping of MathML elements. See https://w3c.github.io/mathml-aam/ if (auto* element = DynamicTo<MathMLElement>(GetNode())) { - if (element->HasTagName(mathml_names::kMathTag)) - return ax::mojom::blink::Role::kMathMLMath; + if (element->HasTagName(mathml_names::kMathTag)) { + return RuntimeEnabledFeatures::MathMLCoreEnabled() + ? ax::mojom::blink::Role::kMathMLMath + : ax::mojom::blink::Role::kMath; + } if (element->HasTagName(mathml_names::kMfracTag)) return ax::mojom::blink::Role::kMathMLFraction; if (element->HasTagName(mathml_names::kMiTag))
diff --git a/third_party/blink/renderer/modules/browsing_topics/BUILD.gn b/third_party/blink/renderer/modules/browsing_topics/BUILD.gn new file mode 100644 index 0000000..236b65c39 --- /dev/null +++ b/third_party/blink/renderer/modules/browsing_topics/BUILD.gn
@@ -0,0 +1,14 @@ +# Copyright 2022 The Chromium 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("//third_party/blink/renderer/modules/modules.gni") + +blink_modules_sources("browsing_topics") { + sources = [ + "browsing_topics_document_supplement.cc", + "browsing_topics_document_supplement.h", + ] + + public_deps = [ "//third_party/blink/public/mojom:mojom_modules_blink" ] +}
diff --git a/third_party/blink/renderer/modules/browsing_topics/OWNERS b/third_party/blink/renderer/modules/browsing_topics/OWNERS new file mode 100644 index 0000000..bb1b03b --- /dev/null +++ b/third_party/blink/renderer/modules/browsing_topics/OWNERS
@@ -0,0 +1 @@ +file://components/browsing_topics/OWNERS
diff --git a/third_party/blink/renderer/modules/browsing_topics/browsing_topic.idl b/third_party/blink/renderer/modules/browsing_topics/browsing_topic.idl new file mode 100644 index 0000000..44e5ce6 --- /dev/null +++ b/third_party/blink/renderer/modules/browsing_topics/browsing_topic.idl
@@ -0,0 +1,13 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://github.com/jkarlin/topics + +dictionary BrowsingTopic { + long topic; + DOMString version; + DOMString configVersion; + DOMString modelVersion; + DOMString taxonomyVersion; +};
diff --git a/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.cc b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.cc new file mode 100644 index 0000000..4514913 --- /dev/null +++ b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.cc
@@ -0,0 +1,93 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.h" + +#include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/common/browser_interface_broker_proxy.h" +#include "third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom-blink.h" +#include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom-blink.h" +#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_browsing_topic.h" + +namespace blink { + +// static +const char BrowsingTopicsDocumentSupplement::kSupplementName[] = + "BrowsingTopicsDocumentSupplement"; + +// static +BrowsingTopicsDocumentSupplement* BrowsingTopicsDocumentSupplement::From( + Document& document) { + auto* supplement = + Supplement<Document>::From<BrowsingTopicsDocumentSupplement>(document); + if (!supplement) { + supplement = + MakeGarbageCollected<BrowsingTopicsDocumentSupplement>(document); + Supplement<Document>::ProvideTo(document, supplement); + } + return supplement; +} + +// static +ScriptPromise BrowsingTopicsDocumentSupplement::browsingTopics( + ScriptState* script_state, + Document& document, + ExceptionState& exception_state) { + auto* supplement = From(document); + return supplement->GetBrowsingTopics(script_state, document, exception_state); +} + +BrowsingTopicsDocumentSupplement::BrowsingTopicsDocumentSupplement( + Document& document) + : Supplement<Document>(document) {} + +ScriptPromise BrowsingTopicsDocumentSupplement::GetBrowsingTopics( + ScriptState* script_state, + Document& document, + ExceptionState& exception_state) { + if (!document.GetFrame()) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError, + "A browsing context is required when " + "calling document.browsingTopics()."); + return ScriptPromise(); + } + + ScriptPromiseResolver* resolver = + MakeGarbageCollected<ScriptPromiseResolver>(script_state); + ScriptPromise promise = resolver->Promise(); + + if (!document.GetExecutionContext()->IsFeatureEnabled( + mojom::blink::PermissionsPolicyFeature::kBrowsingTopics)) { + resolver->Reject(V8ThrowDOMException::CreateOrEmpty( + script_state->GetIsolate(), DOMExceptionCode::kInvalidAccessError, + "The \"browsing-topics\" Permissions Policy denied the use of " + "document.browsingTopics().")); + + return promise; + } + + if (!document.GetExecutionContext()->IsFeatureEnabled( + mojom::blink::PermissionsPolicyFeature:: + kBrowsingTopicsBackwardCompatible)) { + resolver->Reject(V8ThrowDOMException::CreateOrEmpty( + script_state->GetIsolate(), DOMExceptionCode::kInvalidAccessError, + "The \"interest-cohort\" Permissions Policy denied the use of " + "document.browsingTopics().")); + + return promise; + } + + resolver->Resolve(HeapVector<Member<BrowsingTopic>>()); + return promise; +} + +void BrowsingTopicsDocumentSupplement::Trace(Visitor* visitor) const { + Supplement<Document>::Trace(visitor); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.h b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.h new file mode 100644 index 0000000..52317a16 --- /dev/null +++ b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.h
@@ -0,0 +1,43 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BROWSING_TOPICS_BROWSING_TOPICS_DOCUMENT_SUPPLEMENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_BROWSING_TOPICS_BROWSING_TOPICS_DOCUMENT_SUPPLEMENT_H_ + +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/modules/modules_export.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +// Provides the implementation for the Topics API. +// Explainer: https://github.com/jkarlin/topics +class MODULES_EXPORT BrowsingTopicsDocumentSupplement + : public GarbageCollected<BrowsingTopicsDocumentSupplement>, + public Supplement<Document> { + public: + static const char kSupplementName[]; + + // Supplement functionality. + static BrowsingTopicsDocumentSupplement* From(Document&); + static ScriptPromise browsingTopics(ScriptState* script_state, + Document& document, + ExceptionState& exception_state); + + explicit BrowsingTopicsDocumentSupplement(Document&); + + // Implements the document.browsingTopics(). + ScriptPromise GetBrowsingTopics(ScriptState* script_state, + Document& document, + ExceptionState& exception_state); + + // GC functionality. + void Trace(Visitor* visitor) const override; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_BROWSING_TOPICS_BROWSING_TOPICS_DOCUMENT_SUPPLEMENT_H_
diff --git a/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.idl b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.idl new file mode 100644 index 0000000..ab1830c --- /dev/null +++ b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.idl
@@ -0,0 +1,19 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://github.com/jkarlin/topics + +[ + ImplementedAs=BrowsingTopicsDocumentSupplement, + RuntimeEnabled=TopicsAPI +] partial interface Document { + [ + NewObject, + CallWith=ScriptState, + RaisesException, + SecureContext, + MeasureAs=TopicsAPI_BrowsingTopics_Method + ] + Promise<sequence<BrowsingTopic>> browsingTopics(); +};
diff --git a/third_party/blink/renderer/modules/browsing_topics/idls.gni b/third_party/blink/renderer/modules/browsing_topics/idls.gni new file mode 100644 index 0000000..75134f2 --- /dev/null +++ b/third_party/blink/renderer/modules/browsing_topics/idls.gni
@@ -0,0 +1,7 @@ +# Copyright 2022 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +modules_dictionary_idl_files = [ "browsing_topic.idl" ] + +modules_dependency_idl_files = [ "browsing_topics_document_supplement.idl" ]
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_store.cc b/third_party/blink/renderer/modules/cookie_store/cookie_store.cc index eb69c0a8..3ebe2d88 100644 --- a/third_party/blink/renderer/modules/cookie_store/cookie_store.cc +++ b/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
@@ -7,6 +7,7 @@ #include <utility> #include "base/containers/contains.h" +#include "net/base/features.h" #include "net/cookies/canonical_cookie.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" #include "services/network/public/mojom/restricted_cookie_manager.mojom-blink.h" @@ -152,7 +153,10 @@ } absl::optional<net::CookiePartitionKey> cookie_partition_key = absl::nullopt; - if (options->partitioned() && partitioned_cookies_runtime_feature_enabled) { + if (options->partitioned() && + (partitioned_cookies_runtime_feature_enabled || + base::FeatureList::IsEnabled( + net::features::kPartitionedCookiesBypassOriginTrial))) { // We don't trust the renderer to determine the cookie partition key, so we // use this factory to indicate we are using a temporary value here. cookie_partition_key = net::CookiePartitionKey::FromScript();
diff --git a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.cc b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.cc index 53608cba..a3fc9de 100644 --- a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.cc +++ b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.cc
@@ -78,6 +78,11 @@ stream_associator_.Run(*stream_id_, output_device_id); } +media::AudioProcessorControls* MojoAudioInputIPC::GetProcessorControls() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return this; +} + void MojoAudioInputIPC::CloseStream() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); delegate_ = nullptr; @@ -87,6 +92,19 @@ processor_controls_.reset(); } +void MojoAudioInputIPC::GetStats(GetStatsCB callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (processor_controls_) + processor_controls_->GetStats(std::move(callback)); +} + +void MojoAudioInputIPC::SetPreferredNumCaptureChannels( + int32_t num_preferred_channels) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (processor_controls_) + processor_controls_->SetPreferredNumCaptureChannels(num_preferred_channels); +} + void MojoAudioInputIPC::StreamCreated( mojo::PendingRemote<media::mojom::blink::AudioInputStream> stream, mojo::PendingReceiver<media::mojom::blink::AudioInputStreamClient>
diff --git a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.h b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.h index 72e0bbd..3282f42 100644 --- a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.h +++ b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.h
@@ -12,9 +12,9 @@ #include "base/sequence_checker.h" #include "media/audio/audio_input_ipc.h" #include "media/audio/audio_source_parameters.h" +#include "media/base/audio_processor_controls.h" #include "media/mojo/mojom/audio_input_stream.mojom-blink.h" #include "media/mojo/mojom/audio_processing.mojom-blink.h" -#include "media/webrtc/audio_processor_controls.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -29,6 +29,7 @@ // thread. class MODULES_EXPORT MojoAudioInputIPC : public media::AudioInputIPC, + public media::AudioProcessorControls, public mojom::blink::RendererAudioInputStreamFactoryClient, public media::mojom::blink::AudioInputStreamClient { public: @@ -67,8 +68,13 @@ void RecordStream() override; void SetVolume(double volume) override; void SetOutputDeviceForAec(const std::string& output_device_id) override; + media::AudioProcessorControls* GetProcessorControls() override; void CloseStream() override; + // AudioProcessorControls implementation + void GetStats(GetStatsCB callback) override; + void SetPreferredNumCaptureChannels(int32_t num_preferred_channels) override; + private: void StreamCreated( mojo::PendingRemote<media::mojom::blink::AudioInputStream> stream,
diff --git a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc_test.cc b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc_test.cc index 1333f37..164f16628 100644 --- a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc_test.cc +++ b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc_test.cc
@@ -66,6 +66,17 @@ MOCK_METHOD1(SetVolume, void(double)); }; +class MockAudioProcessorControls + : public media::mojom::blink::AudioProcessorControls { + public: + void GetStats(GetStatsCallback cb) override { + GetStatsCalled(); + std::move(cb).Run(media::AudioProcessingStats()); + } + MOCK_METHOD0(GetStatsCalled, void()); + MOCK_METHOD1(SetPreferredNumCaptureChannels, void(int32_t)); +}; + class MockDelegate : public media::AudioInputIPCDelegate { public: MockDelegate() = default; @@ -86,10 +97,11 @@ class FakeStreamCreator { public: FakeStreamCreator(media::mojom::blink::AudioInputStream* stream, + media::mojom::blink::AudioProcessorControls* controls, bool initially_muted, bool expect_processing_config = false) - : stream_(stream), - receiver_(stream_), + : receiver_(stream), + controls_receiver_(controls), initially_muted_(initially_muted), expect_processing_config_(expect_processing_config) {} @@ -98,12 +110,11 @@ mojo::PendingRemote<mojom::blink::RendererAudioInputStreamFactoryClient> factory_client, mojo::PendingReceiver<media::mojom::blink::AudioProcessorControls> - controls_receiver, + pending_controls_receiver, const media::AudioParameters& params, bool automatic_gain_control, uint32_t total_segments) { EXPECT_FALSE(receiver_.is_bound()); - EXPECT_NE(stream_, nullptr); EXPECT_EQ(source_params.session_id, SourceParams().session_id); factory_client_.reset(); factory_client_.Bind(std::move(factory_client)); @@ -111,7 +122,9 @@ EXPECT_TRUE( base::CancelableSyncSocket::CreatePair(&socket_, &foreign_socket)); - EXPECT_EQ(!!controls_receiver, expect_processing_config_); + EXPECT_EQ(!!pending_controls_receiver, expect_processing_config_); + if (pending_controls_receiver) + controls_receiver_.Bind(std::move(pending_controls_receiver)); factory_client_->StreamCreated( receiver_.BindNewPipeAndPassRemote(), @@ -130,6 +143,7 @@ void Rearm() { stream_client_.reset(); receiver_.reset(); + controls_receiver_.reset(); socket_.Close(); } @@ -139,11 +153,12 @@ } private: - media::mojom::blink::AudioInputStream* stream_; mojo::Remote<media::mojom::blink::AudioInputStreamClient> stream_client_; mojo::Remote<mojom::blink::RendererAudioInputStreamFactoryClient> factory_client_; mojo::Receiver<media::mojom::blink::AudioInputStream> receiver_; + mojo::Receiver<media::mojom::blink::AudioProcessorControls> + controls_receiver_; bool initially_muted_; bool expect_processing_config_; base::CancelableSyncSocket socket_; @@ -159,8 +174,9 @@ TEST(MojoAudioInputIPC, OnStreamCreated_Propagates) { StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; StrictMock<MockDelegate> delegate; - FakeStreamCreator creator(&stream, false); + FakeStreamCreator creator(&stream, &controls, false); const std::unique_ptr<media::AudioInputIPC> ipc = std::make_unique<MojoAudioInputIPC>( @@ -178,8 +194,9 @@ TEST(MojoAudioInputIPC, OnStreamCreated_Propagates_WithProcessingConfig) { StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; StrictMock<MockDelegate> delegate; - FakeStreamCreator creator(&stream, false, + FakeStreamCreator creator(&stream, &controls, false, /*expect_processing_config*/ true); const std::unique_ptr<media::AudioInputIPC> ipc = @@ -225,8 +242,9 @@ TEST(MojoAudioInputIPC, OnStreamCreated_PropagatesInitiallyMuted) { StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; StrictMock<MockDelegate> delegate; - FakeStreamCreator creator(&stream, true); + FakeStreamCreator creator(&stream, &controls, true); const std::unique_ptr<media::AudioInputIPC> ipc = std::make_unique<MojoAudioInputIPC>( @@ -244,8 +262,9 @@ TEST(MojoAudioInputIPC, IsReusable) { StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; StrictMock<MockDelegate> delegate; - FakeStreamCreator creator(&stream, false); + FakeStreamCreator creator(&stream, &controls, false); const std::unique_ptr<media::AudioInputIPC> ipc = std::make_unique<MojoAudioInputIPC>( @@ -268,8 +287,9 @@ TEST(MojoAudioInputIPC, IsReusableAfterError) { StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; StrictMock<MockDelegate> delegate; - FakeStreamCreator creator(&stream, false); + FakeStreamCreator creator(&stream, &controls, false); const std::unique_ptr<media::AudioInputIPC> ipc = std::make_unique<MojoAudioInputIPC>( @@ -298,8 +318,9 @@ TEST(MojoAudioInputIPC, Record_Records) { StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; StrictMock<MockDelegate> delegate; - FakeStreamCreator creator(&stream, false); + FakeStreamCreator creator(&stream, &controls, false); const std::unique_ptr<media::AudioInputIPC> ipc = std::make_unique<MojoAudioInputIPC>( @@ -320,8 +341,9 @@ TEST(MojoAudioInputIPC, SetVolume_SetsVolume) { StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; StrictMock<MockDelegate> delegate; - FakeStreamCreator creator(&stream, false); + FakeStreamCreator creator(&stream, &controls, false); const std::unique_ptr<media::AudioInputIPC> ipc = std::make_unique<MojoAudioInputIPC>( @@ -342,8 +364,9 @@ TEST(MojoAudioInputIPC, SetOutputDeviceForAec_AssociatesInputAndOutputForAec) { StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; StrictMock<MockDelegate> delegate; - FakeStreamCreator creator(&stream, false); + FakeStreamCreator creator(&stream, &controls, false); const std::unique_ptr<media::AudioInputIPC> ipc = std::make_unique<MojoAudioInputIPC>( @@ -361,4 +384,134 @@ base::RunLoop().RunUntilIdle(); } +TEST(MojoAudioInputIPC, + Controls_NotCalled_BeforeStreamCreated_WithoutProcessing) { + StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; + StrictMock<MockDelegate> delegate; + FakeStreamCreator creator(&stream, &controls, false); + + const std::unique_ptr<media::AudioInputIPC> ipc = + std::make_unique<MojoAudioInputIPC>( + SourceParams(), creator.GetCallback(), + base::BindRepeating(&AssociateOutputForAec)); + + // StrictMock will verify that no calls are made to |controls|. + media::AudioProcessorControls* media_controls = ipc->GetProcessorControls(); + media_controls->SetPreferredNumCaptureChannels(1); + media_controls->GetStats(media::AudioProcessorControls::GetStatsCB()); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioInputIPC, + Controls_NotCalled_AfterStreamCreated_WithoutProcessing) { + StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; + StrictMock<MockDelegate> delegate; + FakeStreamCreator creator(&stream, &controls, false); + + const std::unique_ptr<media::AudioInputIPC> ipc = + std::make_unique<MojoAudioInputIPC>( + SourceParams(), creator.GetCallback(), + base::BindRepeating(&AssociateOutputForAec)); + + media::AudioProcessorControls* media_controls = ipc->GetProcessorControls(); + + EXPECT_CALL(delegate, GotOnStreamCreated(_)); + + ipc->CreateStream(&delegate, Params(), false, kTotalSegments); + base::RunLoop().RunUntilIdle(); + + // StrictMock will verify that no calls are made to |controls|. + media_controls->SetPreferredNumCaptureChannels(1); + media_controls->GetStats(media::AudioProcessorControls::GetStatsCB()); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioInputIPC, Controls_NotCalled_BeforeStreamCreated_WithProcessing) { + StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; + StrictMock<MockDelegate> delegate; + FakeStreamCreator creator(&stream, &controls, false, + /*expect_processing_config*/ true); + + const std::unique_ptr<media::AudioInputIPC> ipc = + std::make_unique<MojoAudioInputIPC>( + SourceParamsWithProcessing(), creator.GetCallback(), + base::BindRepeating(&AssociateOutputForAec)); + + // StrictMock will verify that no calls are made to |controls|. + media::AudioProcessorControls* media_controls = ipc->GetProcessorControls(); + media_controls->SetPreferredNumCaptureChannels(1); + media_controls->GetStats(media::AudioProcessorControls::GetStatsCB()); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioInputIPC, Controls_Called_AfterStreamCreated_WithProcessing) { + StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; + StrictMock<MockDelegate> delegate; + FakeStreamCreator creator(&stream, &controls, false, + /*expect_processing_config*/ true); + + const std::unique_ptr<media::AudioInputIPC> ipc = + std::make_unique<MojoAudioInputIPC>( + SourceParamsWithProcessing(), creator.GetCallback(), + base::BindRepeating(&AssociateOutputForAec)); + + media::AudioProcessorControls* media_controls = ipc->GetProcessorControls(); + + EXPECT_CALL(delegate, GotOnStreamCreated(_)); + EXPECT_CALL(controls, SetPreferredNumCaptureChannels(1)); + EXPECT_CALL(controls, GetStatsCalled()); + + ipc->CreateStream(&delegate, Params(), false, kTotalSegments); + base::RunLoop().RunUntilIdle(); + + media_controls->SetPreferredNumCaptureChannels(1); + media_controls->GetStats( + base::BindOnce([](const media::AudioProcessingStats& stats) {})); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioInputIPC, Controls_NotCalled_AfterStreamClosed_WithProcessing) { + StrictMock<MockStream> stream; + StrictMock<MockAudioProcessorControls> controls; + StrictMock<MockDelegate> delegate; + FakeStreamCreator creator(&stream, &controls, false, + /*expect_processing_config*/ true); + + const std::unique_ptr<media::AudioInputIPC> ipc = + std::make_unique<MojoAudioInputIPC>( + SourceParamsWithProcessing(), creator.GetCallback(), + base::BindRepeating(&AssociateOutputForAec)); + + media::AudioProcessorControls* media_controls = ipc->GetProcessorControls(); + + EXPECT_CALL(delegate, GotOnStreamCreated(_)); + + ipc->CreateStream(&delegate, Params(), false, kTotalSegments); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); + + // StrictMock will verify that no calls are made to |controls|. + media_controls->SetPreferredNumCaptureChannels(1); + media_controls->GetStats(media::AudioProcessorControls::GetStatsCB()); + base::RunLoop().RunUntilIdle(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc index 1026869..9dc8e1d 100644 --- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc +++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -12,7 +12,6 @@ #include "build/build_config.h" #include "media/base/audio_parameters.h" #include "third_party/abseil-cpp/absl/types/optional.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" #include "third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.h" #include "third_party/blink/renderer/platform/mediastream/aec_dump_agent_impl.h" @@ -110,13 +109,6 @@ return audio_processor_->OutputFormat(); } -void MediaStreamAudioProcessor::SetOutputWillBeMuted(bool muted) { - DCHECK(main_thread_runner_->BelongsToCurrentThread()); - DCHECK(base::FeatureList::IsEnabled( - features::kMinimizeAudioProcessingForUnusedOutput)); - audio_processor_->SetOutputWillBeMuted(muted); -} - void MediaStreamAudioProcessor::OnStartDump(base::File dump_file) { DCHECK(main_thread_runner_->BelongsToCurrentThread()); audio_processor_->OnStartDump(std::move(dump_file));
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h index ae5f60b..7baa6add 100644 --- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h +++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
@@ -88,11 +88,6 @@ return audio_processor_->has_webrtc_audio_processing(); } - // Instructs the Audio Processing Module (APM) to reduce its complexity when - // |muted| is true. This mode is triggered when all audio tracks are disabled. - // The default APM complexity mode is restored by |muted| set to false. - void SetOutputWillBeMuted(bool muted); - // AecDumpAgentImpl::Delegate implementation. // Called on the main render thread. void OnStartDump(base::File dump_file) override;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc index 0ebce6f2..a4140057 100644 --- a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc +++ b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
@@ -380,25 +380,6 @@ return; DidSetMediaStreamTrackEnabled(component_.Get()); - - MediaStreamAudioSource* media_stream_audio_source = - MediaStreamAudioSource::From(component_->Source()); - ProcessedLocalAudioSource* processed_local_audio_source = - ProcessedLocalAudioSource::From(media_stream_audio_source); - if (media_stream_audio_source && processed_local_audio_source) { - if (!enabled) { - // One track was disabled. Check if all tracks are disabled and inform the - // APM about the state. The APM can enter a low-complexity mode if it - // knows that all tracks are muted and that saves CPU cycles. - const bool all_tracks_disabled = - media_stream_audio_source->AllTracksAreDisabled(); - processed_local_audio_source->SetOutputWillBeMuted(all_tracks_disabled); - } else { - // At least one track is enabled. Tell the APM to go back to its normal - // mode. - processed_local_audio_source->SetOutputWillBeMuted(false); - } - } } bool MediaStreamTrack::muted() const {
diff --git a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc index 0ccb6dd6..d36770b 100644 --- a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc +++ b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc
@@ -17,7 +17,6 @@ #include "media/audio/audio_source_parameters.h" #include "media/base/channel_layout.h" #include "media/base/sample_rates.h" -#include "media/webrtc/audio_processor_controls.h" #include "media/webrtc/webrtc_features.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h" @@ -465,23 +464,12 @@ scoped_refptr<webrtc::AudioProcessorInterface> ProcessedLocalAudioSource::GetAudioProcessor() const { DCHECK(media_stream_audio_processor_); + if (!media_stream_audio_processor_->has_webrtc_audio_processing()) + return nullptr; return static_cast<scoped_refptr<webrtc::AudioProcessorInterface>>( media_stream_audio_processor_); } -bool ProcessedLocalAudioSource::HasWebRtcAudioProcessing() const { - return media_stream_audio_processor_ && - media_stream_audio_processor_->has_webrtc_audio_processing(); -} - -void ProcessedLocalAudioSource::SetOutputWillBeMuted(bool muted) { - if (base::FeatureList::IsEnabled( - features::kMinimizeAudioProcessingForUnusedOutput) && - HasWebRtcAudioProcessing()) { - media_stream_audio_processor_->SetOutputWillBeMuted(muted); - } -} - void ProcessedLocalAudioSource::SetVolume(double volume) { DVLOG(1) << "ProcessedLocalAudioSource::SetVolume()"; DCHECK_LE(volume, 1.0);
diff --git a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h index 3a98847..f99e103 100644 --- a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h +++ b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h
@@ -75,17 +75,11 @@ absl::optional<blink::AudioProcessingProperties> GetAudioProcessingProperties() const final; - // The following accessors are valid after the source is started (when the - // first track is connected). + // Valid after the source is started (when the first track is connected). Will + // return nullptr if WebRTC stats are no available for the current + // configuration. scoped_refptr<webrtc::AudioProcessorInterface> GetAudioProcessor() const; - bool HasWebRtcAudioProcessing() const; - - // Instructs the Audio Processing Module (APM) to reduce its complexity when - // |muted| is true. This mode is triggered when all audio tracks are disabled. - // The default APM complexity mode is restored when |muted| is set to false. - void SetOutputWillBeMuted(bool muted); - const scoped_refptr<blink::MediaStreamAudioLevelCalculator::Level>& audio_level() const { return level_calculator_.level();
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc index bbcccda..5be0c77 100644 --- a/third_party/blink/renderer/modules/modules_initializer.cc +++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -80,6 +80,7 @@ #include "third_party/blink/renderer/modules/push_messaging/push_messaging_client.h" #include "third_party/blink/renderer/modules/remoteplayback/html_media_element_remote_playback.h" #include "third_party/blink/renderer/modules/remoteplayback/remote_playback.h" +#include "third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h" #include "third_party/blink/renderer/modules/screen_enumeration/screen_details.h" #include "third_party/blink/renderer/modules/screen_enumeration/window_screens.h" #include "third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller.h" @@ -103,6 +104,7 @@ #include "third_party/blink/renderer/platform/heap/persistent.h" #include "third_party/blink/renderer/platform/mojo/mojo_helper.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/widget/frame_widget.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/blink/renderer/platform/wtf/functional.h" @@ -210,6 +212,9 @@ std::make_unique<ImageBitmapRenderingContext::Factory>()); OffscreenCanvas::RegisterRenderingContextFactory( std::make_unique<GPUCanvasContext::Factory>()); + + ThreadScheduler::Current()->InitializeTaskAttributionTracker( + std::make_unique<scheduler::TaskAttributionTrackerImpl>()); } void ModulesInitializer::InitLocalFrame(LocalFrame& frame) const {
diff --git a/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc b/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc index de6e487..89a86b5c 100644 --- a/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc +++ b/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc
@@ -51,18 +51,16 @@ const char kToken[] = "%s"; +// Verify custom handler URL security as described in steps 6 and 7 +// https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters static bool VerifyCustomHandlerURLSecurity( const LocalDOMWindow& window, const KURL& full_url, String& error_message, ProtocolHandlerSecurityLevel security_level) { - // This matches ProtocolHandler::IsValid(). - // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters - bool has_valid_scheme = - full_url.ProtocolIsInHTTPFamily() || - (security_level == ProtocolHandlerSecurityLevel::kExtensionFeatures && - CommonSchemeRegistry::IsExtensionScheme(full_url.Protocol().Ascii())); - if (!has_valid_scheme || !network::IsUrlPotentiallyTrustworthy(full_url)) { + // The specification says that the API throws SecurityError exception if the + // URL's protocol isn't HTTP(S) or is potentially trustworthy. + if (!IsAllowedCustomHandlerURL(full_url, security_level)) { error_message = "The scheme of the url provided must be HTTP(S)."; return false; } @@ -115,13 +113,10 @@ return false; } - bool allow_ext_plus_prefix = - security_level >= ProtocolHandlerSecurityLevel::kExtensionFeatures; - bool has_custom_scheme_prefix; + bool has_custom_scheme_prefix = false; StringUTF8Adaptor scheme_adaptor(scheme); if (!IsValidCustomHandlerScheme(scheme_adaptor.AsStringPiece(), - allow_ext_plus_prefix, - has_custom_scheme_prefix)) { + security_level, &has_custom_scheme_prefix)) { if (has_custom_scheme_prefix) { error_string = "The scheme name '" + scheme + "' is not allowed. Schemes starting with '" + scheme +
diff --git a/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.h b/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.h index 3ca6f6a4..607d4fb6 100644 --- a/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.h +++ b/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.h
@@ -40,14 +40,14 @@ class NavigatorContentUtilsClient; enum class ProtocolHandlerSecurityLevel; -// Verify custom handler schemes for errors as described in +// Verify custom handler schemes for errors as described in steps 1 and 2 // https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers. // Callers should surface an error with |error_message| if it returns false. bool VerifyCustomHandlerScheme(const String& scheme, String& error_message, ProtocolHandlerSecurityLevel security_level); -// Verify custom handler URLs for syntax errors as described in +// Verify custom handler URLs for syntax errors as described in step 3 // https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers. // Callers should surface an error with |error_message| if it returns false. // |full_url| is calculated URL that needs to resolve to a valid URL.
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc index 42973ab..3787783 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc +++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -39,6 +39,8 @@ #include "base/lazy_instance.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" +#include "build/build_config.h" +#include "build/buildflag.h" #include "services/metrics/public/cpp/ukm_builders.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/common/features.h" @@ -294,63 +296,27 @@ DCHECK_EQ(configuration->rtcpMuxPolicy(), "require"); } - if (configuration->hasSdpSemantics()) { - if (configuration->sdpSemantics() == "plan-b") { - web_configuration.sdp_semantics = webrtc::SdpSemantics::kPlanB; - // Extend the Plan B deprecation deadline if - // RTCExtendDeadlineForPlanBRemoval is enabled, i.e. if the page has opted - // in to the 'RTCPeerConnection Plan B SDP Semantics' Deprecation Trial or - // if --enable-blink-features=RTCExtendDeadlineForPlanBRemoval was used. - // Local files also get the extended deadline beecause "file://" URLs - // cannot sign up for Origin Trials. - if (RuntimeEnabledFeatures::RTCExtendDeadlineForPlanBRemovalEnabled( - context) || - context->Url().IsLocalFile()) { - // TODO(https://crbug.com/857004): In M97, when the Deprecation Trial - // ends, remove this code path in favor of throwing the exception below. - Deprecation::CountDeprecation( - context, - WebFeature:: - kRTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial); - } else { - // The Deprecation Trial is not active. In this case, throw an exception - // if RTCDisallowPlanBOutsideDeprecationTrial is enabled. - if (base::FeatureList::IsEnabled( - features::kRTCDisallowPlanBOutsideDeprecationTrial)) { - // Throw Plan B exception! - UseCounter::Count( - context, WebFeature::kRTCPeerConnectionPlanBThrewAnException); - exception_state->ThrowDOMException( - DOMExceptionCode::kNotSupportedError, - "Plan B SDP semantics is a legacy version of the Session " - "Description Protocol that has severe compatibility issues on " - "modern browsers and is no longer supported. See " - "https://www.chromestatus.com/feature/5823036655665152 for more " - "details, including the possibility of registering for a " - "Deprecation Trial in order to extend the Plan B deprecation " - "deadline for a limited amount of time."); - } else { - // Throwing is not enabled, so just show a deprecation warning. - Deprecation::CountDeprecation( - context, WebFeature::kRTCPeerConnectionSdpSemanticsPlanB); - } - } - } else { - DCHECK_EQ(configuration->sdpSemantics(), "unified-plan"); - web_configuration.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; - } - } else { - // RTCUnifiedPlanByDefault decides the SDP semantics unless specified by the - // configuration (see above). By default RTCUnifiedPlanByDefault is enabled, - // meaning "Unified Plan" is used. For "Plan B"-by-default, pass the flag: - // --disable-features=RTCUnifiedPlanByDefault - if (!base::FeatureList::IsEnabled(features::kRTCUnifiedPlanByDefault) && - !RuntimeEnabledFeatures::RTCUnifiedPlanByDefaultEnabled()) { - web_configuration.sdp_semantics = webrtc::SdpSemantics::kPlanB; - } else { - web_configuration.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; - } + // Unified Plan is used by default (RTCUnifiedPlanByDefault is + // ENABLED_BY_DEFAULT). For testing-only purposes the default can be set to + // Plan B instead using --disable-features=RTCUnifiedPlanByDefault. + DCHECK_EQ(web_configuration.sdp_semantics, + webrtc::SdpSemantics::kUnifiedPlan); + if (!base::FeatureList::IsEnabled(features::kRTCUnifiedPlanByDefault) && + !RuntimeEnabledFeatures::RTCUnifiedPlanByDefaultEnabled()) { + web_configuration.sdp_semantics = webrtc::SdpSemantics::kPlanB; + Deprecation::CountDeprecation( + context, WebFeature::kRTCPeerConnectionSdpSemanticsPlanB); } + // Only on Fuchsia is it still possible to overwrite the default sdpSemantics + // value in JavaScript. + // TODO(https://crbug.com/1302249): Don't support Plan B on Fuchsia either, + // delete Plan B from all of Chromium. +#if BUILDFLAG(IS_FUCHSIA) + if (configuration->hasSdpSemantics() && + configuration->sdpSemantics() == "plan-b") { + web_configuration.sdp_semantics = webrtc::SdpSemantics::kPlanB; + } +#endif if (configuration->hasIceServers()) { WebVector<webrtc::PeerConnectionInterface::IceServer> ice_servers;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc index 32d27722..7ac9153c 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc +++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
@@ -7,6 +7,8 @@ #include <string> #include "base/bind.h" +#include "build/build_config.h" +#include "build/buildflag.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" @@ -583,7 +585,10 @@ EXPECT_FALSE(pc->GetTrackForTesting(track_component.Get())); } -TEST_F(RTCPeerConnectionTest, CheckForComplexSdpWithSdpSemanticsPlanB) { +#if BUILDFLAG(IS_FUCHSIA) + +TEST_F(RTCPeerConnectionTest, + CheckForComplexSdpWithSdpSemanticsPlanBOnFuchsia) { V8TestingScope scope; Persistent<RTCPeerConnection> pc = CreatePC(scope, "plan-b"); RTCSessionDescriptionInit* sdp = RTCSessionDescriptionInit::Create(); @@ -612,6 +617,8 @@ pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value()); } +#endif // BUILDFLAG(IS_FUCHSIA) + TEST_F(RTCPeerConnectionTest, CheckForComplexSdpWithSdpSemanticsUnifiedPlan) { V8TestingScope scope; Persistent<RTCPeerConnection> pc = CreatePC(scope, "unified-plan"); @@ -1204,6 +1211,7 @@ EXPECT_TRUE(scope.GetDocument().IsUseCounted( WebFeature::kRTCPeerConnectionUsingComplexUnifiedPlan)); } +#if BUILDFLAG(IS_FUCHSIA) // Constructor with {sdpSemantics:"plan-b"}. { V8TestingScope scope; @@ -1232,6 +1240,7 @@ EXPECT_FALSE(scope.GetDocument().IsUseCounted( WebFeature::kRTCPeerConnectionUsingComplexUnifiedPlan)); } +#endif // BUILDFLAG(IS_FUCHSIA) // Constructor with {sdpSemantics:"unified-plan"}. { V8TestingScope scope;
diff --git a/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc index 19d056a..b966170 100644 --- a/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc +++ b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc
@@ -192,14 +192,8 @@ if (auto* media_stream_source = blink::ProcessedLocalAudioSource::From( blink::MediaStreamAudioSource::From(component_->Source()))) { local_track_audio_sink_->SetLevel(media_stream_source->audio_level()); - // The sink only grabs stats from the audio processor. Stats are only - // available if WebRtc audio processing is turned on. Therefore, only - // provide the sink a reference to the processor if audio processing is - // turned on. - if (media_stream_source->HasWebRtcAudioProcessing()) { - local_track_audio_sink_->SetAudioProcessor( - media_stream_source->GetAudioProcessor()); - } + local_track_audio_sink_->SetAudioProcessor( + media_stream_source->GetAudioProcessor()); } native_track->AddSink(local_track_audio_sink_.get()); webrtc_track_ = local_track_audio_sink_->webrtc_audio_track();
diff --git a/third_party/blink/renderer/modules/scheduler/BUILD.gn b/third_party/blink/renderer/modules/scheduler/BUILD.gn index 5fc44e9..c7908c41 100644 --- a/third_party/blink/renderer/modules/scheduler/BUILD.gn +++ b/third_party/blink/renderer/modules/scheduler/BUILD.gn
@@ -14,6 +14,9 @@ "dom_task_controller.h", "dom_task_signal.cc", "dom_task_signal.h", + "script_wrappable_task_id.h", + "task_attribution_tracker_impl.cc", + "task_attribution_tracker_impl.h", "task_priority_change_event.cc", "task_priority_change_event.h", ]
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc index 3f5dd8a1..40505db5 100644 --- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc +++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
@@ -12,7 +12,8 @@ #include "third_party/blink/renderer/modules/scheduler/dom_task.h" #include "third_party/blink/renderer/modules/scheduler/dom_task_signal.h" #include "third_party/blink/renderer/platform/bindings/enumeration_base.h" -#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h" #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h" @@ -99,6 +100,40 @@ return resolver->Promise(); } +scheduler::TaskIdType DOMScheduler::taskId(ScriptState* script_state) { + ThreadScheduler* scheduler = ThreadScheduler::Current(); + DCHECK(scheduler); + DCHECK(scheduler->GetTaskAttributionTracker()); + absl::optional<scheduler::TaskId> task_id = + scheduler->GetTaskAttributionTracker()->RunningTaskId(script_state); + // task_id cannot be unset here, as a task has presumably already ran in order + // for this API call to be called. + DCHECK(task_id); + return task_id.value().value(); +} + +AtomicString DOMScheduler::isAncestor(ScriptState* script_state, + scheduler::TaskIdType parentId) { + scheduler::TaskAttributionTracker::AncestorStatus status = + scheduler::TaskAttributionTracker::AncestorStatus::kNotAncestor; + ThreadScheduler* scheduler = ThreadScheduler::Current(); + DCHECK(scheduler); + scheduler::TaskAttributionTracker* tracker = + scheduler->GetTaskAttributionTracker(); + DCHECK(tracker); + status = tracker->IsAncestor(script_state, scheduler::TaskId(parentId)); + switch (status) { + case scheduler::TaskAttributionTracker::AncestorStatus::kAncestor: + return "ancestor"; + case scheduler::TaskAttributionTracker::AncestorStatus::kNotAncestor: + return "not ancestor"; + case scheduler::TaskAttributionTracker::AncestorStatus::kUnknown: + return "unknown"; + } + NOTREACHED(); + return "not reached"; +} + void DOMScheduler::CreateFixedPriorityTaskQueues(ExecutionContext* context) { FrameOrWorkerScheduler* scheduler = context->GetScheduler(); for (size_t i = 0; i < kWebSchedulingPriorityCount; i++) {
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.h b/third_party/blink/renderer/modules/scheduler/dom_scheduler.h index fd9571d0..49d5b4b 100644 --- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.h +++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
@@ -14,6 +14,7 @@ #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_id.h" #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h" #include "third_party/blink/renderer/platform/supplementable.h" #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" @@ -74,6 +75,9 @@ SchedulerPostTaskOptions*, ExceptionState&); + scheduler::TaskIdType taskId(ScriptState*); + AtomicString isAncestor(ScriptState*, scheduler::TaskIdType parent_id); + void ContextDestroyed() override; void Trace(Visitor*) const override;
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler.idl b/third_party/blink/renderer/modules/scheduler/scheduler.idl index 38f2707..411b316 100644 --- a/third_party/blink/renderer/modules/scheduler/scheduler.idl +++ b/third_party/blink/renderer/modules/scheduler/scheduler.idl
@@ -6,10 +6,20 @@ // // currentTaskSignal: // https://github.com/WICG/scheduling-apis/blob/main/explainers/post-task-propagation.md + +// https://docs.google.com/document/d/1_m-h9_KgDMddTS2OFP0CShr4zjU-C-up64DwCrCfBo4 +enum AncestorStatus { + "ancestor", + "not ancestor", + "unknown" +}; + [ Exposed=(Window,Worker), ImplementedAs=DOMScheduler, RuntimeEnabled=WebScheduler ] interface Scheduler { [CallWith=ScriptState, MeasureAs=SchedulerPostTask, RaisesException] Promise<any> postTask(SchedulerPostTaskCallback callback, optional SchedulerPostTaskOptions options = {}); + [RuntimeEnabled=UnexposedTaskIds, CallWith=ScriptState, Exposed=Window] readonly attribute unsigned long taskId; + [RuntimeEnabled=UnexposedTaskIds, CallWith=ScriptState, Exposed=Window] AncestorStatus isAncestor(unsigned long parentId); };
diff --git a/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.h b/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.h new file mode 100644 index 0000000..d75776c --- /dev/null +++ b/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.h
@@ -0,0 +1,24 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_SCRIPT_WRAPPABLE_TASK_ID_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_SCRIPT_WRAPPABLE_TASK_ID_H_ + +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/bindings/v8_set_return_value.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_id.h" + +namespace blink { + +class ScriptWrappableTaskId final : public ScriptWrappable, + public scheduler::TaskId { + DEFINE_WRAPPERTYPEINFO(); + + public: + explicit ScriptWrappableTaskId(const scheduler::TaskId& id) : TaskId(id) {} +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_SCRIPT_WRAPPABLE_TASK_ID_H_
diff --git a/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.idl b/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.idl new file mode 100644 index 0000000..f3b5a4f --- /dev/null +++ b/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.idl
@@ -0,0 +1,10 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This interface of not really web-exposed, and only used to generate a +// ScriptWrappable object, that would pass along the TaskId to V8 and back, as +// continuation embedder data. +interface ScriptWrappableTaskId { + readonly attribute long value; +}; \ No newline at end of file
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc new file mode 100644 index 0000000..75a6c8c --- /dev/null +++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc
@@ -0,0 +1,194 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h" + +#include <memory> +#include <utility> + +#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h" +#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_script_wrappable_task_id.h" +#include "third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.h" +#include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/bindings/to_v8.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" +#include "third_party/blink/renderer/platform/wtf/wtf.h" + +namespace blink::scheduler { + +namespace { + +static unsigned Hash(TaskId id) { + return id.value() % TaskAttributionTrackerImpl::kVectorSize; +} + +} // namespace + +TaskAttributionTrackerImpl::TaskAttributionTrackerImpl() + : next_task_id_(0), v8_adapter_(std::make_unique<V8Adapter>()) {} + +absl::optional<TaskId> TaskAttributionTrackerImpl::RunningTaskId( + ScriptState* script_state) const { + DCHECK(v8_adapter_); + absl::optional<TaskId> task_id = v8_adapter_->GetValue(script_state); + + // V8 embedder state may have no value in the case of a JSPromise that wasn't + // yet resolved. + return task_id ? task_id : running_task_id_; +} + +void TaskAttributionTrackerImpl::InsertTaskIdPair( + TaskId task_id, + absl::optional<TaskId> parent_task_id) { + unsigned task_id_hash = Hash(task_id); + task_container_[task_id_hash] = TaskIdPair(parent_task_id, task_id); +} + +TaskAttributionTrackerImpl::TaskIdPair& +TaskAttributionTrackerImpl::GetTaskIdPairFromTaskContainer(TaskId id) { + unsigned slot = Hash(id); + DCHECK_LT(slot, task_container_.size()); + return (task_container_[slot]); +} + +TaskAttributionTracker::AncestorStatus TaskAttributionTrackerImpl::IsAncestor( + ScriptState* script_state, + TaskId ancestor_id) { + absl::optional<TaskId> current_task_id = RunningTaskId(script_state); + DCHECK(current_task_id); + if (current_task_id.value() == ancestor_id) { + return AncestorStatus::kAncestor; + } + + // Each slot in the `task_container_` contains a struct with a parent and + // current task ID optionals. The loop "climbs" up that task dependency tree + // until it either reaches the "root" task (which has no parent), or until it + // finds a parent, which current task ID doesn't match the one its child + // pointed at, indicating that the parent's slot in the array was overwritten. + // In that case, it's returning the kUnknown value. + const TaskIdPair& current_pair = + GetTaskIdPairFromTaskContainer(current_task_id.value()); + absl::optional<TaskId> parent_id = current_pair.parent; + DCHECK(current_pair.current); + while (parent_id) { + const TaskIdPair& parent_pair = + GetTaskIdPairFromTaskContainer(parent_id.value()); + if (parent_pair.current && parent_pair.current != parent_id) { + // Found a parent slot, but its ID doesn't match what we thought it would + // be. That means we circled around the circular array, and we can no + // longer know the ancestry. + return AncestorStatus::kUnknown; + } + if (parent_id.value() == ancestor_id) { + return AncestorStatus::kAncestor; + } + DCHECK(parent_pair.current.value() != current_task_id.value()); + DCHECK(!parent_pair.parent || parent_pair.parent < parent_id); + parent_id = parent_pair.parent; + } + return AncestorStatus::kNotAncestor; +} + +std::unique_ptr<TaskAttributionTracker::TaskScope> +TaskAttributionTrackerImpl::CreateTaskScope( + ScriptState* script_state, + absl::optional<TaskId> parent_task_id) { + next_task_id_ = next_task_id_.NextTaskId(); + running_task_id_ = next_task_id_; + + InsertTaskIdPair(next_task_id_, parent_task_id); + running_task_ids_.push_back(next_task_id_); + + SaveTaskIdStateInV8(script_state, next_task_id_); + return std::make_unique<TaskScopeImpl>(script_state, this, next_task_id_); +} + +void TaskAttributionTrackerImpl::TaskScopeCompleted(ScriptState* script_state, + TaskId scope_id) { + DCHECK(!running_task_ids_.IsEmpty()); + DCHECK(running_task_ids_.back() == scope_id); + running_task_ids_.pop_back(); + if (!running_task_ids_.IsEmpty()) { + running_task_id_ = running_task_ids_.back(); + } else { + running_task_id_ = absl::nullopt; + } + SaveTaskIdStateInV8(script_state, running_task_id_); +} + +void TaskAttributionTrackerImpl::SaveTaskIdStateInV8( + ScriptState* script_state, + absl::optional<TaskId> task_id) { + DCHECK(v8_adapter_); + v8_adapter_->SetValue(script_state, task_id); +} + +// TaskScope's implementation +////////////////////////////////////// +TaskAttributionTrackerImpl::TaskScopeImpl::TaskScopeImpl( + ScriptState* script_state, + TaskAttributionTrackerImpl* task_tracker, + TaskId scope_task_id) + : task_tracker_(task_tracker), + scope_task_id_(scope_task_id), + script_state_(script_state) {} + +TaskAttributionTrackerImpl::TaskScopeImpl::~TaskScopeImpl() { + task_tracker_->TaskScopeCompleted(script_state_, scope_task_id_); +} + +// V8Adapter's implementation +////////////////////////////////////// +absl::optional<TaskId> TaskAttributionTrackerImpl::V8Adapter::GetValue( + ScriptState* script_state) { + DCHECK(script_state); + if (!script_state->ContextIsValid()) { + return absl::nullopt; + } + + v8::Local<v8::Context> context = script_state->GetContext(); + DCHECK(!context.IsEmpty()); + v8::Local<v8::Value> v8_value = + context->GetContinuationPreservedEmbedderData(); + if (v8_value->IsNullOrUndefined()) { + return absl::nullopt; + } + v8::Isolate* isolate = script_state->GetIsolate(); + DCHECK(isolate); + // If not empty, the value must be a ScriptWrappableTaskId. + NonThrowableExceptionState exception_state; + ScriptWrappableTaskId* script_wrappable_task_id = + NativeValueTraits<ScriptWrappableTaskId>::NativeValue(isolate, v8_value, + exception_state); + DCHECK(script_wrappable_task_id); + return *script_wrappable_task_id; +} + +void TaskAttributionTrackerImpl::V8Adapter::SetValue( + ScriptState* script_state, + absl::optional<TaskId> task_id) { + DCHECK(script_state); + if (!script_state->ContextIsValid()) { + return; + } + ScriptState::Scope scope(script_state); + v8::Isolate* isolate = script_state->GetIsolate(); + DCHECK(isolate); + v8::Local<v8::Context> context = script_state->GetContext(); + DCHECK(!context.IsEmpty()); + + if (task_id) { + ScriptWrappableTaskId* script_wrappable_task_id = + MakeGarbageCollected<ScriptWrappableTaskId>(task_id.value()); + context->SetContinuationPreservedEmbedderData( + ToV8Traits<ScriptWrappableTaskId>::ToV8(script_state, + script_wrappable_task_id) + .ToLocalChecked()); + } else { + context->SetContinuationPreservedEmbedderData(v8::Local<v8::Value>()); + } +} + +} // namespace blink::scheduler
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h new file mode 100644 index 0000000..193bebb --- /dev/null +++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h
@@ -0,0 +1,112 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_TASK_ATTRIBUTION_TRACKER_IMPL_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_TASK_ATTRIBUTION_TRACKER_IMPL_H_ + +#include "third_party/blink/renderer/modules/modules_export.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_id.h" +#include "third_party/blink/renderer/platform/wtf/hash_map.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink::scheduler { + +class TaskAttributionTrackerTest; + +// This class is used to keep track of tasks posted on the main thread and their +// ancestry. It assigns an incerementing ID per task, and gets notified when a +// task is posted, started or ended, and using that, it keeps track of which +// task is the parent of the current task, and stores that info for later. It +// then enables callers to determine if a certain task ID is an ancestor of the +// current task. +class MODULES_EXPORT TaskAttributionTrackerImpl + : public TaskAttributionTracker { + friend class TaskAttributionTrackerTest; + + public: + TaskAttributionTrackerImpl(); + + absl::optional<TaskId> RunningTaskId(ScriptState*) const override; + + AncestorStatus IsAncestor(ScriptState*, TaskId parent_id) override; + + std::unique_ptr<TaskScope> CreateTaskScope( + ScriptState* script_state, + absl::optional<TaskId> parent_task_id) override; + + // The vector size limits the amount of tasks we keep track of. Setting this + // value too small can result in calls to `IsAncestor` returning an `Unknown` + // ancestor status. If this happens a lot in realistic scenarios, we'd need to + // increase this value (at the expense of memory dedicated to task tracking). + static constexpr size_t kVectorSize = 1024; + + void SetRunningTaskId(absl::optional<TaskId> id) { running_task_id_ = id; } + + void TaskScopeCompleted(ScriptState*, TaskId); + + private: + struct TaskIdPair { + TaskIdPair() = default; + TaskIdPair(absl::optional<TaskId> parent_id, + absl::optional<TaskId> current_id) + : parent(parent_id), current(current_id) {} + + explicit operator bool() const { return parent.has_value(); } + absl::optional<TaskId> parent; + absl::optional<TaskId> current; + }; + + class TaskScopeImpl : public TaskScope { + public: + TaskScopeImpl(ScriptState*, TaskAttributionTrackerImpl*, TaskId); + ~TaskScopeImpl() override; + TaskScopeImpl(const TaskScopeImpl&) = delete; + TaskScopeImpl& operator=(const TaskScopeImpl&) = delete; + + private: + TaskAttributionTrackerImpl* task_tracker_; + TaskId scope_task_id_; + Persistent<ScriptState> script_state_; + }; + + class MODULES_EXPORT V8Adapter { + public: + virtual absl::optional<TaskId> GetValue(ScriptState*); + virtual void SetValue(ScriptState*, absl::optional<TaskId>); + virtual ~V8Adapter() = default; + }; + + TaskIdPair& GetTaskIdPairFromTaskContainer(TaskId); + void InsertTaskIdPair(TaskId task_id, absl::optional<TaskId> parent_task_id); + void SaveTaskIdStateInV8(ScriptState*, absl::optional<TaskId>); + + void SetV8AdapterForTesting(std::unique_ptr<V8Adapter> adapter) { + v8_adapter_.swap(adapter); + } + + TaskId next_task_id_; + absl::optional<TaskId> running_task_id_; + WTF::Vector<TaskId> running_task_ids_; + + std::unique_ptr<V8Adapter> v8_adapter_; + + // The task container is a vector of optional TaskIdPairs where its indexes + // are TaskId hashes, and its values are the TaskId of the parent task for the + // TaskId that is resulted in the index. We're using this vector as a circular + // array, where in order to find if task A is an ancestor of task B, we look + // up the value at B's taskId hash position, get its parent, and repeat that + // process until we either find A in the ancestor chain, get no parent task + // (indicating that a task has no parent, so wasn't initiated by another JS + // task), or reach a parent that doesn't have the current ID its child though + // it should have, which indicates that the parent was overwritten by a newer + // task, indicating that we went "full circle". + WTF::Vector<TaskIdPair> task_container_ = + WTF::Vector<TaskIdPair>(kVectorSize); +}; + +} // namespace blink::scheduler + +#endif
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl_test.cc b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl_test.cc new file mode 100644 index 0000000..a5d3744e --- /dev/null +++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl_test.cc
@@ -0,0 +1,157 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_id.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink::scheduler { + +class TaskAttributionTrackerTest : public testing::Test { + protected: + WTF::Vector<std::unique_ptr<TaskAttributionTracker::TaskScope>> task_stack_; + + public: + class MockV8Adapter : public TaskAttributionTrackerImpl::V8Adapter { + public: + absl::optional<TaskId> GetValue(ScriptState*) override { return value_; } + void SetValue(ScriptState*, absl::optional<TaskId> task_id) override { + value_ = task_id; + } + + private: + absl::optional<TaskId> value_; + }; + + void PostTasks(TaskAttributionTrackerImpl& tracker, + unsigned task_number, + unsigned number_to_assert, + TaskIdType parent_to_assert, + bool complete, + TaskId* task_id = nullptr) { + TaskId previous_task_id = TaskId(parent_to_assert); + for (unsigned i = 0; i < task_number; ++i) { + task_stack_.push_back(tracker.CreateTaskScope(nullptr, previous_task_id)); + absl::optional<TaskId> running_task_id = tracker.RunningTaskId(nullptr); + if (i < number_to_assert) { + // Make sure that the parent task is an ancestor. + TaskId parent_task(parent_to_assert); + TaskAttributionTracker::AncestorStatus is_ancestor = + tracker.IsAncestor(nullptr, parent_task); + ASSERT_TRUE(is_ancestor == + TaskAttributionTracker::AncestorStatus::kAncestor); + if (!complete) { + ASSERT_TRUE(tracker.IsAncestor(nullptr, previous_task_id) == + TaskAttributionTracker::AncestorStatus::kAncestor); + } + } + if (task_id) { + *task_id = running_task_id.value(); + } + previous_task_id = running_task_id.value(); + if (complete) { + task_stack_.pop_back(); + } + } + } + + void TestAttributionQueue(unsigned overflow_length, + unsigned asserts_length, + bool nested_tasks_complete, + bool assert_last_task) { + TaskAttributionTrackerImpl tracker; + MockV8ForTracker(tracker); + // Post tasks for half the queue. + unsigned half_queue = TaskAttributionTrackerImpl::kVectorSize / 2; + TaskId task_id(0); + PostTasks(tracker, half_queue, /*number_to_assert=*/0, + /*parent_to_assert=*/0, + /*complete=*/true, &task_id); + // Verify that the ID of the last task that ran is what we expect it to be. + ASSERT_EQ(half_queue, task_id.value()); + // Start the parent task, but don't complete it. + task_stack_.push_back( + tracker.CreateTaskScope(nullptr, tracker.RunningTaskId(nullptr))); + // Get its ID. + TaskId parent_task_id = tracker.RunningTaskId(nullptr).value(); + // Post |overflow_length| tasks. + PostTasks(tracker, overflow_length, asserts_length, parent_task_id.value(), + nested_tasks_complete); + if (assert_last_task && ((overflow_length + half_queue) > + TaskAttributionTrackerImpl::kVectorSize)) { + // Post another task. + task_stack_.push_back( + tracker.CreateTaskScope(nullptr, tracker.RunningTaskId(nullptr))); + // Since it goes beyond the queue length and the parent task was + // overwritten, we cannot track ancestry. + ASSERT_TRUE(tracker.IsAncestor(nullptr, parent_task_id) != + TaskAttributionTracker::AncestorStatus::kAncestor); + if (nested_tasks_complete) { + task_stack_.pop_back(); + } + } + // Complete all the tasks + while (!task_stack_.IsEmpty()) { + task_stack_.pop_back(); + } + } + + void MockV8ForTracker(TaskAttributionTrackerImpl& tracker) { + tracker.SetV8AdapterForTesting(std::make_unique<MockV8Adapter>()); + } +}; + +TEST_F(TaskAttributionTrackerTest, TrackTaskLargerThanQueue) { + TestAttributionQueue( + /*overflow_length=*/TaskAttributionTrackerImpl::kVectorSize - 1, + /*asserts_length=*/TaskAttributionTrackerImpl::kVectorSize - 1, + /*nested_tasks_complete=*/true, + /*assert_last_task=*/true); +} + +TEST_F(TaskAttributionTrackerTest, TrackTwoTasksAcrossTheQueue) { + TestAttributionQueue( + /*overflow_length=*/TaskAttributionTrackerImpl::kVectorSize + 100, + /*asserts_length=*/TaskAttributionTrackerImpl::kVectorSize - 1, + /*nested_tasks_complete=*/true, + /*assert_last_task=*/true); +} + +TEST_F(TaskAttributionTrackerTest, CausalityChain) { + TestAttributionQueue(/*overflow_length=*/100, /*asserts_length=*/100, + /*nested_tasks_complete=*/false, + /*assert_last_task=*/false); +} + +TEST_F(TaskAttributionTrackerTest, CausalityChainOverflow) { + TestAttributionQueue( + /*overflow_length=*/TaskAttributionTrackerImpl::kVectorSize - 1, + /*asserts_length=*/TaskAttributionTrackerImpl::kVectorSize - 1, + /*nested_tasks_complete=*/false, + /*assert_last_task=*/true); +} + +TEST_F(TaskAttributionTrackerTest, NotAncestor) { + TaskAttributionTrackerImpl tracker; + MockV8ForTracker(tracker); + TaskId first_task_id(0); + + // Start a task, get its ID and complete it. + { + auto scope = tracker.CreateTaskScope(nullptr, absl::nullopt); + first_task_id = tracker.RunningTaskId(nullptr).value(); + } + + // Start an incomplete task. + auto scope = tracker.CreateTaskScope(nullptr, absl::nullopt); + // Make sure that the first task is not an ancestor. + ASSERT_TRUE(tracker.IsAncestor(nullptr, first_task_id) == + TaskAttributionTracker::AncestorStatus::kNotAncestor); +} + +} // namespace blink::scheduler
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc index fd0f1f1f..7e9e2c3 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -5579,6 +5579,14 @@ bool have_source_canvas_webgl_context = source_canvas_webgl_context; DCHECK(have_source_image ^ have_source_canvas_webgl_context); + if (source_canvas_webgl_context && + source_canvas_webgl_context->isContextLost()) { + SynthesizeGLError(GL_INVALID_OPERATION, + GetTexImageFunctionName(function_id), + "Can't upload a texture from a lost WebGL context."); + return; + } + int width = source_sub_rectangle.width(); int height = source_sub_rectangle.height();
diff --git a/third_party/blink/renderer/platform/bindings/callback_function_base.cc b/third_party/blink/renderer/platform/bindings/callback_function_base.cc index 78bfb58..38010b7f 100644 --- a/third_party/blink/renderer/platform/bindings/callback_function_base.cc +++ b/third_party/blink/renderer/platform/bindings/callback_function_base.cc
@@ -6,6 +6,8 @@ #include "third_party/blink/renderer/platform/bindings/binding_security_for_platform.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" namespace blink { @@ -28,6 +30,10 @@ BindingSecurityForPlatform::ErrorReportOption::kDoNotReport)) { callback_relevant_script_state_ = ScriptState::From(creation_context.ToLocalChecked()); + if (auto* tracker = + ThreadScheduler::Current()->GetTaskAttributionTracker()) { + parent_task_id_ = tracker->RunningTaskId(callback_relevant_script_state_); + } } }
diff --git a/third_party/blink/renderer/platform/bindings/callback_function_base.h b/third_party/blink/renderer/platform/bindings/callback_function_base.h index dcede63..3ee271c 100644 --- a/third_party/blink/renderer/platform/bindings/callback_function_base.h +++ b/third_party/blink/renderer/platform/bindings/callback_function_base.h
@@ -10,6 +10,7 @@ #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_id.h" namespace blink { @@ -88,6 +89,10 @@ callback_function_.Reset(); } + absl::optional<scheduler::TaskId> GetParentTaskId() const { + return parent_task_id_; + } + protected: explicit CallbackFunctionBase(v8::Local<v8::Object>); @@ -107,6 +112,8 @@ // converted to an IDL value. // https://webidl.spec.whatwg.org/#dfn-callback-context Member<ScriptState> incumbent_script_state_; + + absl::optional<scheduler::TaskId> parent_task_id_; }; } // namespace blink
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.cc b/third_party/blink/renderer/platform/bindings/callback_interface_base.cc index 55cc3ad..cfce04f 100644 --- a/third_party/blink/renderer/platform/bindings/callback_interface_base.cc +++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
@@ -6,6 +6,8 @@ #include "third_party/blink/renderer/platform/bindings/binding_security_for_platform.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" namespace blink { @@ -31,6 +33,10 @@ BindingSecurityForPlatform::ErrorReportOption::kDoNotReport)) { callback_relevant_script_state_ = ScriptState::From(creation_context.ToLocalChecked()); + if (auto* tracker = + ThreadScheduler::Current()->GetTaskAttributionTracker()) { + parent_task_id_ = tracker->RunningTaskId(callback_relevant_script_state_); + } } }
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.h b/third_party/blink/renderer/platform/bindings/callback_interface_base.h index 43cf545..6d84207c 100644 --- a/third_party/blink/renderer/platform/bindings/callback_interface_base.h +++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.h
@@ -9,6 +9,7 @@ #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_id.h" namespace blink { @@ -82,6 +83,10 @@ DOMWrapperWorld& GetWorld() const { return incumbent_script_state_->World(); } + absl::optional<scheduler::TaskId> GetParentTaskId() const { + return parent_task_id_; + } + protected: explicit CallbackInterfaceBase(v8::Local<v8::Object> callback_object, SingleOperationOrNot); @@ -98,6 +103,8 @@ // converted to an IDL value. // https://webidl.spec.whatwg.org/#dfn-callback-context Member<ScriptState> incumbent_script_state_; + + absl::optional<scheduler::TaskId> parent_task_id_; }; } // namespace blink
diff --git a/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc b/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc index cd220c5..098e4495 100644 --- a/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc +++ b/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
@@ -225,21 +225,6 @@ radii_.OutsetForMarginOrShadow(size); } -void FloatRoundedRect::InflateWithRadii(int size) { - gfx::RectF old = rect_; - - rect_.Outset(size); - // Considering the inflation factor of shorter size to scale the radii seems - // appropriate here - float factor; - if (rect_.width() < rect_.height()) - factor = old.width() ? (float)rect_.width() / old.width() : int(0); - else - factor = old.height() ? (float)rect_.height() / old.height() : int(0); - - radii_.Scale(factor); -} - bool FloatRoundedRect::IntersectsQuad(const gfx::QuadF& quad) const { if (!quad.IntersectsRect(rect_)) return false;
diff --git a/third_party/blink/renderer/platform/geometry/float_rounded_rect.h b/third_party/blink/renderer/platform/geometry/float_rounded_rect.h index 68eb652..f4b088e4 100644 --- a/third_party/blink/renderer/platform/geometry/float_rounded_rect.h +++ b/third_party/blink/renderer/platform/geometry/float_rounded_rect.h
@@ -138,8 +138,6 @@ void SetRadii(const Radii& radii) { radii_ = radii; } void Move(const gfx::Vector2dF& offset) { rect_.Offset(offset); } - // TODO(wangxianzhu): Consider merging this into Outset(). - void InflateWithRadii(int size); // Inflates/shrinks the rounded rect by the specified amount on each side and // corner. Zero widths and heights of radii are kept zero so that sharp
diff --git a/third_party/blink/renderer/platform/media/key_system_config_selector.cc b/third_party/blink/renderer/platform/media/key_system_config_selector.cc index fa06a896..1e90c89c 100644 --- a/third_party/blink/renderer/platform/media/key_system_config_selector.cc +++ b/third_party/blink/renderer/platform/media/key_system_config_selector.cc
@@ -992,9 +992,6 @@ const WebString& key_system, const WebVector<WebMediaKeySystemConfiguration>& candidate_configurations, SelectConfigCB cb) { - DCHECK(key_systems_->IsUpToDate()) - << "The caller must make sure the Key Systems are up to date"; - // Continued from requestMediaKeySystemAccess(), step 6, from // https://w3c.github.io/encrypted-media/#requestmediakeysystemaccess //
diff --git a/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc b/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc index 07e56506..3ad3405 100644 --- a/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc +++ b/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc
@@ -199,12 +199,10 @@ ~FakeKeySystems() override = default; void UpdateIfNeeded(base::OnceClosure done_cb) override { - NOTREACHED() << "Not needed since IsUpToDate() always returns true"; + // Call the callback directly since it's always up to date. std::move(done_cb).Run(); } - bool IsUpToDate() override { return true; } - std::string GetBaseKeySystemName( const std::string& key_system) const override { DCHECK(IsSupportedKeySystem(key_system));
diff --git a/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc b/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc index 6a824434..cbc8011 100644 --- a/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc +++ b/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc
@@ -113,15 +113,10 @@ WebEncryptedMediaRequest request) { GetReporter(request.KeySystem())->ReportRequested(); - if (!key_systems_->IsUpToDate()) { - pending_requests_.push_back(std::move(request)); - key_systems_->UpdateIfNeeded( - base::BindOnce(&WebEncryptedMediaClientImpl::OnKeySystemsUpdated, - weak_factory_.GetWeakPtr())); - return; - } - - SelectConfig(request); + pending_requests_.push_back(std::move(request)); + key_systems_->UpdateIfNeeded( + base::BindOnce(&WebEncryptedMediaClientImpl::OnKeySystemsUpdated, + weak_factory_.GetWeakPtr())); } void WebEncryptedMediaClientImpl::CreateCdm(
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc b/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc index 34298fe3..5b4c8433 100644 --- a/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc +++ b/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc
@@ -129,22 +129,6 @@ return device().matched_output_device_id.has_value(); } -bool MediaStreamAudioSource::AllTracksAreDisabled() { - DCHECK(GetTaskRunner()->BelongsToCurrentThread()); - - unsigned int num_disabled_tracks = 0; - Vector<MediaStreamAudioTrack*> audio_tracks; - deliverer_.GetConsumerList(&audio_tracks); - for (MediaStreamAudioTrack* track : audio_tracks) { - if (!track->IsEnabled()) - ++num_disabled_tracks; - } - LogMessage(base::StringPrintf("%s => (%u of %u tracks are disabled))", - __func__, num_disabled_tracks, - audio_tracks.size())); - return (num_disabled_tracks == audio_tracks.size()); -} - void* MediaStreamAudioSource::GetClassIdentifier() const { return nullptr; }
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h b/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h index 66912cb8..7867fa9 100644 --- a/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h +++ b/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h
@@ -103,9 +103,6 @@ bool disable_local_echo() const { return disable_local_echo_; } bool RenderToAssociatedSinkEnabled() const; - // Checks all tracks acting as consumers and returns true if all are disabled. - bool AllTracksAreDisabled(); - // Returns a unique class identifier. Some subclasses override and use this // method to provide safe down-casting to their type. virtual void* GetClassIdentifier() const;
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc index 5d26751..2725f226 100644 --- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc +++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
@@ -1196,9 +1196,9 @@ // All non-native frames require a copy because we can't tell if non-copy // conditions are met. - bool requires_copy = + bool requires_copy_or_scale = buffer->type() != webrtc::VideoFrameBuffer::Type::kNative; - if (!requires_copy) { + if (!requires_copy_or_scale) { const WebRtcVideoFrameAdapter* frame_adapter = static_cast<WebRtcVideoFrameAdapter*>(buffer.get()); frame = frame_adapter->getMediaVideoFrame(); @@ -1206,25 +1206,32 @@ const bool is_shmem_frame = storage == media::VideoFrame::STORAGE_SHMEM; const bool is_gmb_frame = storage == media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER; - requires_copy = + requires_copy_or_scale = RequiresSizeChange(*frame) || !(is_shmem_frame || is_gmb_frame); } - if (requires_copy) { + if (requires_copy_or_scale) { const base::TimeDelta timestamp = frame ? frame->timestamp() : base::Milliseconds(next_frame->ntp_time_ms()); - // TODO(https://crbug.com/1194500): Android (e.g. android-pie-arm64-rel) - // and CrOS does not support the optimzed path, perhaps due to not - // supporting STORAGE_GPU_MEMORY_BUFFER or NV12? When this is fixed, remove - // the special casing on platform and the legacy code path. - bool optimized_scaling = + // Native buffer scaling is performed by WebRtcVideoFrameAdapter, which may + // be more efficient in some cases. E.g. avoiding I420 conversion or scaling + // from a middle layer instead of top layer. + // + // Native buffer scaling is only supported when `input_frame_coded_size_` + // and `input_visible_size_` strides match. This ensures the strides of the + // frame that we pass to the encoder fits the input requirements. + bool native_buffer_scaling = #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS) - buffer->type() == webrtc::VideoFrameBuffer::Type::kNative; + buffer->type() == webrtc::VideoFrameBuffer::Type::kNative && + input_frame_coded_size_.width() == input_visible_size_.width(); #else + // TODO(https://crbug.com/1194500): Android (e.g. android-pie-arm64-rel) + // and CrOS does not support the native buffer scaling path. Investigate + // why and find a way to enable it, if possible. false; #endif - if (optimized_scaling) { + if (native_buffer_scaling) { DCHECK_EQ(buffer->type(), webrtc::VideoFrameBuffer::Type::kNative); auto scaled_buffer = buffer->Scale(input_visible_size_.width(), input_visible_size_.height()); @@ -1250,8 +1257,6 @@ return; } } else { - // TODO(https://crbug.com/1194500): Remove this code path in favor of the - // above code path. This will allow us to remove |input_buffers_|. std::pair<base::UnsafeSharedMemoryRegion, base::WritableSharedMemoryMapping>* input_buffer = input_buffers_[index].get(); @@ -1270,7 +1275,6 @@ // Do a strided copy and scale (if necessary) the input frame to match // the input requirements for the encoder. - // TODO(sheu): Support zero-copy from WebRTC. http://crbug.com/269312 // TODO(magjed): Downscale with an image pyramid instead. rtc::scoped_refptr<webrtc::I420BufferInterface> i420_buffer = next_frame->video_frame_buffer()->ToI420();
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 3f68fb4..7505a8d 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1985,13 +1985,6 @@ origin_trial_feature_name: "RtcAudioJitterBufferRtxHandling", status: "experimental", }, - // Reverse Origin Trial for Plan B. When enabled through the Origin Trial, - // Plan B is available beyond the initial removal date of Plan B (M93). - { - name: "RTCExtendDeadlineForPlanBRemoval", - origin_trial_feature_name: "RTCExtendDeadlineForPlanBRemoval", - status: "test", - }, // Enables the use of the RTCIceTransport with extensions. { name: "RTCIceTransportExtension", @@ -2295,6 +2288,9 @@ name: "TimeZoneChangeEvent", status: "experimental", }, + { + name: "TopicsAPI", + }, // This feature allows touch dragging and a context menu to occur // simultaneously, with the assumption that the menu is non-modal. Without // this feature, a long-press touch gesture can start either a drag or a @@ -2367,6 +2363,9 @@ name: "UnderlineOffsetThickness", status: "stable", }, + { + name: "UnexposedTaskIds", + }, // This is a reverse OT used for a phased deprecation, on desktop // https://crbug.com/1071424 {
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn index c305569..3082f88a 100644 --- a/third_party/blink/renderer/platform/scheduler/BUILD.gn +++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -126,6 +126,8 @@ "public/rail_mode_observer.h", "public/scheduling_lifecycle_state.h", "public/scheduling_policy.h", + "public/task_attribution_tracker.h", + "public/task_id.h", "public/thread.h", "public/thread_cpu_throttler.h", "public/thread_scheduler.h",
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h index 374727c..b176a68 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -44,6 +44,7 @@ #include "third_party/blink/renderer/platform/scheduler/main_thread/user_model.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/rail_mode_observer.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -266,6 +267,16 @@ scoped_refptr<base::SingleThreadTaskRunner> VirtualTimeControlTaskRunner(); + TaskAttributionTracker* GetTaskAttributionTracker() override { + return main_thread_only().task_attribution_tracker.get(); + } + + void InitializeTaskAttributionTracker( + std::unique_ptr<TaskAttributionTracker> tracker) override { + DCHECK(!main_thread_only().task_attribution_tracker); + main_thread_only().task_attribution_tracker = std::move(tracker); + } + // Returns a new task queue created with given params. scoped_refptr<MainThreadTaskQueue> NewTaskQueue( const MainThreadTaskQueue::QueueCreationParams& params); @@ -909,6 +920,8 @@ WTF::Vector<AgentGroupSchedulerScope> agent_group_scheduler_scope_stack; std::unique_ptr<power_scheduler::PowerModeVoter> audible_power_mode_voter; + + std::unique_ptr<TaskAttributionTracker> task_attribution_tracker; }; struct AnyThread {
diff --git a/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h b/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h new file mode 100644 index 0000000..af303a9 --- /dev/null +++ b/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h
@@ -0,0 +1,58 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ATTRIBUTION_TRACKER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ATTRIBUTION_TRACKER_H_ + +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_id.h" + +namespace blink { +class ScriptState; +} // namespace blink + +namespace blink::scheduler { + +// This public interface enables platform/ and core/ callers to create a task +// scope on the one hand, and check on the ID of the currently running task as +// well as its ancestry on the other. +class PLATFORM_EXPORT TaskAttributionTracker { + public: + // Return value for encestry related queries. + enum class AncestorStatus { + kAncestor, + kNotAncestor, + kUnknown, + }; + + // A class maintaining the scope of the current task. Keeping it alive ensures + // that the current task is counted as a continuous one. + class TaskScope { + public: + virtual ~TaskScope() = default; + TaskScope(const TaskScope&) = delete; + TaskScope& operator=(const TaskScope&) = delete; + + protected: + TaskScope() = default; + }; + + virtual ~TaskAttributionTracker() = default; + + // Create a new task scope. + virtual std::unique_ptr<TaskScope> CreateTaskScope( + ScriptState*, + absl::optional<TaskId> parent_task_id) = 0; + + // Get the ID of the currently running task. + virtual absl::optional<TaskId> RunningTaskId(ScriptState*) const = 0; + // Check for ancestry of the currently running task against an input + // |parentId|. + virtual AncestorStatus IsAncestor(ScriptState*, TaskId parentId) = 0; +}; + +} // namespace blink::scheduler + +#endif
diff --git a/third_party/blink/renderer/platform/scheduler/public/task_id.h b/third_party/blink/renderer/platform/scheduler/public/task_id.h new file mode 100644 index 0000000..84267f9 --- /dev/null +++ b/third_party/blink/renderer/platform/scheduler/public/task_id.h
@@ -0,0 +1,35 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ID_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ID_H_ + +#include <cstdint> +#include "base/types/strong_alias.h" + +namespace blink::scheduler { + +using TaskIdType = uint32_t; + +// TaskId represents the ID of a task, enabling comparison and incrementation +// operations on it, while abstracting the underlying value from callers. +class TaskId { + public: + explicit TaskId(TaskIdType value) : value_(value) {} + TaskId(const TaskId&) = default; + TaskId& operator=(const TaskId&) = default; + scheduler::TaskIdType value() const { return value_; } + + bool operator==(const TaskId& id) const { return id.value_ == value_; } + bool operator!=(const TaskId& id) const { return id.value_ != value_; } + bool operator<(const TaskId& id) const { return value_ < id.value_; } + TaskId NextTaskId() const { return TaskId(value_ + 1); } + + private: + TaskIdType value_; +}; + +} // namespace blink::scheduler + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ID_H_
diff --git a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h index dab565b7..32506d1 100644 --- a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h +++ b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
@@ -14,6 +14,7 @@ #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h" #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" #include "third_party/blink/renderer/platform/scheduler/public/thread.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -154,10 +155,19 @@ // Associates |isolate| to the scheduler. virtual void SetV8Isolate(v8::Isolate* isolate) = 0; + // Returns the scheduler's TaskAttributionTracker is we're running on the main + // thread, or a nullptr otherwise. + virtual scheduler::TaskAttributionTracker* GetTaskAttributionTracker() { + return nullptr; + } + // Test helpers. virtual scheduler::NonMainThreadSchedulerImpl* AsNonMainThreadScheduler() = 0; + virtual void InitializeTaskAttributionTracker( + std::unique_ptr<scheduler::TaskAttributionTracker>) {} + private: // For GetWebMainThreadScheduler(). friend class scheduler::WebThreadScheduler;
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py index 6fef402a..ed1bd01 100755 --- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py +++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -560,6 +560,9 @@ # nested in the blink namespace. 'internal::.+', + # TODO(crbug.com/1296161): Remove this when the CHIPS OT ends. + "net::features::kPartitionedCookiesBypassOriginTrial", + # HTTP structured headers 'net::structured_headers::.+',
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer.py b/third_party/blink/tools/blinkpy/w3c/test_importer.py index 85f12b4..f15d5d8 100644 --- a/third_party/blink/tools/blinkpy/w3c/test_importer.py +++ b/third_party/blink/tools/blinkpy/w3c/test_importer.py
@@ -39,7 +39,7 @@ TIMEOUT_SECONDS = 210 * 60 # Sheriff calendar URL, used for getting the ecosystem infra sheriff to cc. -ROTATIONS_URL = 'https://chrome-ops-rotation-proxy.appspot.com/current/grotation:chrome-ecosystem-infra' +ROTATIONS_URL = 'https://chrome-ops-rotation-proxy.appspot.com/current/grotation:chromium-wpt-two-way-sync' SHERIFF_EMAIL_FALLBACK = 'weizhong@google.com' RUBBER_STAMPER_BOT = 'rubber-stamper@appspot.gserviceaccount.com'
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng index ef22101d..5154211 100644 --- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng +++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -13,6 +13,10 @@ # Tests that fail in legacy but pass in NG # ====== New tests from wpt-importer added here ====== +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-cross-origin-subframe-navigation.html [ Timeout ] +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-main-frame-navigation.html [ Timeout ] +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-remove-subframe.html [ Timeout ] +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-same-origin-subframe-navigation.html [ Timeout ] crbug.com/626703 virtual/prerender/external/wpt/speculation-rules/prerender/opt-out.html [ Crash ] crbug.com/626703 virtual/no-forced-frame-updates/external/wpt/html/dom/render-blocking/header-inserted-preload-link.tentative.html [ Timeout ] crbug.com/626703 external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html [ Timeout ]
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials index 9e9bb4d..0caa6aa 100644 --- a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials +++ b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
@@ -47,6 +47,10 @@ crbug.com/1209223 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html [ Failure ] # ====== New tests from wpt-importer added here ====== +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-cross-origin-subframe-navigation.html [ Timeout ] +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-main-frame-navigation.html [ Timeout ] +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-remove-subframe.html [ Timeout ] +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-same-origin-subframe-navigation.html [ Timeout ] crbug.com/626703 virtual/prerender/external/wpt/speculation-rules/prerender/opt-out.html [ Crash ] crbug.com/626703 virtual/no-forced-frame-updates/external/wpt/html/dom/render-blocking/header-inserted-preload-link.tentative.html [ Timeout ] crbug.com/626703 external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html [ Timeout ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests index 3b1d9bfc..2c692c0 100644 --- a/third_party/blink/web_tests/SlowTests +++ b/third_party/blink/web_tests/SlowTests
@@ -69,6 +69,10 @@ # Most DevTools tests are slow in Debug. webkit.org/b/90488 [ Debug ] http/tests/devtools/* [ Slow ] webkit.org/b/90488 [ Debug ] inspector-protocol/* [ Slow ] +webkit.org/b/90488 [ Debug ] http/tests/inspector-protocol/* [ Slow ] + +# Conversion tests are slow on Windows +crbug.com/1306848 [ Win ] http/tests/inspector-protocol/conversion/* [ Slow ] # DevTools tests cause flakes on slow hardware in Release as well. crbug.com/1063051 [ Release ] http/tests/devtools/* [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index d137b67..a4486d4 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1633,6 +1633,18 @@ virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/flex-container-fragmentation-007.tentative.html [ Pass ] virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-015.html [ Pass ] virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-016.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html [ Pass ] +virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html [ Pass ] virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-007.html [ Pass ] virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-008.html [ Pass ] virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-010.html [ Pass ] @@ -3444,6 +3456,13 @@ crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ] # ====== New tests from wpt-importer added here ====== +crbug.com/626703 external/wpt/css/css-grid/subgrid/subgrid-stretch.html [ Failure ] +crbug.com/626703 [ Mac11 ] external/wpt/css/css-animations/parsing/animation-computed.html [ Failure Timeout ] +crbug.com/626703 [ Mac11 ] virtual/threaded/external/wpt/css/css-animations/parsing/animation-computed.html [ Failure Timeout ] +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-cross-origin-subframe-navigation.html [ Timeout ] +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-main-frame-navigation.html [ Timeout ] +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-remove-subframe.html [ Timeout ] +crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-same-origin-subframe-navigation.html [ Timeout ] crbug.com/626703 [ Linux ] external/wpt/css/css-backgrounds/background-clip/clip-text-flex.html [ Failure ] crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-backgrounds/background-clip/clip-text-flex.html [ Failure ] crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-backgrounds/background-clip/clip-text-flex.html [ Failure ] @@ -4290,6 +4309,10 @@ crbug.com/626703 external/wpt/screen-orientation/orientation-reading.html [ Timeout ] crbug.com/626703 external/wpt/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-closeable.html [ Failure ] +# Tests that rely on a non-exposed test-only API. Tested in virtual/task-tracking +wpt_internal/task-tracking/* [ Skip Failure ] +virtual/task-tracking/* [ Pass ] + # navigation.sub.html fails or times out when run with run_web_tests.py but passes with run_wpt_tests.py crbug.com/626703 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/navigation.sub.html?encoding=windows-1252 [ Failure Timeout ] crbug.com/626703 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/navigation.sub.html?encoding=x-cp1251 [ Failure Timeout ] @@ -4402,6 +4425,18 @@ crbug.com/660611 external/wpt/css/css-break/flexbox/flex-container-fragmentation-007.tentative.html [ Failure ] crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-015.html [ Failure ] crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-016.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html [ Failure ] +crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html [ Failure ] crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-007.html [ Failure ] crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-008.html [ Failure ] crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-010.html [ Failure ] @@ -6533,10 +6568,6 @@ crbug.com/1205659 [ Mac ] storage/indexeddb/empty-blob-file.html [ Pass Timeout ] crbug.com/1205673 [ Linux ] virtual/threaded-prefer-compositing/fast/scroll-snap/snaps-after-wheel-scrolling-single-tick.html [ Failure Pass ] -# Browser Infra 2021-05-04 -# Re-enable once all Linux CI/CQ builders migrated to Bionic -crbug.com/1200134 [ Linux ] fast/gradients/unprefixed-repeating-gradient-color-hint.html [ Failure Pass ] - # Sheriff 2021-05-05 crbug.com/1205796 [ Mac10.12 ] external/wpt/html/dom/idlharness.https.html?include=HTML.* [ Failure ] crbug.com/1205796 [ Mac10.14 ] external/wpt/html/dom/idlharness.https.html?include=HTML.* [ Failure ] @@ -7683,9 +7714,6 @@ crbug.com/1296179 [ Mac Release ] virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/service-worker-background-fetch.https.window.html [ Failure Pass ] crbug.com/1296179 [ Linux Release ] virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/service-worker-background-fetch.https.window.html [ Failure Pass ] -# Flaky due to https://crrev.com/c/3453430 -crbug.com/1298944 virtual/partitioned-cookies/external/wpt/cookies/partitioned-cookies/partitioned-cookies.tentative.https.html [ Failure Pass ] - # Fails flakily on Win crbug.com/1173382 [ Win ] virtual/stable/http/tests/navigation/replacestate-base-legal.html [ Failure Pass ] @@ -7699,6 +7727,10 @@ crbug.com/1299948 [ Mac ] external/wpt/css/css-tables/crashtests/textarea-intrinsic-size-crash.html [ Pass Timeout ] crbug.com/1299972 [ Linux ] screen_orientation/screenorientation-unsupported-no-crash.html [ Failure Pass Timeout ] +# Until Plan B is removed on Fuchsia, allow these tests to fail to allow contradictory expectations. +crbug.com/1302249 [ Fuchsia ] external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported.html [ Failure Pass ] +crbug.com/1302249 [ Fuchsia ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported.html [ Failure Pass ] + # Disable flaky test for further investigation crbug.com/1229801 http/tests/devtools/elements/css-rule-hover-highlights-selectors.js [ Failure Pass ] @@ -7730,5 +7762,7 @@ crbug.com/1298633 http/tests/inspector-protocol/network/cross-origin-isolation/coep-load-error-reporting-worker.js [ Pass Timeout ] # Sheriff 2022-03-16 -crbug.com/1306848 http/tests/inspector-protocol/conversion/attribution-invalid-data.js [ Failure Timeout Pass ] crbug.com/1306770 http/tests/inspector-protocol/network/interception-download.js [ Failure Pass ] + +# Sheriff 2022-03-18 +crbug.com/1290040 [ Linux ] external/wpt/service-workers/service-worker/same-site-cookies.https.html [ Skip ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index 37251c11..5fb67156 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -557,7 +557,8 @@ }, { "prefix": "dark-color-scheme", - "bases": ["external/wpt/css/css-color-adjust/rendering/dark-color-scheme", + "bases": ["external/wpt/css/css-color/system-color-consistency.html", + "external/wpt/css/css-color-adjust/rendering/dark-color-scheme", "fast/forms/color-scheme", "fast/forms/validation-bubble-appearance-edge.html", "fast/forms/validation-bubble-appearance-wrap.html", @@ -857,12 +858,12 @@ "external/wpt/html/cross-origin-embedder-policy/anonymous-iframe/", "http/tests/cookies/partitioned-cookies", "http/tests/inspector-protocol/network/"], - "args": ["--enable-features=PartitionedCookies"] + "args": ["--enable-features=PartitionedCookies,PartitionedCookiesBypassOriginTrial"] }, { "prefix": "partitioned-cookies-first-party-sets", "bases": ["http/tests/inspector-protocol/network/"], - "args": ["--enable-features=PartitionedCookies", + "args": ["--enable-features=PartitionedCookies,PartitionedCookiesBypassOriginTrial", "--use-first-party-set=https://devtools.test,https://example.test"] }, { @@ -1004,6 +1005,13 @@ "args": ["--enable-features=SharedStorageAPI,FencedFrames:implementation_type/shadow_dom"] }, { + "prefix": "task-tracking", + "bases": [ + "wpt_internal/task-tracking" + ], + "args": ["--enable-blink-features=UnexposedTaskIds"] + }, + { "prefix": "fenced-frame-mparch", "bases": [ "http/tests/fenced_frame", @@ -1011,7 +1019,7 @@ "http/tests/inspector-protocol/fenced-frame", "external/wpt/html/cross-origin-embedder-policy/anonymous-iframe" ], - "args": ["--enable-features=FencedFrames:implementation_type/mparch,Prerender2,PartitionedCookies"] + "args": ["--enable-features=FencedFrames:implementation_type/mparch,Prerender2,PartitionedCookies,PartitionedCookiesBypassOriginTrial"] }, { "prefix": "fenced-frame-shadow-dom", @@ -1020,7 +1028,7 @@ "wpt_internal/fenced_frame", "external/wpt/html/cross-origin-embedder-policy/anonymous-iframe" ], - "args": ["--enable-features=FencedFrames:implementation_type/shadow_dom,Prerender2,PartitionedCookies"] + "args": ["--enable-features=FencedFrames:implementation_type/shadow_dom,Prerender2,PartitionedCookies,PartitionedCookiesBypassOriginTrial"] }, { "prefix": "disable-custom-element-default-style",
diff --git a/third_party/blink/web_tests/compositing/overflow/border-radius-above-composited-subframe-expected.png b/third_party/blink/web_tests/compositing/overflow/border-radius-above-composited-subframe-expected.png index a26c8cfe..1de1030 100644 --- a/third_party/blink/web_tests/compositing/overflow/border-radius-above-composited-subframe-expected.png +++ b/third_party/blink/web_tests/compositing/overflow/border-radius-above-composited-subframe-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/compositing/overflow/border-radius-composited-subframe-expected.png b/third_party/blink/web_tests/compositing/overflow/border-radius-composited-subframe-expected.png index ca07de2..496f2b6 100644 --- a/third_party/blink/web_tests/compositing/overflow/border-radius-composited-subframe-expected.png +++ b/third_party/blink/web_tests/compositing/overflow/border-radius-composited-subframe-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version index 75be44f7..662ad985 100644 --- a/third_party/blink/web_tests/external/Version +++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@ -Version: afd66ac5976672821b2788cd5f6ae57701240308 +Version: 378ce87107fcdc462957e337f41731226f66698f
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 6724e9a..9656bee 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -383,6 +383,13 @@ {} ] ], + "break-before-float-after-line-after-floats-crash.html": [ + "9066cd77ecdc74037b9e75016fe0ec5975e399b0", + [ + null, + {} + ] + ], "break-before-with-no-fragmentation-crash.html": [ "fb80ec45bceec093481fa54513c606c5952628b1", [ @@ -525,6 +532,13 @@ {} ] ], + "line-and-fragmentainer-break-before-float-crash.html": [ + "14bce6b80c54453edefc51ceb9b210b46bc42719", + [ + null, + {} + ] + ], "nested-oof-in-multicol-crash.html": [ "6b2f81005d3e41642c6a545050d7c5536bae6af3", [ @@ -591,195 +605,197 @@ ] ], "container-queries": { - "canvas-as-container-crash.html": [ - "8d825606561884090c74b63e3d7bdfef19709169", - [ - null, - {} + "crashtests": { + "canvas-as-container-crash.html": [ + "8d825606561884090c74b63e3d7bdfef19709169", + [ + null, + {} + ] + ], + "chrome-bug-1289718-000-crash.html": [ + "5dab634a2a2586722ddbd0a9cf9eea8e31001dc6", + [ + null, + {} + ] + ], + "chrome-bug-1289718-001-crash.html": [ + "7236714183fd2130c9d12ffaf5226376d3ee7e58", + [ + null, + {} + ] + ], + "columns-in-table-001-crash.html": [ + "fe421500dade86b6fedbbf6289a668dcc5174f45", + [ + null, + {} + ] + ], + "container-type-change-chrome-legacy-crash.html": [ + "609142a2c5f7fa16f241b6ac06842a62ed68be8c", + [ + null, + {} + ] + ], + "flex-in-columns-000-crash.html": [ + "e7b789345c807362888a651952486b7e8fcc8ba5", + [ + null, + {} + ] + ], + "flex-in-columns-001-crash.html": [ + "0c0648c15bc1f6873bd6da9d868267cbc7d1df23", + [ + null, + {} + ] + ], + "flex-in-columns-002-crash.html": [ + "ef3052d2c98656bf8996b394cc5f66589e04421f", + [ + null, + {} + ] + ], + "flex-in-columns-003-crash.html": [ + "a86f25a773dbbcd1f622a0b3c32d00d8e845293c", + [ + null, + {} + ] + ], + "focus-inside-content-visibility-crash.html": [ + "1bf68d6686c2c902205faa912573e2b3c97f0ad8", + [ + null, + {} + ] + ], + "grid-in-columns-000-crash.html": [ + "56cf6cfdbbcc43c1403692895cec7c0071f6eea8", + [ + null, + {} + ] + ], + "grid-in-columns-001-crash.html": [ + "b9cf100533a74c12e3c26f274d65363d12f36705", + [ + null, + {} + ] + ], + "grid-in-columns-002-crash.html": [ + "762ad44f247d8773d978bc99740d7192669ea006", + [ + null, + {} + ] + ], + "grid-in-columns-003-crash.html": [ + "11089e690205d3ac03928b8d60c0030d6d16e91c", + [ + null, + {} + ] + ], + "inline-multicol-inside-container-crash.html": [ + "7e209f7ffd389744f843fe97b8483404f8ace7e5", + [ + null, + {} + ] + ], + "inline-with-columns-000-crash.html": [ + "733b2c4ee9a7406f15ffb819440d906e76c6740f", + [ + null, + {} + ] + ], + "inline-with-columns-001-crash.html": [ + "3b9bdf32bd2920256147d0b78bea9481e6921c51", + [ + null, + {} + ] + ], + "input-column-group-container-crash.html": [ + "5e520a45cffb9c203085118efde823b6b40cdf60", + [ + null, + {} + ] + ], + "input-placeholder-inline-size-crash.html": [ + "4b1284e5cb3d90a76a756a8d89a439c43d7e65e1", + [ + null, + {} + ] + ], + "math-block-container-child-crash.html": [ + "00b6836655e904f1f0d64f7e3f42afaf02fc351b", + [ + null, + {} + ] + ], + "pseudo-container-crash.html": [ + "f998c3a4464ca3eb3ce07687cf24d9dcdc9a16af", + [ + null, + {} + ] + ], + "svg-layout-root-crash.html": [ + "75a3839add0f0dcc33378ab194f37acdfcc39095", + [ + null, + {} + ] + ], + "svg-text-crash.html": [ + "aadba08679e8ce4c0645f7673c64ea41eb7c1869", + [ + null, + {} + ] + ], + "table-in-columns-000-crash.html": [ + "566a4eb1eb2daca575dc9bbb7a19d31dd66c3b4f", + [ + null, + {} + ] + ], + "table-in-columns-001-crash.html": [ + "4fab9de88f61016e5214a91b9c7efd898eca0803", + [ + null, + {} + ] + ], + "table-in-columns-002-crash.html": [ + "4f0cdc07400c0a597346cdb3b3c37571ef0fb58e", + [ + null, + {} + ] + ], + "table-in-columns-003-crash.html": [ + "436da592d97993f64d465bc1faecff7c805fc766", + [ + null, + {} + ] ] - ], - "chrome-bug-1289718-000-crash.html": [ - "5dab634a2a2586722ddbd0a9cf9eea8e31001dc6", - [ - null, - {} - ] - ], - "chrome-bug-1289718-001-crash.html": [ - "7236714183fd2130c9d12ffaf5226376d3ee7e58", - [ - null, - {} - ] - ], - "columns-in-table-001-crash.html": [ - "fe421500dade86b6fedbbf6289a668dcc5174f45", - [ - null, - {} - ] - ], - "container-type-change-chrome-legacy-crash.html": [ - "609142a2c5f7fa16f241b6ac06842a62ed68be8c", - [ - null, - {} - ] - ], - "flex-in-columns-000-crash.html": [ - "e7b789345c807362888a651952486b7e8fcc8ba5", - [ - null, - {} - ] - ], - "flex-in-columns-001-crash.html": [ - "0c0648c15bc1f6873bd6da9d868267cbc7d1df23", - [ - null, - {} - ] - ], - "flex-in-columns-002-crash.html": [ - "ef3052d2c98656bf8996b394cc5f66589e04421f", - [ - null, - {} - ] - ], - "flex-in-columns-003-crash.html": [ - "a86f25a773dbbcd1f622a0b3c32d00d8e845293c", - [ - null, - {} - ] - ], - "focus-inside-content-visibility-crash.html": [ - "1bf68d6686c2c902205faa912573e2b3c97f0ad8", - [ - null, - {} - ] - ], - "grid-in-columns-000-crash.html": [ - "56cf6cfdbbcc43c1403692895cec7c0071f6eea8", - [ - null, - {} - ] - ], - "grid-in-columns-001-crash.html": [ - "b9cf100533a74c12e3c26f274d65363d12f36705", - [ - null, - {} - ] - ], - "grid-in-columns-002-crash.html": [ - "762ad44f247d8773d978bc99740d7192669ea006", - [ - null, - {} - ] - ], - "grid-in-columns-003-crash.html": [ - "11089e690205d3ac03928b8d60c0030d6d16e91c", - [ - null, - {} - ] - ], - "inline-multicol-inside-container-crash.html": [ - "7e209f7ffd389744f843fe97b8483404f8ace7e5", - [ - null, - {} - ] - ], - "inline-with-columns-000-crash.html": [ - "733b2c4ee9a7406f15ffb819440d906e76c6740f", - [ - null, - {} - ] - ], - "inline-with-columns-001-crash.html": [ - "3b9bdf32bd2920256147d0b78bea9481e6921c51", - [ - null, - {} - ] - ], - "input-column-group-container-crash.html": [ - "5e520a45cffb9c203085118efde823b6b40cdf60", - [ - null, - {} - ] - ], - "input-placeholder-inline-size-crash.html": [ - "4b1284e5cb3d90a76a756a8d89a439c43d7e65e1", - [ - null, - {} - ] - ], - "math-block-container-child-crash.html": [ - "00b6836655e904f1f0d64f7e3f42afaf02fc351b", - [ - null, - {} - ] - ], - "pseudo-container-crash.html": [ - "f998c3a4464ca3eb3ce07687cf24d9dcdc9a16af", - [ - null, - {} - ] - ], - "svg-layout-root-crash.html": [ - "75a3839add0f0dcc33378ab194f37acdfcc39095", - [ - null, - {} - ] - ], - "svg-text-crash.html": [ - "aadba08679e8ce4c0645f7673c64ea41eb7c1869", - [ - null, - {} - ] - ], - "table-in-columns-000-crash.html": [ - "566a4eb1eb2daca575dc9bbb7a19d31dd66c3b4f", - [ - null, - {} - ] - ], - "table-in-columns-001-crash.html": [ - "4fab9de88f61016e5214a91b9c7efd898eca0803", - [ - null, - {} - ] - ], - "table-in-columns-002-crash.html": [ - "4f0cdc07400c0a597346cdb3b3c37571ef0fb58e", - [ - null, - {} - ] - ], - "table-in-columns-003-crash.html": [ - "436da592d97993f64d465bc1faecff7c805fc766", - [ - null, - {} - ] - ] + } }, "content-visibility": { "content-visibility-continuations-crash.html": [ @@ -2435,6 +2451,13 @@ }, "filter-effects": { "crashtests": { + "broken-reference-crash-001.html": [ + "6e18e06c317e329a58b2b5132cf17dc6086d444f", + [ + null, + {} + ] + ], "multiple-references-id-crash-001.html": [ "9ee04e1015d3c9fc04e8fab240a15d59ae92892d", [ @@ -3918,6 +3941,13 @@ null, {} ] + ], + "reparent-animating-element-002.html": [ + "0d3549fc33dae6c4a351f00790e63262b7258ff1", + [ + null, + {} + ] ] }, "interfaces": { @@ -79871,6 +79901,214 @@ {} ] ], + "multi-line-column-flex-fragmentation-017.html": [ + "070b0d6e854f631743fb77522b0fc5d4ea2ac72e", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-018.html": [ + "a491dfac810a6a4e4dc4ee9668b5e55b90e87e54", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-019.html": [ + "414326a5815a6a34bcaf04ff274ec8d331c17c99", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-020.html": [ + "18030dd0025679163fb9103918bb99aadf75ff17", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-021.html": [ + "833da1afce4d57a9c0644d962d3e7b014c93a93b", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-022.html": [ + "60f543b5d12e466352feb63467a9f16c16b3e75b", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-023.html": [ + "626d2679ea4063ae4135dcb63034eeb22e4e7a97", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-024.html": [ + "4cd53adf6d5e4117ea1c327bd569855f8ee7980b", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-025.html": [ + "93dd9b4a6211a9dc0688cd6b1bd17658d4a5ed1f", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-026.html": [ + "204cc15a2d514fc1c081dcdbaa29de9f8650d69b", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-027.html": [ + "95b73907b860320a6859e77095d3f1519f6fc4d5", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-028.html": [ + "d96cd56d6924c315d30094bef6805dd5c46b983a", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-029.html": [ + "ec47c9f3af5c6ca997592a4d03b6796c860534d4", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-030.html": [ + "443984588b1d9f65cf2fbadf834db984fb8a93cc", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-031.html": [ + "fc88e70a9cd1f790759f772ee10d791c25cddc99", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "multi-line-column-flex-fragmentation-032.html": [ + "978dbd1fa2f8e0e17e6abbbfbf0c7f0a0c1225a1", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], "multi-line-row-flex-fragmentation-001.html": [ "ba6b0103e447994a2778cbd9ea356a490f62b8fb", [ @@ -122424,6 +122662,19 @@ ], {} ] + ], + "subgrid-stretch.html": [ + "321e12d2a3ba6d1bab3623c385153b2375c1b441", + [ + null, + [ + [ + "/css/css-grid/subgrid/subgrid-stretch-ref.html", + "==" + ] + ], + {} + ] ] }, "table-grid-item-dynamic-001.html": [ @@ -213163,6 +213414,19 @@ {} ] ], + "visited-inheritance.html": [ + "1442307a9293ed0b26970dd25e70e8c57fef8ee8", + [ + null, + [ + [ + "/css/selectors/visited-inheritance-ref.html", + "==" + ] + ], + {} + ] + ], "xml-class-selector.xml": [ "5666c0065d6262e0ca3c586d145a67e7f9c2a3cf", [ @@ -235732,6 +235996,14 @@ "9e65b42435908e33ed5b16e97f8bee3700bab340", [] ], + "preload-csp-report.https.sub.html": [ + "6b79414edd532d6b1a0128479eb9a788caefa1fa", + [] + ], + "preload-csp-report.https.sub.html.sub.headers": [ + "bb0506b41da4fbfb43ed2a0c581495019d0690fd", + [] + ], "redirect-throw-function.sub.py": [ "1bc89abf7176bbada8f0c8c35fd699f52bb0e8a9", [] @@ -236825,7 +237097,7 @@ [] ], "OWNERS": [ - "f0d1e9f64b369dd5c46866e3008aac3f9c710215", + "c03182b1e8f0b29918bce193453359dee5410068", [] ], "fedcm-logout-rps.https.html.headers": [ @@ -244546,6 +244818,10 @@ "0f58883c97448d970d76b822f26f36efe3f14489", [] ], + "animation-computed-expected.txt": [ + "7cf62fe5bbfce3752012ad4adfe7773b7ce127ce", + [] + ], "animation-name-invalid-expected.txt": [ "a6d072671edbf2b2528754cf15f469eb7ce08d41", [] @@ -246999,7 +247275,7 @@ [] ], "system-color-valid-expected.txt": [ - "5194a9465974f66b1965d80e3ad4307797608c1e", + "dde0e81fbed53164e38009674385ca18af22c659", [] ] }, @@ -247682,6 +247958,10 @@ "ecd72b7516c41c83b6e8d320c9b0b87f9c231781", [] ], + "layout-dependent-focus-expected.txt": [ + "c69221b27d619ce342cc52163d2b42d778d450c2", + [] + ], "multicol-container-001-expected.txt": [ "81e1ba5e89ba840e12b22c5475412827d6db71f5", [] @@ -260847,6 +261127,10 @@ "subgrid-mbp-overflow-004-ref.html": [ "5188a3c9fbf98f3730dd9195bbbf2cb56ab1aa90", [] + ], + "subgrid-stretch-ref.html": [ + "33e8669da01787826ea27895323f17b5b3a6a2c9", + [] ] }, "support": { @@ -281171,6 +281455,10 @@ "60549107fbeec9ba2ae7c07bcf3afe5d5bc94b34", [] ], + "visited-inheritance-ref.html": [ + "64300b13f4291f416eac17f70ff62e2febf604fe", + [] + ], "xml-class-selector-ref.xml": [ "6b44280737ab254649036ff50808b764bc87cc67", [] @@ -282022,7 +282310,7 @@ [] ], "testdriver.md": [ - "dd751c3b7668790054d68cd1858b7ec0e7de89c6", + "37ceb1ca4776235d4abaacb97b99f04190e7bcc2", [] ], "testharness-api.md": [ @@ -287368,7 +287656,7 @@ [] ], "message-target.js": [ - "211caeb6cca2fe9e5012a65bd18fde5c56ce4abb", + "175d139c604e882b2bf8ae4334e643d858786d65", [] ], "messaging-blob-helpers.js": [ @@ -287392,7 +287680,7 @@ [] ], "sync-access-handle-test.js": [ - "ec13ff9a06bc67e9a770489fddc3e84678281027", + "489eeb8e2fe4ed3bb7eb0882e7fc6d6464fecb93", [] ], "test-helpers.js": [ @@ -288924,6 +289212,10 @@ "1a2996928163f16a8dd496ef98e71bf7b523db45", [] ], + "permission.https-expected.txt": [ + "3a82cfc21afb5d7b8dbfbca32b3c436ee1135179", + [] + ], "resources": { "iframe.html": [ "25cf3d92f3942738686a267c22d95d4a6464af7e", @@ -307312,14 +307604,22 @@ ] }, "lint.ignore": [ - "bc320dcd71e42da152519358865499226919fb4a", + "f1d1c2b273ada22f4e5f64aa785b74aa97808c0d", [] ], "loading": { "early-hints": { "resources": { + "csp-basic-loader.h2.py": [ + "080901efe60e70d8b178d0d9ffea5bbe04d71b94", + [] + ], + "csp-basic.html": [ + "0086711fb7c7bb5de136346e4ab7be50bb895d99", + [] + ], "early-hints-helpers.sub.js": [ - "5c1b2198eaee880fe6cd0a97684619788fd07b8c", + "33f80bc5f2dbe53ee1d7599f3ac46086951d356b", [] ], "early-hints-test-loader.h2.py": [ @@ -309725,11 +310025,7 @@ [] ], "OWNERS": [ - "cc6a94b332a04fdf400165265f41194abaf67d3e", - [] - ], - "README.md": [ - "068ae5b7114745e50c7c8a86fec8ef684ee02f61", + "e962bea003364aff10975e414f8a68f3b69cee97", [] ], "focus-reset": { @@ -309826,7 +310122,7 @@ [] ], "helpers.js": [ - "bf711b18ade2c881fbfa40fc8e44ddf456fa37d2", + "5b147f4329094fb39c9c166532a06b963cb365fc", [] ], "navigate-opaque-origin-page.html": [ @@ -309843,7 +310139,7 @@ ], "resources": { "helpers.mjs": [ - "b5a1e6b03186bfcc063a0a49ec46575d03bae969", + "359774de4f72c4c0bcb19ff33081ab7325b1756a", [] ], "notify-top-early.html": [ @@ -314321,7 +314617,7 @@ [] ], "testdriver-actions.js": [ - "ef097961fa7d51ba05bba4c90806b0aceb24bba8", + "3e5ba74b4cab356e56c73a4662e8f4b91dddf6de", [] ], "testdriver-vendor.js": [ @@ -314341,7 +314637,7 @@ [] ], "testharness.js": [ - "fcff33a59406ced4ef0b3a2da1afde19418cf5c2", + "a6f48e1f943b9952a8fbe5d882278c2cfb86baba", [] ], "testharness.js.headers": [ @@ -318270,6 +318566,18 @@ } }, "speculation-rules": { + "prefetch": { + "resources": { + "executor.sub.html": [ + "05b1295e9204f6ab92a16b2a5da939c426b09ba9", + [] + ], + "utils.js": [ + "e5e5bd8ee34dd1803a658f6bb4db3edb9f25760e", + [] + ] + } + }, "prerender": { "resources": { "about-blank-iframes.html": [ @@ -318280,6 +318588,10 @@ "456f15ff699eb36b230b5079f918cbd57ea2705a", [] ], + "broadcast-channel.html": [ + "14980720a1640de6bc4141ae08d7238366f54ffa", + [] + ], "cache.txt": [ "89164cfabed56b5852445eb70470cfd1331bdb48", [] @@ -324397,6 +324709,10 @@ "META.yml": [ "17d93c51a9f195f5093882b1934c79f0a0635554", [] + ], + "RTCRtpParameters-scalability-expected.txt": [ + "79d1d57591325dccc5818ad89f288e27ba48c49b", + [] ] }, "websockets": { @@ -355894,6 +356210,13 @@ {} ] ], + "report-preload-and-consume.https.html": [ + "c4481cfd27a6fad1a07ee9ffe606569fa440f73e", + [ + null, + {} + ] + ], "report-same-origin-with-cookies.html": [ "aa2ec6bd9d4e31dc704da8a7ac2bf65c1ed0baac", [ @@ -360023,6 +360346,13 @@ {} ] ], + "block-in-inline-hittest-001.html": [ + "c51e4c5329a686808009f04d0392f688aacbd236", + [ + null, + {} + ] + ], "block-in-inline-hittest-float-001.html": [ "6ede29df26d72485dad385539ebc20d9fb07bf70", [ @@ -361432,7 +361762,7 @@ ] ], "animation-computed.html": [ - "467f4357f102fb75ed8df196d221d83cd2efdabc", + "f8d34b889ba19dfc7d9fd4d644da499b979d4166", [ null, {} @@ -363361,7 +363691,7 @@ ] ], "system-color-valid.html": [ - "77c6d214fbce92e33df6829f09a2d9fd7fcfbf83", + "eea01e5d4ee60d1d00c2da6e887851d369477182", [ null, {} @@ -363381,6 +363711,13 @@ null, {} ] + ], + "system-color-consistency.html": [ + "f8fc5ef0d00ec92ed377021b5b8537851f20f13c", + [ + null, + {} + ] ] }, "css-color-adjust": { @@ -363873,6 +364210,13 @@ {} ] ], + "layout-dependent-focus.html": [ + "a16370ac566d7044efb03f0466889a89dda49c4c", + [ + null, + {} + ] + ], "multicol-container-001.html": [ "3032170ac61bee9d4322d1992ab8a27af668c48e", [ @@ -381375,7 +381719,7 @@ ], "parsing": { "transition-computed.html": [ - "3f253c3a0d1fbe2193bc101872ad67e4a24835cc", + "a82551372f160fa77fdd0dcc7ac7d62d546db20d", [ null, {} @@ -422327,7 +422671,7 @@ ] ], "sandboxed_FileSystemFileHandle-sync-access-handle-writable-lock.https.tentative.worker.js": [ - "a2ed99db1d538ada4d76796ccd6a6c6cfba2565e", + "51e7f63d12f04b27d304832ef8659485c8961765", [ "file-system-access/sandboxed_FileSystemFileHandle-sync-access-handle-writable-lock.https.tentative.worker.html", {} @@ -423353,6 +423697,13 @@ {} ] ], + "permission.https.html": [ + "4062843349d77263fad2d8e12c6558f5d715ac04", + [ + null, + {} + ] + ], "watchPosition_TypeError.https.html": [ "64f0616949f3c00ed0d24f7d34f173c69e8e65d6", [ @@ -450967,6 +451318,15 @@ ] ] } + }, + "the-hidden-attribute": { + "hidden-idl.html": [ + "331b63f93fd80a360dbc46e7b2da93d24596367c", + [ + null, + {} + ] + ] } }, "infrastructure": { @@ -473323,6 +473683,168 @@ }, "loading": { "early-hints": { + "csp-early-hints-absent-final-absent.h2.window.js": [ + "f61e268c8a3e2692951607eb8ad4dddd988f0ad3", + [ + "loading/early-hints/csp-early-hints-absent-final-absent.h2.window.html", + { + "script_metadata": [ + [ + "script", + "/common/utils.js" + ], + [ + "script", + "resources/early-hints-helpers.sub.js" + ] + ] + } + ] + ], + "csp-early-hints-absent-final-allowed.h2.window.js": [ + "0e1762a28c9937a0086f6768d75fef24b7ebf388", + [ + "loading/early-hints/csp-early-hints-absent-final-allowed.h2.window.html", + { + "script_metadata": [ + [ + "script", + "/common/utils.js" + ], + [ + "script", + "resources/early-hints-helpers.sub.js" + ] + ] + } + ] + ], + "csp-early-hints-absent-final-disallowed.h2.window.js": [ + "3fcd89c4cc089c38a53ab759bf7727813c4c5017", + [ + "loading/early-hints/csp-early-hints-absent-final-disallowed.h2.window.html", + { + "script_metadata": [ + [ + "script", + "/common/utils.js" + ], + [ + "script", + "resources/early-hints-helpers.sub.js" + ] + ] + } + ] + ], + "csp-early-hints-allowed-final-absent.h2.window.js": [ + "15128ce5d9dca0dd8034939695ec99d07d3b5ce2", + [ + "loading/early-hints/csp-early-hints-allowed-final-absent.h2.window.html", + { + "script_metadata": [ + [ + "script", + "/common/utils.js" + ], + [ + "script", + "resources/early-hints-helpers.sub.js" + ] + ] + } + ] + ], + "csp-early-hints-allowed-final-allowed.h2.window.js": [ + "ee51e78bf168c111a60dbc14972691b412a945b8", + [ + "loading/early-hints/csp-early-hints-allowed-final-allowed.h2.window.html", + { + "script_metadata": [ + [ + "script", + "/common/utils.js" + ], + [ + "script", + "resources/early-hints-helpers.sub.js" + ] + ] + } + ] + ], + "csp-early-hints-allowed-final-disallowed.h2.window.js": [ + "67b39333ad7b58220bc73090ce350c842f2987d8", + [ + "loading/early-hints/csp-early-hints-allowed-final-disallowed.h2.window.html", + { + "script_metadata": [ + [ + "script", + "/common/utils.js" + ], + [ + "script", + "resources/early-hints-helpers.sub.js" + ] + ] + } + ] + ], + "csp-early-hints-disallowed-final-absent.h2.window.js": [ + "80b563dd888f2defd34b4b993156a6ad23d2feb6", + [ + "loading/early-hints/csp-early-hints-disallowed-final-absent.h2.window.html", + { + "script_metadata": [ + [ + "script", + "/common/utils.js" + ], + [ + "script", + "resources/early-hints-helpers.sub.js" + ] + ] + } + ] + ], + "csp-early-hints-disallowed-final-allowed.h2.window.js": [ + "cfac9b0b9864b1d42035de1ba4cf51837cdce3d4", + [ + "loading/early-hints/csp-early-hints-disallowed-final-allowed.h2.window.html", + { + "script_metadata": [ + [ + "script", + "/common/utils.js" + ], + [ + "script", + "resources/early-hints-helpers.sub.js" + ] + ] + } + ] + ], + "csp-early-hints-disallowed-final-disallowed.h2.window.js": [ + "c8a7ca346fca680038ae2a18b82363f8759fac23", + [ + "loading/early-hints/csp-early-hints-disallowed-final-disallowed.h2.window.html", + { + "script_metadata": [ + [ + "script", + "/common/utils.js" + ], + [ + "script", + "resources/early-hints-helpers.sub.js" + ] + ] + } + ] + ], "preload-initiator-type.h2.window.js": [ "959aacef4978b935b44fec694d5f3ca3cb074485", [ @@ -480943,154 +481465,154 @@ "navigation-api": { "currententrychange-event": { "anchor-click.html": [ - "55e45561351ff1c6f43d211f1b8d7c78cfa098cc", + "e0bf91166aa2386b7043ccc12aed636c965952a5", [ null, {} ] ], "constructor.html": [ - "64ea8cf37ebd545cb349e4b0d863be3061851b99", + "b09e68e1c71e0edc9b2cf5a045cce9bba3a42706", [ null, {} ] ], "history-back-same-doc.html": [ - "bee3649395a8b1f4802de17d7fb4b21f894863e2", + "768805b7526fcd25898200d0d33f87f9440e6893", [ null, {} ] ], "history-pushState.html": [ - "c0e2d9a783cbe508bd065792ff115f62e26770a6", + "1a17a7cec0cade51f537d1ef719fed647e6ebfb6", [ null, {} ] ], "history-replaceState.html": [ - "da5505a6c14a2485b2ba194d0060bdfadef53a5c", + "e8ae8ee57a2c78f36f27643274be01c9fe44b602", [ null, {} ] ], "location-api.html": [ - "f06dc8ec615942aa277a602b7070502e74396725", + "88ebd985a1e9803a4c66b71afc4dda3033fa0565", [ null, {} ] ], "navigate-from-initial-about-blank-same-doc-popup.html": [ - "a8aa2a25fb51c0bfc0a40c953d50dc3786ddd409", + "2399fb2ef9cc88aa2d3cb306fdc7fe846d171ce8", [ null, {} ] ], "navigate-from-initial-about-blank-same-doc.html": [ - "e2c56743588e0bb1919565495fed603ff6b2becf", + "f0fa9cdf57b70e8dc80eafbfb2bb7f549b8c13b7", [ null, {} ] ], "navigate-from-initial-about-blank.html": [ - "7b406c733c4a11712cabc7596daacc7eb769d5ce", + "56eaa1af265b40433f085b600ac06fdf36a730a7", [ null, {} ] ], "navigation-back-forward-cross-doc.html": [ - "df230bd9acb5daae40f30a2dfe625ebe0f8aa78f", + "7416caa94b1f6588ce4779a4339df97509302c63", [ null, {} ] ], "navigation-back-forward-same-doc.html": [ - "41cc4afd446e6338792a0f4cceb285082903d858", + "8182673aa59dc007d52de97d9cce27614eec38cb", [ null, {} ] ], "navigation-navigate-cross-doc.html": [ - "2f804a307f6cbeef899411f6fd58c382b2f7f1cf", + "81a4e239baeefd9c73c1126fd9069c4315c1fdca", [ null, {} ] ], "navigation-navigate-preventDefault.html": [ - "997fb559492c7383e630e41c399a2106bcf58732", + "34b98353ba3745441a830c7d2a78a107f9aef7b4", [ null, {} ] ], "navigation-navigate-replace-cross-doc.html": [ - "52478f22799b0593749a2b59963ed0de43ee0ff9", + "d37ba34d62bc93b6c47fe5080875908ef789392e", [ null, {} ] ], "navigation-navigate-replace-same-doc.html": [ - "a4027709687949140948681c9115d9eeff8d9307", + "1c0f985b9854ffa3530c7da9c68c004166204846", [ null, {} ] ], "navigation-navigate-replace-transitionWhile.html": [ - "55b314a554d2542c3a8de9388135180fae2cada0", + "a8babf298dc2bc2bcc57f49117cd7004fe3db220", [ null, {} ] ], "navigation-navigate-same-doc.html": [ - "96de3de6d3d62b2d33d566c2e38568cb8d327f36", + "87fc28d174a6c8bfbcde746fc019cbd833800319", [ null, {} ] ], "navigation-navigate-transitionWhile.html": [ - "2132016404dd17968100315ec78fc4bf998bfb84", + "e5855cf63e81517bcc53a37a95bbfdeca9c68e16", [ null, {} ] ], "navigation-reload-cross-doc.html": [ - "cb4279cfe53f44146bca35f6aa529997f70207fb", + "e590cab3828e3f8472713462f82e7e7efa8c4fd5", [ null, {} ] ], "navigation-reload-transitionWhile.html": [ - "cfa6258dc578de7fe011ed8a938b333ea8e38388", + "c732de941f7ed8d0ee52085b1c9039e2d41728d4", [ null, {} ] ], "navigation-updateCurrentEntry.html": [ - "8d548706be938d26e459cb4c5c7bf69620517f3e", + "4423b2bc90e1c52dfc17de0770c1d7f2ed57a48f", [ null, {} ] ], "properties.html": [ - "7048034e4f051bf1f302383fbc0d5828ede3a837", + "e862543bcc1eca7569a094080c8491df65f4f161", [ null, {} @@ -481292,7 +481814,7 @@ ] }, "event-constructor.html": [ - "501857feba4471575531965c865bcaf163d17985", + "6ef5cdfc911955b18f31261bc1313704e8cb22a6", [ null, {} @@ -481520,49 +482042,49 @@ ] ], "signal-abort-detach-in-onnavigate.html": [ - "adef895565b9bc283cbc570323c617acf763104a", + "467ea88899323bbf47e9a12fd3a03ee577963a43", [ null, {} ] ], "signal-abort-preventDefault.html": [ - "f281617866c0cd6a8e7e2a61ef6ba8e58cf38115", + "60fed90ce6c36afbd6221feba367bb45085d1297", [ null, {} ] ], "signal-abort-transitionWhile.html": [ - "d90ee5b0837c41bd3bdce5f28284037e76aa0e0c", + "ce195cf677229eb31591bec780cb7684ba9cb502", [ null, {} ] ], "signal-abort-window-stop-after-transitionWhile.html": [ - "aad3be34d7e80bc6e613f309f45089c180fb8fc4", + "bc44a07b20e8303095511dadb0b03b9563a63ca9", [ null, {} ] ], "signal-abort-window-stop-in-onnavigate.html": [ - "dde666c93386175a50d08da6296a8d8121d73243", + "1b406c42d367f98e0d993f5e4d66d8b704386697", [ null, {} ] ], "signal-abort-window-stop.html": [ - "49399fb663777ea87b6c5b0686a1168557ed5e97", + "43e005e8b41180b058055ef30714e7dd49954861", [ null, {} ] ], "transitionWhile-after-dispatch.html": [ - "7f4a25cef528a1609731b2b1cc8c5139eee56a12", + "910665cb3deb51b8537b9f069841d26ecec1ae5e", [ null, {} @@ -481576,91 +482098,91 @@ ] ], "transitionWhile-canceled-event.html": [ - "ed6dbdc85cfe0796e26fc519ca2b498fd0fdf526", + "f1fe3ece38bf202dcba44cced42a348dc97554fe", [ null, {} ] ], "transitionWhile-cross-document-same-origin.html": [ - "79e03f1c397c5d3bc6b5ee82695e188575400542", + "9e55958c533785d6656292bd408232c76fd42435", [ null, {} ] ], "transitionWhile-cross-origin.html": [ - "ece79d263571838352ab74244df296a798708629", + "e53371a96a2ce5d7bf22c1a37b4fc81e9c53d111", [ null, {} ] ], "transitionWhile-detach.html": [ - "d6a7d536276b6af21a96de2b20d5abd05e544a7e", + "25159850803d819e8319b61c59cbcdbafbc4a65c", [ null, {} ] ], "transitionWhile-history-pushState.html": [ - "fb6152fc2289ffe74082307a439ca6de004832d1", + "a08e4ea2823764c7e7856149f84e8e63dfd853ab", [ null, {} ] ], "transitionWhile-history-replaceState.html": [ - "00f85dcc0fcc958f4224cf8b3ae4f0b5a5a757f2", + "e575876de245b3c6d2d0e391663efa47885116a5", [ null, {} ] ], "transitionWhile-multiple-times-reject.html": [ - "c325829001572dfd082c98d1b64fec117c3b12b4", + "0532d755fd343fee914967c351e7c0283601716c", [ null, {} ] ], "transitionWhile-multiple-times.html": [ - "0f6caf16f1411e850522df207289c4f933b80b65", + "d5661a54eba39a76fa66dfc00e76b2052b603cc9", [ null, {} ] ], "transitionWhile-navigation-back.html": [ - "a0bc453d36e3095fcb0d5c76f6fabb0c6ad3f612", + "3a4629005be58a3314cffae0fcc8fc524b53f7cf", [ null, {} ] ], "transitionWhile-on-synthetic-event.html": [ - "21a32e7b4c93946f7c8a899a78d12133c2a2dfb9", + "3acf41745811aca4d5e31de5b257e97a72b556fa", [ null, {} ] ], "transitionWhile-reject.html": [ - "2cdea05072911cf6883f90071547f20b5693c01a", + "28864b38a5186aadc8233940870ed9e0f53848dd", [ null, {} ] ], "transitionWhile-resolve.html": [ - "2ee4fb39713cffe36724b85beda4cba9c0ca3287", + "b49c66399daee59a595aafabbb09c94463ac6c48", [ null, {} ] ], "transitionWhile-same-document-history-back.html": [ - "5e8ac39ef20ec07b78fd3ee58437dbe90a0a6a45", + "4c404386fe216092d87bbe87c876eb29ee47d17d", [ null, {} @@ -481697,21 +482219,21 @@ ] ], "entries-after-bfcache.html": [ - "11bd7df931d56880163f42af33676ef76a0b74e9", + "ef93d1e27e76cc72f8fb24ee711b76361c71f42a", [ null, {} ] ], "entries-after-blank-navigation.html": [ - "6f6dbcc20df9bb8c0beb4921d84da359b8dbc01d", + "f54ae06e148d8c45352acbf927b458a6c83e9cd2", [ null, {} ] ], "entries-after-blob-navigation.html": [ - "77c25174da2f87a04c9c642e0fa732717bc1937e", + "67611f4d446cc25895058c18007190c957ea458a", [ null, {} @@ -481725,7 +482247,7 @@ ] ], "entries-after-javascript-url-navigation.html": [ - "27a07c9e55d37c364595b393cf4282e425854f6a", + "c5ef7f33e425a2eb128c600742f3a6541c7c5085", [ null, {} @@ -481739,7 +482261,7 @@ ] ], "entries-after-srcdoc-navigation.html": [ - "434b376224c6bd121c28e2667050f8fd263a4a04", + "b80e8aa0e21636c9627eaccd3a64e31a3a46a271", [ null, {} @@ -481753,14 +482275,14 @@ ] ], "entries-in-new-javascript-url-iframe.html": [ - "cd4279a7f31fb4264b01a372e62cad9fad94c660", + "6f217f5e3c4f335888c84128b3b32febe88a61cb", [ null, {} ] ], "entries-in-new-srcdoc-iframe.html": [ - "d261efd0778a787df0194fb40c59cd979680d01a", + "a7e0f88d37a3746c0fdefd991c2b8d013d5e86e6", [ null, {} @@ -481774,56 +482296,56 @@ ] ], "entry-after-detach.html": [ - "83962d2a8e146244720f687b5980fec6e5b0bc15", + "69c52d140983bc2a781706b13a3c7c1c3bc05bb7", [ null, {} ] ], "index-not-in-entries.html": [ - "37cccd39d829be865485649762b91cc6687710af", + "1c09a0f53ae4a9d7582a299daa9c90a77446e54b", [ null, {} ] ], "key-id-back-cross-document.html": [ - "f0dc182be41f27d5abae0b67e0d3fe38d06a135a", + "2dd58c03e9d7cd3f81f50ad0c320e60dcaa07bf7", [ null, {} ] ], "key-id-back-same-document.html": [ - "b5137f3e51bfcce5e29a72766f230eae1da328d7", + "858b5fd2c896347ae86680ed554f4160636aa3fd", [ null, {} ] ], "key-id-location-reload-transitionWhile.html": [ - "a06b645fa6f2f190cb91d4f8bb462a79c972db4e", + "e5924d243160cd9abcc7928a356defcc972543d3", [ null, {} ] ], "key-id-location-reload.html": [ - "267906d2c38cc4cc65d3c56c8dca0c77bb551fff", + "f950e2f3a2172e266810fa44ded85647a8eb5247", [ null, {} ] ], "key-id-location-replace-cross-origin.html": [ - "ee0f749a9edc3435e454f96ef83a366494a1de99", + "65aff4a3abc3166d31ffbdeb9f9ffd236f8f75a0", [ null, {} ] ], "key-id-location-replace.html": [ - "ec96371e5648b2bc5e64802cdfc134ff18b18efa", + "a58772a7f479ba2f64f2b220c85588e496e8e4c4", [ null, {} @@ -481837,21 +482359,21 @@ ] ], "no-referrer-from-meta-url-censored.html": [ - "51eed0800fb02814ef90342bc2a87bb36b01c161", + "fc563f509e79c8bcc98487bcde12d3068cc3d456", [ null, {} ] ], "no-referrer-url-censored.html": [ - "20397b8ef9c8af8bf2188257e9a57e687ef6694d", + "e7eb1afc7d1d2d80df8131bcc449187093f0deb2", [ null, {} ] ], "opaque-origin-data-url.html": [ - "3275a30ef420f7220aacf85788e1ef9e3b486def", + "65123fa60babbdc77bb58de6b4e68300c14256b2", [ null, {} @@ -481865,7 +482387,7 @@ ] ], "sameDocument-after-fragment-navigate.html": [ - "acb67ee72c705563fe850b41f9aa166e634c3313", + "a197f825d8eda300250af2961bfd14448cdc3fc2", [ null, {} @@ -481879,7 +482401,7 @@ ] ], "sameDocument-after-navigate.html": [ - "e1376a07f2550210be1304ae16a37df28ed3daa6", + "bfcb7c6599729c551bc447cde058b4477570e043", [ null, {} @@ -482028,7 +482550,7 @@ ] ], "reload-base-url.html": [ - "936ad75c832a13b3aff1f05d21fbf7e9a8f2f728", + "1e8d3b90c34691fd080cdd928b38788bb84383a7", [ null, {} @@ -482063,7 +482585,7 @@ ] ], "reload-state-and-info.html": [ - "3ee6a8b8fa0218767e4322e2e4014d12db68d93e", + "a68dd2ab8be014f5f1b578dc631adbd92578e2d8", [ null, {} @@ -482534,7 +483056,7 @@ ] ], "traverseTo-detach-between-navigate-and-navigatesuccess.html": [ - "9a9529578147b557c83a10f499227645b0ceb746", + "25dd134649627eb646afbae39420c1aa5d717552", [ null, {} @@ -482564,7 +483086,7 @@ }, "ordering-and-transition": { "back-same-document-transitionWhile-reject.html": [ - "68cff8085d49fd8f5751d05e81088d828728fff6", + "05d86b4910213a89cdf0f458ed80653a9d936d08", [ null, {} @@ -482575,7 +483097,7 @@ ] ], "back-same-document-transitionWhile.html": [ - "30d083c9e6dbb80b1f60401cc6ba2ddba92c6366", + "d435aab37b4b16d825c525fec0f99629067587c9", [ null, {} @@ -482586,7 +483108,7 @@ ] ], "back-same-document.html": [ - "cc303754119484bf3338e64fe7af8c3a000e50f1", + "76c3e99311ed3e792446d9b250d0bef48dbd2847", [ null, {} @@ -482597,28 +483119,28 @@ ] ], "currententrychange-before-popstate-transitionWhile.html": [ - "f4c36fa0ab21143fd69f4fe5079845d87231ad7f", + "59f37e97125e6d438866b628624f889d57da315c", [ null, {} ] ], "currententrychange-dispose-ordering.html": [ - "396a46ff969b2f3649b39850662bd37186bc0cd8", + "7cbed8b1bbc90a810d92971e1a620c83a39be30d", [ null, {} ] ], "location-href-canceled.html": [ - "f4247413d3aa2f325c920352c643857c0d09ca12", + "eef10cd1730bbb56a9dab64bfeec90ecb34b770f", [ null, {} ] ], "location-href-double-transitionWhile.html": [ - "6e7c89502d60844035eea74f35530e81d63402cd", + "5ec22b119fa673b8125c98bf2f6eadd34007f55a", [ null, {} @@ -482629,7 +483151,7 @@ ] ], "location-href-transitionWhile-reentrant.html": [ - "36b23554ddb9566c4a7ec894a050944bcae0dea9", + "4dedb58d5b042f55dc97f3672d1534946cbf66fc", [ null, {} @@ -482640,7 +483162,7 @@ ] ], "location-href-transitionWhile-reject.html": [ - "e3f65e81d44e0dc1fe53532953f40d4fc767ce6d", + "dbef3928e30c9da1b3e0242448cc62cae04745b3", [ null, {} @@ -482651,7 +483173,7 @@ ] ], "location-href-transitionWhile.html": [ - "0ae41447213ba7bb6e9ea8baf819715d0328c8e2", + "0e1d22df7254f8ca385922f6f29c9ea4505d9458", [ null, {} @@ -482662,7 +483184,7 @@ ] ], "navigate-canceled.html": [ - "851fa349937c207d97ada62d790d22d6a93f53b4", + "2604a60e37bfc240e84df5c7bf02fda31425a303", [ null, {} @@ -482683,7 +483205,7 @@ ] ], "navigate-double-transitionWhile.html": [ - "4413ca4c35d93a33360d565c9fdaf40fa24fcc4b", + "556d1737a6e37e0828a78558d5886953c7adb164", [ null, {} @@ -482694,7 +483216,7 @@ ] ], "navigate-in-transition-finished.html": [ - "f8ebd6927322e5d360ad648a95995a972ecd9dbe", + "8c74ba2f632f7b82af69cacf9d1918dc38ca34d5", [ null, {} @@ -482705,7 +483227,7 @@ ] ], "navigate-same-document-transitionWhile-reentrant.html": [ - "8cbb98e85819e20d742c2b1ac48b2fe33edce6a6", + "2ef4c4fe27294c0028c3ed3fefae95355f64faea", [ null, {} @@ -482716,7 +483238,7 @@ ] ], "navigate-same-document-transitionWhile-reject.html": [ - "45ce700057053b7d7e873f6c21765e5c1ca0cc35", + "b3a10b422e9444ce77487ac788b9904b9c660507", [ null, {} @@ -482727,7 +483249,7 @@ ] ], "navigate-same-document.html": [ - "07f7bdce5d6ea10acd7801bcbed26deea01dbc98", + "589e1105e65a74655e218181835b20c83ac83fd2", [ null, {} @@ -482738,7 +483260,7 @@ ] ], "navigate-transitionWhile-stop.html": [ - "9d06ba17969fa4b19db945589cf5a3933c4709e9", + "9a8c412b3333efab9b6fde4e01a8df3539595f5e", [ null, {} @@ -482749,7 +483271,7 @@ ] ], "navigate-transitionWhile.html": [ - "d3125b0fc099877014eddfb401115f1a3833f248", + "b72bf49eae20f53e1650644a525fe1792b1d4fab", [ null, {} @@ -482760,14 +483282,14 @@ ] ], "reload-canceled.html": [ - "a7ae7913c6107f89e79f9b964d3ae103bf657770", + "3e9e24e777112908703e45daebd87b88dd5d1e59", [ null, {} ] ], "reload-transitionWhile-reject.html": [ - "0a70c6deed4fdfe49edbe10d1956fa1d2adecf98", + "6939ee2df8208b9ed8867242f797cedb0689679a", [ null, {} @@ -482778,7 +483300,7 @@ ] ], "reload-transitionWhile.html": [ - "8cc237f2dddafa98b4fcf19f9df54f35920f0a87", + "31cd813f7419bb6c91da98e604c7c34851723369", [ null, {} @@ -482803,7 +483325,7 @@ ] ], "transition-realms-and-identity.html": [ - "16fb8184545e7df127dd581f61ab7e497771c474", + "c2c06fcca2604438e0f9e48277971e22ade657ab", [ null, {} @@ -482870,49 +483392,49 @@ }, "state": { "cross-document-away-and-back.html": [ - "25d0c1e79811cea7de5b747aab2066caaceb558e", + "60f70363d09c3482a46e0e185f8ae6bbf9b10f4d", [ null, {} ] ], "cross-document-location-api.html": [ - "9c869ca1d276e465b29ae719a1e24a735e0e91b8", + "29e5a025d7c1fb2a4bd22d2ba65772b780ed8c05", [ null, {} ] ], "history-pushState.html": [ - "1c8858c0af0c363bb12f74fbc1c76b5914264449", + "dad01fd8371d57d4f00dc213b93db078aad5a6c9", [ null, {} ] ], "history-replaceState.html": [ - "cc37949415eb8b29ac85ebd4efc8c6dd96616b8d", + "14934d69f4520be20a623ebc800d4f8f6817a75d", [ null, {} ] ], "location-reload.html": [ - "94d466434638ef5823dee86541a69349a4d5878c", + "ae483bf900eb6e7f284961278c0ac36e2a31e74f", [ null, {} ] ], "same-document-away-and-back-location-api.html": [ - "9cb6215089fdb56e2a241e927a8fb5366de94b62", + "d161df8b5295f1bbc8e396a4345ef2ae0112264f", [ null, {} ] ], "same-document-away-and-back-navigation-api.html": [ - "eed296d583afc722bf14f378e4b0641b64d1c16a", + "0ece14d4b7d3bc61fa5f6cffff6ea3297ce5edc5", [ null, {} @@ -482928,14 +483450,14 @@ ] ], "cross-document-away-and-back.html": [ - "a192314050df0a2251a7b89d8eea4569862de7de", + "c37d5e979a069f73faf7972801792bc36c5a6f01", [ null, {} ] ], "cross-document-location-api.html": [ - "3b4ec68bae006db8149a323ffb542f7f7fbcbec8", + "26191fb8761b6dfbca7881f5b5bb2e62d7690155", [ null, {} @@ -482956,14 +483478,14 @@ ] ], "history-pushState.html": [ - "73fb89f2a0f8d4daf76537038b1eb315cd1bb0b0", + "852294c64f4e8fd596637032ecc363076ca86ee7", [ null, {} ] ], "history-replaceState.html": [ - "15472db2e777daf05d107998729b0b3f7f79035a", + "3eb91a9a80cef8e1a2036181503867ec490c1168", [ null, {} @@ -482977,7 +483499,7 @@ ] ], "location-reload.html": [ - "664ff1b23a5c2223e462c1c0feba2a991ba8044d", + "8589eeb694e73cb08852ec31c0577cb5d21e2a30", [ null, {} @@ -483005,7 +483527,7 @@ ] ], "same-document-away-and-back-location-api.html": [ - "a1f54f5b7c21f1a8767c9ed2623bf819c7bad0d8", + "47b1904f4f46f6cbd1a8bbdebbd2516e9669df93", [ null, {} @@ -509907,6 +510429,15 @@ ] }, "speculation-rules": { + "prefetch": { + "single-url.https.html": [ + "e98f06f30f5b722c9efcec00e0611fb44afd59e3", + [ + null, + {} + ] + ] + }, "prerender": { "about-blank-iframes.html": [ "9cc0ab3792714828be6235a4405db9cd77eec906", @@ -510007,6 +510538,15 @@ } ] ], + "restriction-broadcast-channel.html": [ + "7225e64cf9848921de4e33982e733104265daa43", + [ + null, + { + "timeout": "long" + } + ] + ], "restriction-focus.html": [ "1149b8bd0981e8cf0109f2bca9dd974b9cd9a604", [ @@ -527778,6 +528318,13 @@ {} ] ], + "script-invalid-json.https.tentative.html": [ + "57bd82c39d32a8183895efb16ecb8cc370d4d5eb", + [ + null, + {} + ] + ], "script-nested-bundle.https.tentative.html": [ "4daf60acf5eecf6420e8e0b82a74662aec5f6ea7", [ @@ -537753,7 +538300,7 @@ }, "webrtc-svc": { "RTCRtpParameters-scalability.html": [ - "52907af55eab90bcddc958d0cb44723e5079715b", + "c32a12807e97fcd5735052288bfc21e5f70fc05c", [ null, {}
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-preload-and-consume.https.html b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-preload-and-consume.https.html new file mode 100644 index 0000000..c4481cf --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-preload-and-consume.https.html
@@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test that reports are sent with credentials to same-origin endpoints</title> + <script src="/common/utils.js"></script> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/reporting/resources/report-helper.js'></script> +</head> +<body> + <script> + const endpoint = '/reporting/resources/report.py'; + + promise_test(async t => { + const uid = token(); + const win = window.open(`./support/preload-csp-report.https.sub.html?uid=${uid}`); + t.add_cleanup(() => win.close()); + await wait(3000); + const reports = await pollReports(endpoint, uid); + const failures = reports.filter(r => r['csp-report']['blocked-uri'].endsWith('fail.png')); + assert_equals(failures.length, 2); + }, "Reporting endpoints received credentials."); + </script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html new file mode 100644 index 0000000..6b79414 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html
@@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> +<body> +<!-- This image will cause a CSP violation, which will trigger an immediate report --> +<script> + const href = "/reporting/resources/fail.png"; + + window.addEventListener('load', async () => { + // Trigger a CSP error. + await new Promise(resolve => { + const link = document.createElement('link'); + link.rel = 'preload'; + link.href = href; + link.as = 'image'; + document.head.appendChild(link); + link.addEventListener('error', resolve); + }); + + // Trigger a second CSP error by consuming. + await new Promise(resolve => { + const img = document.createElement('img'); + img.src = href; + img.addEventListener('error', resolve); + document.body.appendChild(img); + }); + }); +</script> +</body> +</html> +
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html.sub.headers b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html.sub.headers new file mode 100644 index 0000000..bb0506b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html.sub.headers
@@ -0,0 +1 @@ +Content-Security-Policy: img-src none; report-uri /reporting/resources/report.py?op=put&reportID={{GET[uid]}}
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/normal-flow/block-in-inline-hittest-002.html b/third_party/blink/web_tests/external/wpt/css/CSS2/normal-flow/block-in-inline-hittest-002.html new file mode 100644 index 0000000..b01eaa9 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/CSS2/normal-flow/block-in-inline-hittest-002.html
@@ -0,0 +1,41 @@ +<html> +<meta name="assert" content="Test list-based hit-testing for block-in-inline"> +<link rel="help" href="http://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level"> +<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint"> +<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint"> +<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> + <div> + <a href="#"> + <h3 id="target"> + text + </h3> + </a> + </div> +<script> +function ancestors(element) { + const list = []; + for (; element; element = element.parentElement) + list.push(element); + return list; +} + +const target = document.getElementById('target'); +const bounds = target.getBoundingClientRect(); +const x = bounds.x + bounds.width / 2; +const y = bounds.y + bounds.height / 2; + +test(() => { + const result = document.elementFromPoint(x, y); + assert_equals(result, target); +}, "elementFromPoint"); + +test(() => { + const results = document.elementsFromPoint(x, y); + assert_array_equals(results, ancestors(target)); +}, "elementsFromPoint"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed-expected.txt new file mode 100644 index 0000000..7cf62fe --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
@@ -0,0 +1,15 @@ +This is a testharness.js-based test. +FAIL Default animation value assert_equals: expected "0s ease 0s 1 normal none running none" but got "none 0s ease 0s 1 normal none running" +PASS Property animation value '1s' +PASS Property animation value 'cubic-bezier(0, -2, 1, 3)' +PASS Property animation value '1s -3s' +PASS Property animation value '4' +PASS Property animation value 'reverse' +PASS Property animation value 'both' +PASS Property animation value 'paused' +PASS Property animation value 'none' +PASS Property animation value 'anim' +PASS Property animation value 'anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)' +PASS Property animation value 'anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)' +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html index 467f435..f8d34b8 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html +++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html
@@ -16,6 +16,11 @@ // <single-animation-iteration-count> || <single-animation-direction> || // <single-animation-fill-mode> || <single-animation-play-state> || // [ none | <keyframes-name> ] + +test(() => { + assert_equals(getComputedStyle(document.getElementById('target')).animation, "0s ease 0s 1 normal none running none"); +}, "Default animation value"); + test_computed_value("animation", "1s", "1s ease 0s 1 normal none running none"); test_computed_value("animation", "cubic-bezier(0, -2, 1, 3)", "0s cubic-bezier(0, -2, 1, 3) 0s 1 normal none running none"); test_computed_value("animation", "1s -3s", "1s ease -3s 1 normal none running none");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html new file mode 100644 index 0000000..070b0d6 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html
@@ -0,0 +1,45 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation with break-inside: avoid. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 5; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 10px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 500px; + } + #flex > div { + background: green; + width: 10px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div id="flex"> + <div style="height: 250px; break-inside: avoid;"></div> + <div style="height: 200px; break-inside: avoid;"></div> + <div style="height: 120px; break-inside: avoid;"></div> + <div style="height: 180px; break-inside: avoid;"></div> + <div style="height: 100px; break-inside: avoid;"></div> + </div> + <div class="abs" style="top: 20px; left: 30px; height: 80px;"></div> + <div class="abs" style="top: 50px; left: 40px; height: 50px;"></div> + <div class="abs" style="top: 80px; left: 70px; height: 20px;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html new file mode 100644 index 0000000..a491dfa --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation with break-before: avoid. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 5; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 5px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 500px; + } + #flex > div { + background: green; + width: 5px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="top: 50px; height: 50px;"></div> + <div class="abs" style="top: 50px; left: 25px; height: 50px;"></div> + <div id="flex"> + <div style="height: 50px;"></div> + <div style="height: 50px;"></div> + <div style="height: 350px; break-before: avoid;"></div> + <div style="height: 100px;"></div> + <div style="height: 50px;"></div> + <div style="height: 50px;"></div> + <div style="height: 250px; break-before: avoid;"></div> + <div style="height: 350px; break-before: avoid;"></div> + <div style="height: 150px; break-before: avoid;"></div> + <div style="height: 500px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html new file mode 100644 index 0000000..414326a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation with break-before: avoid. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 5; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 5px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 500px; + } + #flex > div { + background: green; + width: 5px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="top: 50px; height: 50px;"></div> + <div class="abs" style="top: 50px; left: 25px; height: 50px;"></div> + <div id="flex"> + <div style="height: 50px;"></div> + <div style="height: 50px;"></div> + <div style="height: 350px; break-before: avoid;"></div> + <div style="height: 100px;"></div> + <div style="height: 50px;"></div> + <div style="height: 50px;"></div> + <div style="height: 250px; break-before: avoid;"></div> + <div style="height: 400px;"></div> + <div style="height: 100px; break-before: avoid;"></div> + <div style="height: 500px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html new file mode 100644 index 0000000..18030dd0 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation with break-after: avoid. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 5; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 5px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 500px; + } + #flex > div { + background: green; + width: 5px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="top: 50px; height: 50px;"></div> + <div class="abs" style="top: 50px; left: 25px; height: 50px;"></div> + <div id="flex"> + <div style="height: 50px;"></div> + <div style="height: 50px; break-after: avoid;"></div> + <div style="height: 350px;"></div> + <div style="height: 100px;"></div> + <div style="height: 50px;"></div> + <div style="height: 50px; break-after: avoid;"></div> + <div style="height: 250px;"></div> + <div style="height: 400px; break-after: avoid;"></div> + <div style="height: 100px; break-after: avoid;"></div> + <div style="height: 500px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html new file mode 100644 index 0000000..833da1a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html
@@ -0,0 +1,46 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation with break-before: column. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 5; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 10px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 500px; + } + #flex > div { + background: green; + width: 10px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="top: 50px; height: 50px;"></div> + <div class="abs" style="top: 50px; left: 30px; height: 50px;"></div> + <div id="flex"> + <div style="height: 50px;"></div> + <div style="height: 50px; break-before: column;"></div> + <div style="height: 350px;"></div> + <div style="height: 100px;"></div> + <div style="height: 50px;"></div> + <div style="height: 50px; break-before: column;"></div> + <div style="height: 250px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html new file mode 100644 index 0000000..60f543b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html
@@ -0,0 +1,46 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation with break-after: column. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 5; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 10px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 500px; + } + #flex > div { + background: green; + width: 10px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="top: 50px; height: 50px;"></div> + <div class="abs" style="top: 50px; left: 30px; height: 50px;"></div> + <div id="flex"> + <div style="height: 50px; break-after: column;"></div> + <div style="height: 50px;"></div> + <div style="height: 350px;"></div> + <div style="height: 100px;"></div> + <div style="height: 50px; break-after: column;"></div> + <div style="height: 50px;"></div> + <div style="height: 250px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html new file mode 100644 index 0000000..626d267 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation: break-before values on the first item + are propagated to the flex container. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 5; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 20px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 400px; + } + #flex > div { + background: green; + width: 10px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="top: 50px; height: 50px;"></div> + <div style="width: 20px; height: 50px; background: green;"></div> + <div style="width: 20px; height: 50px; background: green;"></div> + <div id="flex"> + <div style="height: 50px;"></div> + <div style="height: 50px;"></div> + <div style="height: 250px;"></div> + <div style="height: 100px; break-before: avoid;"></div> + <div style="height: 50px;"></div> + <div style="height: 50px;"></div> + <div style="height: 150px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html new file mode 100644 index 0000000..4cd53ad --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html
@@ -0,0 +1,44 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation: break-after values on the first item + are propagated to the flex container. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 2; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 50px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 50px; + } + #flex > div { + background: green; + width: 25x; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="top: 50px; height: 50px;"></div> + <div style="width: 50px; height: 50px; background: green;"></div> + <div id="flex"> + <div style="height: 25px;"></div> + <div style="height: 25px; break-after: avoid;"></div> + <div style="height: 50px;"></div> + </div> + <div style="width: 50px; height: 50px; background: green;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html new file mode 100644 index 0000000..93dd9b4 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation: break-before values on the first item + are propagated to the flex container. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 5; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 20px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 350px; + } + #flex > div { + background: green; + width: 10px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="top: 50px; height: 50px;"></div> + <div style="width: 20px; height: 50px; background: green;"></div> + <div id="flex"> + <div style="height: 50px; break-before: avoid;"></div> + <div style="height: 50px;"></div> + <div style="height: 250px;"></div> + <div style="height: 100px; break-before: column;"></div> + <div style="height: 50px;"></div> + <div style="height: 50px;"></div> + <div style="height: 150px;"></div> + </div> + <div style="width: 20px; height: 50px; background: green;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html new file mode 100644 index 0000000..204cc15 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html
@@ -0,0 +1,44 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation: break-after values on the first item + are propagated to the flex container. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 2; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 50px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 50px; + } + #flex > div { + background: green; + width: 25x; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="top: 50px; height: 50px;"></div> + <div id="flex"> + <div style="height: 25px;"></div> + <div style="height: 25px; break-after: column;"></div> + <div style="height: 50px; break-after: avoid;"></div> + </div> + <div style="width: 50px; height: 50px; background: green;"></div> + <div style="width: 50px; height: 50px; background: green;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-027.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-027.html new file mode 100644 index 0000000..95b7390 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-027.html
@@ -0,0 +1,45 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation: early break inside columns. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 2; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 50px; + background: green; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 200px; + } + #flex > div { + background: green; + width: 25px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="top: 50px; height: 50px;"></div> + <div class="abs" style="left: 100px; height: 50px; background: white;"></div> + <div id="flex"> + <div style="height: 50px;"></div> + <div style="height: 50px;"></div> + <div style="height: 100px; break-before: avoid;"></div> + <div style="height: 50px;"></div> + <div style="height: 50px; break-after: avoid;"></div> + <div style="height: 100px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-028.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-028.html new file mode 100644 index 0000000..d96cd56 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-028.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation: early break inside columns. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 2; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + .abs { + position: absolute; + width: 50px; + background: white; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 200px; + } + #flex > div { + background: green; + width: 25px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div class="abs" style="left: 100px; height: 50px;"></div> + <div id="flex"> + <div> + <div style="height: 50px; width: 25px;"></div> + <div style="height: 50px; width: 25px;"></div> + </div> + <div style="height: 100px; break-before: avoid;"></div> + <div> + <div style="height: 50px; width: 25px;"></div> + <div style="height: 50px; width: 25px; break-after: avoid;"></div> + </div> + <div style="height: 100px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-029.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-029.html new file mode 100644 index 0000000..ec47c9f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-029.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation: we shouldn't insert a forced break if + there's no preceding content at the start of a fragmentainer. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 2; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div style="display: flex; flex-direction: column; flex-wrap: wrap; width: 50px; height: 200px;"> + <div style="height: 100px; width: 25px; break-before: column; background: green;"></div> + <div style="height: 100px; width: 25px; break-before: column; background: green;"></div> + <div style="height: 50px; width: 25px; break-before: column; background: green;"></div> + <div style="height: 150px; width: 25px; background: green;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-030.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-030.html new file mode 100644 index 0000000..4439845 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-030.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation: the flex container consumes the + remaining fragmentainer space if an item breaks before. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 2; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div style="display: flex; flex-direction: column; flex-wrap: wrap; width: 50px; height: 200px; background: green;"> + <div style="height: 10px; width: 25px;"></div> + <div style="height: 100px; width: 25px; break-before: column;"></div> + <div style="height: 50px; width: 25px;"></div> + <div style="height: 100px; width: 25px; break-before: column;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html new file mode 100644 index 0000000..fc88e70 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation with break-inside: avoid. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 5; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 500px; + position: relative; + } + #flex > div { + background: green; + width: 10px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div id="flex"> + <div> + <div style="contain: size; width: 10px; height: 90px;"></div> + <div style="contain: size; width: 10px; height: 80px;"></div> + </div> + <div style="height: 30px; break-inside: avoid;"></div> + <div style="height: 170px;"></div> + <div style="height: 100px;"></div> + <div> + <div style="contain: size; width: 10px; height: 90px;"></div> + <div style="contain: size; width: 10px; height: 80px;"></div> + </div> + <div style="height: 30px; break-inside: avoid;"></div> + <div style="height: 170px;"></div> + <div style="height: 100px;"></div> + <div style="position: absolute; height: 20px; width: 20px; top: 180px;"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html new file mode 100644 index 0000000..978dbd1 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<title> + Multi-line column flex fragmentation with forced break and negative margin. +</title> +<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .multicol { + column-count: 5; + column-fill: auto; + column-gap: 0px; + height: 100px; + width: 100px; + position: relative; + background: red; + z-index: -1; + } + #flex { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 500px; + position: relative; + } + #flex > div { + background: green; + width: 10px; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="multicol"> + <div id="flex"> + <div style="height: 150px;"></div> + <div style="height: 350px; break-before: column; margin-top: -50px;"></div> + <div style="height: 150px;"></div> + <div style="height: 300px; break-before: column;"></div> + </div> + <div style="position: absolute; top: 50px; left: 20px; width: 20px; height: 50px; background: green;"></div> + <div style="position: absolute; top: -50px; left: 40px; width: 10px; height: 50px; background: white;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/system-color-consistency.html b/third_party/blink/web_tests/external/wpt/css/css-color/system-color-consistency.html new file mode 100644 index 0000000..f8fc5ef0 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-color/system-color-consistency.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <title>CSS Color 4: Computed value of color-contrast()</title> + <link rel="author" title="Jan Keitel" href="mailto:jkeitel@google.com"> + <link rel="help" href="https://www.w3.org/TR/css-color-4/#css-system-colors"> + <meta name="assert" content="computed style of form elements is consistent with definition of system colors"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/css/support/computed-testcommon.js"></script> +</head> + +<body> + <div id="target"></div> + <!-- Reference elements --> + <!-- Buttons --> + <div id="buttons"> + <button name="button"></button><input type="submit" name="submit button"><input type="reset" name="reset button"> + </div> + <script> + for (let element of document.getElementById("buttons").children) { + style = document.defaultView.getComputedStyle(element); + test_computed_value(`color`, `ButtonText`, style.getPropertyValue('color'), 'has the same color as text on a ' + element.name); + } + // Test with no specified target contrast + </script> +</body> + +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/layout-dependent-focus-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/layout-dependent-focus-expected.txt deleted file mode 100644 index c69221b..0000000 --- a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/layout-dependent-focus-expected.txt +++ /dev/null
@@ -1,4 +0,0 @@ -This is a testharness.js-based test. -FAIL Verify that onblur is called on hidden input assert_unreached: Event listener for 'blur' not called Reached unreachable code -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch-ref.html new file mode 100644 index 0000000..33e8669d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch-ref.html
@@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html><head> + <meta charset="utf-8"> + <title>CSS Grid Test: The subgrid is always stretched in its subgridded dimension(s)</title> + <link rel="author" title="Matt Woodrow" href="mailto:mattwoodrow@apple.com"> + <link rel="help" href="https://drafts.csswg.org/css-grid-2/#subgrid-box-alignment"> + <style> + body { + margin: 0; + } + .grid { + display: inline-block; + width: 100px; + height: 100px; + background-color: blue; + } + </style> +</head> +<body> + <div class="grid"></div> + <div class="grid"></div> + <div class="grid"></div> + <div class="grid"></div> + + <div class="grid"></div> + <div class="grid"></div> + <div class="grid"></div> + <div class="grid"></div> + + <br> + + <div class="grid"></div> + <div class="grid"></div> + <div class="grid"></div> + <div class="grid"></div> + <div class="grid"></div> + <div class="grid"></div> + <div class="grid"></div> + <div class="grid"></div> + +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch.html new file mode 100644 index 0000000..321e12d2 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch.html
@@ -0,0 +1,96 @@ +<!DOCTYPE HTML> +<html><head> + <meta charset="utf-8"> + <title>CSS Grid Test: The subgrid is always stretched in its subgridded dimension(s)</title> + <link rel="author" title="Matt Woodrow" href="mailto:mattwoodrow@apple.com"> + <link rel="help" href="https://drafts.csswg.org/css-grid-2/#subgrid-box-alignment"> + <link rel="match" href="subgrid-stretch-ref.html"> + <style> + body { + margin: 0; + } + .grid { + display: inline-grid; + grid-template-columns: 100px; + grid-template-rows: 100px; + } + .subgrid { + display: grid; + background-color: blue; + } + .rows { + grid-template-rows: subgrid; + align-self: baseline; + } + .columns { + grid-template-columns: subgrid; + justify-self: baseline; + } + .vrl { + writing-mode: vertical-rl; + } + .vrl.rows { + align-self: initial; + justify-self: baseline; + } + .vrl.columns { + justify-self: initial; + align-self: baseline; + } + </style> +</head> +<body> + <div class="grid"> + <div class="subgrid rows" style="height: 50px;"></div> + </div> + <div class="grid"> + <div class="subgrid rows" style="height: 150px;"></div> + </div> + <div class="grid"> + <div class="subgrid rows" style="max-height: 50px;"></div> + </div> + <div class="grid"> + <div class="subgrid rows" style="min-height: 150px;"></div> + </div> + <div class="grid"> + <div class="subgrid columns" style="width: 50px;"></div> + </div> + <div class="grid"> + <div class="subgrid columns" style="width: 150px;"></div> + </div> + <div class="grid"> + <div class="subgrid columns" style="max-width: 50px;"></div> + </div> + <div class="grid"> + <div class="subgrid columns" style="min-width: 150px;"></div> + </div> + + <br> + + <div class="grid"> + <div class="subgrid vrl rows" style="width: 50px;"></div> + </div> + <div class="grid"> + <div class="subgrid vrl rows" style="width: 150px;"></div> + </div> + <div class="grid"> + <div class="subgrid vrl rows" style="max-width: 50px;"></div> + </div> + <div class="grid"> + <div class="subgrid vrl rows" style="min-width: 150px;"></div> + </div> + <div class="grid"> + <div class="subgrid vrl columns" style="height: 50px;"></div> + </div> + <div class="grid"> + <div class="subgrid vrl columns" style="height: 150px;"></div> + </div> + <div class="grid"> + <div class="subgrid vrl columns" style="max-height: 50px;"></div> + </div> + <div class="grid"> + <div class="subgrid vrl columns" style="min-height: 150px;"></div> + </div> + +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-computed.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-computed.html index 3f253c3..a8255137 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-computed.html +++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-computed.html
@@ -14,6 +14,11 @@ <script> // <single-transition> = [ none | <single-transition-property> ] || // <time> || <easing-function> || <time> + +test(() => { + assert_equals(getComputedStyle(document.getElementById('target')).transition, "all 0s ease 0s"); +}, "Default transition value"); + test_computed_value("transition", "1s", "all 1s ease 0s"); test_computed_value("transition", "cubic-bezier(0, -2, 1, 3)", "all 0s cubic-bezier(0, -2, 1, 3) 0s"); test_computed_value("transition", "1s -3s", "all 1s ease -3s");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/contain.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/contain.html index 0202da4e..f3f50f8 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/contain.html +++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/contain.html
@@ -21,6 +21,7 @@ { syntax: 'layout' }, { syntax: 'style' }, { syntax: 'paint' }, + { syntax: 'inline-size' }, ]); runUnsupportedPropertyTests('contain', [
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/crashtests/broken-reference-crash-001.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/crashtests/broken-reference-crash-001.html new file mode 100644 index 0000000..6e18e06 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/crashtests/broken-reference-crash-001.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: non-existent filters should not crash</title> + <link rel="author" title="Philip Rogers" href="mailto:pdr@chromium.org"> + <link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#FilterProperty"> + <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1304748"> + <meta name="assert" content="Check that a non-existent filter with an animation does not crash."/> + <style> + @keyframes filter_keyframes { + 0% { filter: blur(5px); } + 100% { filter: blur(10px); } + } + #animating { + font-size: 36pt; + animation: filter_keyframes 999s linear 999s; + will-change: contents; + filter: url("#doesotexist"); + } + #child { + width: 10px; + height: 10px; + transform: translateX(10px); + } + #force_document_scroll { + width: 2000px; + height: 2000px; + } + </style> + +</head> +<body> + <div id="animating"> + <div id="child">PASS</div> + </div> + <div id="force_document_scroll"></div> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md index dd751c3..37ceb1c 100644 --- a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md +++ b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
@@ -167,7 +167,7 @@ .setContext(frames[0]) .keyDown("p") .keyUp("p"); -actions.send(); +await actions.send(); ``` Note that if an action uses an element reference, the context will be
diff --git a/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https-expected.txt b/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https-expected.txt new file mode 100644 index 0000000..3a82cfc --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Query "geolocation" powerful feature assert_equals: permission's state must be "prompt" by default expected "prompt" but got "denied" +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https.html b/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https.html new file mode 100644 index 0000000..40628433 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https.html
@@ -0,0 +1,14 @@ +<!doctype html> +<meta charset=utf-8> +<title>Test geolocation is a powerful feature via Permissions API</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> + +<script> +promise_test(async (test) => { + const status = await navigator.permissions.query({ name: "geolocation" }); + assert_true(status instanceof PermissionStatus); + assert_equals(status.name, "geolocation", `permission's name must be "geolocation"`); + assert_equals(status.state, "prompt", `permission's state must be "prompt" by default`); +}, `Query "geolocation" powerful feature`); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/editing/the-hidden-attribute/hidden-idl.html b/third_party/blink/web_tests/external/wpt/html/editing/the-hidden-attribute/hidden-idl.html new file mode 100644 index 0000000..331b63f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/editing/the-hidden-attribute/hidden-idl.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://github.com/whatwg/html/pull/7475"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div>hello</div> +<script> +const div = document.querySelector('div'); + +function runPropertyTest(assignedValue, expectedValue, expectedAttribute) { + test(() => { + div.hidden = assignedValue; + assert_equals(div.hidden, expectedValue, + `div.hidden = ${JSON.stringify(assignedValue)} should return ${JSON.stringify(expectedValue)}`); + assert_equals(div.getAttribute('hidden'), expectedAttribute, + `div.hidden = ${JSON.stringify(assignedValue)} should set the hidden attribute to ${JSON.stringify(expectedAttribute)}`); + }, `div.hidden = ${Number.isNaN(assignedValue) ? 'NaN' : JSON.stringify(assignedValue)}`); +} + +function runAttributeTest(assignedAttribute, expectedValue) { + test(() => { + div.setAttribute('hidden', assignedAttribute); + assert_equals(div.hidden, expectedValue); + }, `div.setAttribute('hidden', ${JSON.stringify(assignedAttribute)}) should make div.hidden return ${JSON.stringify(expectedValue)}`); +} + +runPropertyTest(false, false, null); +runPropertyTest(true, true, ''); +runPropertyTest('foo', true, ''); +runPropertyTest('false', true, ''); +runPropertyTest('', false, null); + +runAttributeTest('false', true); +runAttributeTest('foo', true); + +runPropertyTest('until-found', 'until-found', 'until-found'); +runPropertyTest('UNTIL-FOUND', 'until-found', 'until-found'); +runPropertyTest('UnTiL-FoUnD', 'until-found', 'until-found'); +runPropertyTest('unt\u0131l-found', true, ''); +runPropertyTest('unt\u0130l-found', true, ''); + +runPropertyTest(null, false, null); +runPropertyTest(undefined, false, null); + +runPropertyTest(1, true, ''); +runPropertyTest(0, false, null); +runPropertyTest(NaN, false, null); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js b/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js index ef097961..3e5ba74 100644 --- a/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js +++ b/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js
@@ -29,7 +29,7 @@ * .keyDown("p") * .keyUp("p"); * - * actions.send(); + * await actions.send(); * * @param {number} [defaultTickDuration] - The default duration of a * tick. Be default this is set ot 16ms, which is one frame time
diff --git a/third_party/blink/web_tests/external/wpt/resources/testharness.js b/third_party/blink/web_tests/external/wpt/resources/testharness.js index fcff33a..a6f48e1f9 100644 --- a/third_party/blink/web_tests/external/wpt/resources/testharness.js +++ b/third_party/blink/web_tests/external/wpt/resources/testharness.js
@@ -648,7 +648,7 @@ /** * Create a promise test. * - * Promise tests are tests which are represeted by a promise + * Promise tests are tests which are represented by a promise * object. If the promise is fulfilled the test passes, if it's * rejected the test fails, otherwise the test passes. *
diff --git a/third_party/blink/web_tests/fast/borders/border-radius-with-box-shadow-expected.png b/third_party/blink/web_tests/fast/borders/border-radius-with-box-shadow-expected.png index e331c49b..355aa8c 100644 --- a/third_party/blink/web_tests/fast/borders/border-radius-with-box-shadow-expected.png +++ b/third_party/blink/web_tests/fast/borders/border-radius-with-box-shadow-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/fast/gradients/unprefixed-repeating-gradient-color-hint.html b/third_party/blink/web_tests/fast/gradients/unprefixed-repeating-gradient-color-hint.html index 65d8434..248b3cd1 100644 --- a/third_party/blink/web_tests/fast/gradients/unprefixed-repeating-gradient-color-hint.html +++ b/third_party/blink/web_tests/fast/gradients/unprefixed-repeating-gradient-color-hint.html
@@ -11,7 +11,7 @@ } .linear1 { - background-image: repeating-linear-gradient(to right, red 90%, 95%, blue 105%); + background-image: repeating-linear-gradient(to right, green 10%, 20%, blue 105%); } .linear2 {
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-events.https.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-events.https.html index b14f9ba..e7ae400b4 100644 --- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-events.https.html +++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-events.https.html
@@ -5,76 +5,74 @@ <script> 'use strict'; -['plan-b', 'unified-plan'].forEach(sdpSemantics => { - promise_test(async t => { - console.log(sdpSemantics + ': onaddstream fires for new remote streams.'); - const pc1 = new RTCPeerConnection({sdpSemantics:sdpSemantics}); - t.add_cleanup(() => { pc1.close(); }); - const pc2 = new RTCPeerConnection({sdpSemantics:sdpSemantics}); - t.add_cleanup(() => { pc2.close(); }); +promise_test(async t => { + console.log('onaddstream fires for new remote streams.'); + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => { pc1.close(); }); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => { pc2.close(); }); - const localStream = await navigator.mediaDevices.getUserMedia({audio:true}); - const [localTrack] = localStream.getTracks(); - pc1.addTrack(localTrack, localStream); + const localStream = await navigator.mediaDevices.getUserMedia({audio:true}); + const [localTrack] = localStream.getTracks(); + pc1.addTrack(localTrack, localStream); - const streamWatcher = new EventWatcher(t, pc2, ['addstream']); - const addstreamEvent = streamWatcher.wait_for('addstream'); - await performOfferAnswer(pc1, pc2); - const remoteStream = (await addstreamEvent).stream; - assert_equals(remoteStream.getTracks().length, 1, - 'The stream should contain a track.'); - }, sdpSemantics + ': onaddstream fires for new remote streams.'); + const streamWatcher = new EventWatcher(t, pc2, ['addstream']); + const addstreamEvent = streamWatcher.wait_for('addstream'); + await performOfferAnswer(pc1, pc2); + const remoteStream = (await addstreamEvent).stream; + assert_equals(remoteStream.getTracks().length, 1, + 'The stream should contain a track.'); +}, 'onaddstream fires for new remote streams.'); - promise_test(async t => { - console.log(sdpSemantics + ': onremovestream for removed remote streams.'); - const pc1 = new RTCPeerConnection({sdpSemantics:sdpSemantics}); - t.add_cleanup(() => { pc1.close(); }); - const pc2 = new RTCPeerConnection({sdpSemantics:sdpSemantics}); - t.add_cleanup(() => { pc2.close(); }); +promise_test(async t => { + console.log('onremovestream for removed remote streams.'); + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => { pc1.close(); }); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => { pc2.close(); }); - const localStream = await navigator.mediaDevices.getUserMedia({audio:true}); - const [localTrack] = localStream.getTracks(); - const sender = pc1.addTrack(localTrack, localStream); + const localStream = await navigator.mediaDevices.getUserMedia({audio:true}); + const [localTrack] = localStream.getTracks(); + const sender = pc1.addTrack(localTrack, localStream); - const streamWatcher = new EventWatcher(t, pc2, ['addstream', - 'removestream']); - const addstreamEvent = streamWatcher.wait_for('addstream'); - await performOfferAnswer(pc1, pc2); - const remoteStream = (await addstreamEvent).stream; - const removestreamEvent = streamWatcher.wait_for('removestream'); + const streamWatcher = new EventWatcher(t, pc2, ['addstream', + 'removestream']); + const addstreamEvent = streamWatcher.wait_for('addstream'); + await performOfferAnswer(pc1, pc2); + const remoteStream = (await addstreamEvent).stream; + const removestreamEvent = streamWatcher.wait_for('removestream'); - pc1.removeTrack(sender); - await performOfferAnswer(pc1, pc2); - const removedRemoteStream = (await removestreamEvent).stream; - assert_equals(removedRemoteStream, remoteStream); - }, sdpSemantics + ': onremovestream for removed remote streams.'); + pc1.removeTrack(sender); + await performOfferAnswer(pc1, pc2); + const removedRemoteStream = (await removestreamEvent).stream; + assert_equals(removedRemoteStream, remoteStream); +}, 'onremovestream for removed remote streams.'); - promise_test(async t => { - console.log(sdpSemantics + ': onaddstream for a re-added remote stream.'); - const pc1 = new RTCPeerConnection({sdpSemantics:sdpSemantics}); - t.add_cleanup(() => { pc1.close(); }); - const pc2 = new RTCPeerConnection({sdpSemantics:sdpSemantics}); - t.add_cleanup(() => { pc2.close(); }); +promise_test(async t => { + console.log('onaddstream for a re-added remote stream.'); + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => { pc1.close(); }); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => { pc2.close(); }); - const localStream = await navigator.mediaDevices.getUserMedia({audio:true}); - const [localTrack] = localStream.getTracks(); - const sender = pc1.addTrack(localTrack, localStream); + const localStream = await navigator.mediaDevices.getUserMedia({audio:true}); + const [localTrack] = localStream.getTracks(); + const sender = pc1.addTrack(localTrack, localStream); - const streamWatcher = new EventWatcher(t, pc2, ['addstream']); - let addstreamEvent = streamWatcher.wait_for('addstream'); - await performOfferAnswer(pc1, pc2); - const remoteStream = (await addstreamEvent).stream; + const streamWatcher = new EventWatcher(t, pc2, ['addstream']); + let addstreamEvent = streamWatcher.wait_for('addstream'); + await performOfferAnswer(pc1, pc2); + const remoteStream = (await addstreamEvent).stream; - pc1.removeTrack(sender); - await performOfferAnswer(pc1, pc2); + pc1.removeTrack(sender); + await performOfferAnswer(pc1, pc2); - pc1.addTrack(localTrack, localStream); - addstreamEvent = streamWatcher.wait_for('addstream'); - await performOfferAnswer(pc1, pc2); - const readdedRemoteStream = (await addstreamEvent).stream; - assert_equals(readdedRemoteStream.id, remoteStream.id); - }, sdpSemantics + ': onaddstream for a re-added remote stream.'); -}); + pc1.addTrack(localTrack, localStream); + addstreamEvent = streamWatcher.wait_for('addstream'); + await performOfferAnswer(pc1, pc2); + const readdedRemoteStream = (await addstreamEvent).stream; + assert_equals(readdedRemoteStream.id, remoteStream.id); +}, 'onaddstream for a re-added remote stream.'); async function performOfferAnswer(pc1, pc2) { const offer = await pc1.createOffer();
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-sdpSemantics.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-sdpSemantics.html deleted file mode 100644 index 508cd20..0000000 --- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-sdpSemantics.html +++ /dev/null
@@ -1,188 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title>RTCPeerConnection.createOffer</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - </head> - <body> - <script> -// These tests are testing the non-standard "sdpFormat" extension. -promise_test(async t => { - const pc = new RTCPeerConnection({sdpSemantics: "plan-b"}); - t.add_cleanup(() => pc.close()); - return navigator.mediaDevices.getUserMedia({audio:true}) - .then(stream => { - stream2 = stream.clone(); - pc.addTrack(stream.getAudioTracks()[0]); - pc.addTrack(stream2.getAudioTracks()[0]); - return pc.createOffer(); - }) - .then(offer => { - let section_count = offer.sdp.match(/\nm=audio /g || []).length; - assert_equals(section_count, 1); - }); -}, 'Plan B createOffer creates only one media section for two audio tracks'); - -promise_test(async t => { - const pc = new RTCPeerConnection({sdpSemantics: "unified-plan"}); - t.add_cleanup(() => pc.close()); - return navigator.mediaDevices.getUserMedia({audio:true}) - .then(stream => { - stream2 = stream.clone(); - pc.addTrack(stream.getAudioTracks()[0]); - pc.addTrack(stream2.getAudioTracks()[0]); - return pc.createOffer(); - }) - .then(offer => { - let section_count = (offer.sdp.match(/\nm=audio /g) || []).length; - assert_equals(section_count, 2); - }); -}, 'Unified plan createOffer for audio creates two media sections ' + - 'for two audio tracks'); - -promise_test(async t => { - const pc = new RTCPeerConnection({sdpSemantics: "plan-b"}); - t.add_cleanup(() => pc.close()); - return navigator.mediaDevices.getUserMedia({video:true}) - .then(stream => { - stream2 = stream.clone(); - pc.addTrack(stream.getVideoTracks()[0]); - pc.addTrack(stream2.getVideoTracks()[0]); - return pc.createOffer(); - }) - .then(offer => { - let section_count = offer.sdp.match(/\nm=video /g || []).length; - assert_equals(section_count, 1); - }); -}, 'Plan B createOffer for video creates one media section for two ' + - 'video tracks'); - -promise_test(async t => { - const pc = new RTCPeerConnection({sdpSemantics: "unified-plan"}); - t.add_cleanup(() => pc.close()); - return navigator.mediaDevices.getUserMedia({video:true}) - .then(stream => { - stream2 = stream.clone(); - pc.addTrack(stream.getVideoTracks()[0]); - pc.addTrack(stream2.getVideoTracks()[0]); - return pc.createOffer(); - }) - .then(offer => { - let section_count = offer.sdp.match(/\nm=video /g || []).length; - assert_equals(section_count, 2); - }); -}, 'Unified plan createOffer for video creates two media sections ' + -'for two video tracks'); - -// TODO(hta): Add a test case with both audio and video tracks. - -promise_test(async t => { - const pc1 = new RTCPeerConnection({sdpSemantics: "plan-b"}); - t.add_cleanup(() => pc1.close()); - const pc2 = new RTCPeerConnection({sdpSemantics: "plan-b"}); - t.add_cleanup(() => pc2.close()); - return navigator.mediaDevices.getUserMedia({video: true}) - .then(stream => { - pc1.addTrack(stream.getVideoTracks()[0]); - return pc1.createOffer(); - }) - .then(offer => { - return Promise.all([pc2.setRemoteDescription(offer), - pc1.setLocalDescription(offer)]); - }) - .then(() => { - return pc2.createAnswer(); - }) - .then(answer => { - return Promise.all([pc2.setLocalDescription(answer), - pc1.setRemoteDescription(answer)]); - }); -}, 'Plan B can connect to itself'); - -promise_test(async t => { - const pc1 = new RTCPeerConnection({sdpSemantics: "unified-plan"}); - t.add_cleanup(() => pc1.close()); - const pc2 = new RTCPeerConnection({sdpSemantics: "unified-plan"}); - t.add_cleanup(() => pc2.close()); - return navigator.mediaDevices.getUserMedia({video: true}) - .then(stream => { - pc1.addTrack(stream.getVideoTracks()[0]); - return pc1.createOffer(); - }) - .then(offer => { - return Promise.all([pc2.setRemoteDescription(offer), - pc1.setLocalDescription(offer)]); - }) - .then(() => { - return pc2.createAnswer(); - }) - .then(answer => { - return Promise.all([pc2.setLocalDescription(answer), - pc1.setRemoteDescription(answer)]); - }); -}, 'Unified Plan can connect to itself'); - -promise_test(async t => { - const pc1 = new RTCPeerConnection({sdpSemantics: "unified-plan"}); - t.add_cleanup(() => pc1.close()); - const pc2 = new RTCPeerConnection({sdpSemantics: "plan-b"}); - t.add_cleanup(() => pc2.close()); - return navigator.mediaDevices.getUserMedia({video: true}) - .then(stream => { - pc1.addTrack(stream.getVideoTracks()[0]); - return pc1.createOffer(); - }) - .then(offer => { - return Promise.all([pc2.setRemoteDescription(offer), - pc1.setLocalDescription(offer)]); - }) - .then(() => { - return pc2.createAnswer(); - }) - .then(answer => { - return Promise.all([pc2.setLocalDescription(answer), - pc1.setRemoteDescription(answer)]); - }); - -}, 'Unified plan with one video track can connect to Plan B'); - -promise_test(async t => { - pc1 = new RTCPeerConnection({sdpSemantics: "plan-b"}); - t.add_cleanup(() => pc1.close()); - pc2 = new RTCPeerConnection({sdpSemantics: "unified-plan"}); - t.add_cleanup(() => pc2.close()); - return navigator.mediaDevices.getUserMedia({video: true}) - .then(stream => { - pc1.addTrack(stream.getVideoTracks()[0]); - return pc1.createOffer(); - }) - .then(offer => { - return Promise.all([pc2.setRemoteDescription(offer), - pc1.setLocalDescription(offer)]); - }) - .then(() => { - return pc2.createAnswer(); - }) - .then(answer => { - - return Promise.all([pc2.setLocalDescription(answer), - pc1.setRemoteDescription(answer)]); - }); - -}, 'Plan B with one video track can connect to Unified plan'); - -promise_test(async t => { - pc1 = new RTCPeerConnection({sdpSemantics: 'unified-plan'}); - t.add_cleanup(() => pc1.close()); - pc2 = new RTCPeerConnection({sdpSemantics: 'plan-b'}); - t.add_cleanup(() => pc2.close()); - pc1.addTransceiver('video'); - pc1.addTransceiver('video'); - await pc2.setRemoteDescription(await pc1.createOffer()); - assert_equals(pc1.getReceivers().length, 2); - assert_equals(pc2.getReceivers().length, 1); -}, 'Plan B rejects additional m-lines without throwing an error'); - </script> - </body> -</html>
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/shadow-multiple-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/shadow-multiple-expected.png index eee32fa..1d85574 100644 --- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/shadow-multiple-expected.png +++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/shadow-multiple-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/video-fixed-scrolling-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/video-fixed-scrolling-expected.png index 3c2f1a0..758f638c 100644 --- a/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/video-fixed-scrolling-expected.png +++ b/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/video-fixed-scrolling-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/css-animations/parsing/animation-computed-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/css-animations/parsing/animation-computed-expected.txt new file mode 100644 index 0000000..7cf62fe --- /dev/null +++ b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
@@ -0,0 +1,15 @@ +This is a testharness.js-based test. +FAIL Default animation value assert_equals: expected "0s ease 0s 1 normal none running none" but got "none 0s ease 0s 1 normal none running" +PASS Property animation value '1s' +PASS Property animation value 'cubic-bezier(0, -2, 1, 3)' +PASS Property animation value '1s -3s' +PASS Property animation value '4' +PASS Property animation value 'reverse' +PASS Property animation value 'both' +PASS Property animation value 'paused' +PASS Property animation value 'none' +PASS Property animation value 'anim' +PASS Property animation value 'anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)' +PASS Property animation value 'anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)' +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/virtual/threaded/external/wpt/css/css-animations/parsing/animation-computed-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/virtual/threaded/external/wpt/css/css-animations/parsing/animation-computed-expected.txt new file mode 100644 index 0000000..7cf62fe --- /dev/null +++ b/third_party/blink/web_tests/flag-specific/highdpi/virtual/threaded/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
@@ -0,0 +1,15 @@ +This is a testharness.js-based test. +FAIL Default animation value assert_equals: expected "0s ease 0s 1 normal none running none" but got "none 0s ease 0s 1 normal none running" +PASS Property animation value '1s' +PASS Property animation value 'cubic-bezier(0, -2, 1, 3)' +PASS Property animation value '1s -3s' +PASS Property animation value '4' +PASS Property animation value 'reverse' +PASS Property animation value 'both' +PASS Property animation value 'paused' +PASS Property animation value 'none' +PASS Property animation value 'anim' +PASS Property animation value 'anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)' +PASS Property animation value 'anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)' +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/visibility/visibility-simple-video-layer-expected.png index 6afdc3a..57a5729a2 100644 --- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/visibility/visibility-simple-video-layer-expected.png +++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/visibility/visibility-simple-video-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/devtools/bindings/jssourcemap-bindings-overlapping-sources.js b/third_party/blink/web_tests/http/tests/devtools/bindings/jssourcemap-bindings-overlapping-sources.js index 271580e..055b0f1 100644 --- a/third_party/blink/web_tests/http/tests/devtools/bindings/jssourcemap-bindings-overlapping-sources.js +++ b/third_party/blink/web_tests/http/tests/devtools/bindings/jssourcemap-bindings-overlapping-sources.js
@@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +const GC = async () => { + await TestRunner.evaluateInPageAsync(`new Promise(resolve => + GCController.asyncCollectAll(resolve))`); +}; + (async function () { TestRunner.addResult(`Verify that JavaScript SourceMap handle different sourcemap with overlapping sources.`); await TestRunner.loadTestModule('bindings_test_runner');
diff --git a/third_party/blink/web_tests/http/tests/devtools/bindings/sourcemap-navigator-multiple-frames.js b/third_party/blink/web_tests/http/tests/devtools/bindings/sourcemap-navigator-multiple-frames.js index 1b917ab..174713f 100644 --- a/third_party/blink/web_tests/http/tests/devtools/bindings/sourcemap-navigator-multiple-frames.js +++ b/third_party/blink/web_tests/http/tests/devtools/bindings/sourcemap-navigator-multiple-frames.js
@@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +const GC = async () => { + await TestRunner.evaluateInPageAsync(`new Promise(resolve => + GCController.asyncCollectAll(resolve))`); +}; + (async function () { TestRunner.addResult(`Verify that SourceMap sources are correctly displayed in navigator.\n`); await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/conversion/attribution-invalid-data.js b/third_party/blink/web_tests/http/tests/inspector-protocol/conversion/attribution-invalid-data.js index cd3db5e8..6511808 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/conversion/attribution-invalid-data.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/conversion/attribution-invalid-data.js
@@ -14,9 +14,17 @@ <!DOCTYPE html> <img src="https://devtools.test:8443/inspector-protocol/conversion/resources/conversion-redirect.php"></img> <img src="https://devtools.test:8443/inspector-protocol/conversion/resources/conversion-redirect.php?trigger-data=badinteger"></img>`); - const issues = await Promise.all(issuePromises); - testRunner.log(issues[0].params.issue, "Issue reported: ", ['requestId']); - testRunner.log(issues[1].params.issue, "Issue reported: ", ['requestId']); + + const issues = (await Promise.all(issuePromises)).map(issue => issue.params.issue); + + // In rare cases the issues arrive in reverse order. Lets sort them to be sure. + issues.sort((i1, i2) => { + return (i1.invalidParamter < i2.invalidParamter) ? -1 : + (i2.invalidParamter < i1.invalidParamter) ? 1 : 0; + }); + + testRunner.log(issues[0], "Issue reported: ", ['requestId']); + testRunner.log(issues[1], "Issue reported: ", ['requestId']); testRunner.completeTest(); })
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js index b2ade739..5a701e2 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js
@@ -10,7 +10,7 @@ return; } - testRunner.faiil(`FAIL: ${ + testRunner.fail(`FAIL: ${ description}: networkId !== requestId or one or more ids are missing (${ networkId} vs. ${requestId})`); }; @@ -43,15 +43,27 @@ }); }); - const [initScriptWillBeSentEvent, [ initScriptPausedEvent, subFetchWillBeSentEvent, subFetchPausedEvent ]] = + const [[ + initScriptWillBeSentEvent, initScriptPausedEvent, subFetchWillBeSentEvent, + subFetchPausedEvent + ]] = await Promise.all([ - dp.Network.onceRequestWillBeSent(e => e.params.request.url.endsWith('/service-worker-with-fetch.js')), workerProtocol.then(wdp => Promise.all([ - wdp.Fetch.onceRequestPaused(e => e.params.request.url.endsWith('/service-worker-with-fetch.js')), - wdp.Network.onceRequestWillBeSent(e => e.params.request.url.endsWith('/request-within-service-worker')), - wdp.Fetch.onceRequestPaused(e => e.params.request.url.endsWith('/request-within-service-worker')), + wdp.Network.onceRequestWillBeSent( + e => e.params.request.url.endsWith( + '/service-worker-with-fetch.js')), + wdp.Fetch.onceRequestPaused( + e => e.params.request.url.endsWith( + '/service-worker-with-fetch.js')), + wdp.Network.onceRequestWillBeSent( + e => e.params.request.url.endsWith( + '/request-within-service-worker')), + wdp.Fetch.onceRequestPaused( + e => e.params.request.url.endsWith( + '/request-within-service-worker')), ])), - page.navigate(testRunner.url('./resources/service-worker-with-fetch.html')), + page.navigate( + testRunner.url('./resources/service-worker-with-fetch.html')), ]); assertNetworkIdAlignment(
diff --git a/third_party/blink/web_tests/platform/linux/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/blink/web_tests/platform/linux/compositing/visibility/visibility-simple-video-layer-expected.png new file mode 100644 index 0000000..f17218b9 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/compositing/visibility/visibility-simple-video-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png b/third_party/blink/web_tests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png index ba9b4ef..0a6653d 100644 --- a/third_party/blink/web_tests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png +++ b/third_party/blink/web_tests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/css/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/linux/fast/css/shadow-multiple-expected.png index e7d09d54..0bf4b5b 100644 --- a/third_party/blink/web_tests/platform/linux/fast/css/shadow-multiple-expected.png +++ b/third_party/blink/web_tests/platform/linux/fast/css/shadow-multiple-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png b/third_party/blink/web_tests/platform/linux/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png deleted file mode 100644 index 2506bca..0000000 --- a/third_party/blink/web_tests/platform/linux/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/linux/images/yuv-decode-eligible/color-profile-layer-expected.png new file mode 100644 index 0000000..ea53e31 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/images/yuv-decode-eligible/color-profile-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/transforms/shadows-expected.png b/third_party/blink/web_tests/platform/linux/transforms/shadows-expected.png index 3a15e02..52c49c1 100644 --- a/third_party/blink/web_tests/platform/linux/transforms/shadows-expected.png +++ b/third_party/blink/web_tests/platform/linux/transforms/shadows-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/shadows-expected.png b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/shadows-expected.png new file mode 100644 index 0000000..52c49c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/shadows-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png index a37a161..8a11dec 100644 --- a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png +++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png index 8ec696c..c73f5ae9 100644 --- a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png +++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png new file mode 100644 index 0000000..4de7b2969 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index eb2b80a..5f012ce 100644 --- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index b9717090..ada90607 100644 --- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index b9717090..ada90607 100644 --- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/threaded/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/blink/web_tests/platform/linux/virtual/threaded/compositing/visibility/visibility-simple-video-layer-expected.png new file mode 100644 index 0000000..f17218b9 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/threaded/compositing/visibility/visibility-simple-video-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png index 77b66e55..775a8b4 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index 92df9800..9369e29 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png index e8f7d78..61c7d5d 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png index bdd9125..3b54d6f9 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index 322b0482..1173792 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png index 5b76563..295be6e 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png index c7efa486..81b5f09 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index 965b37f..4e3ced0 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png index fa25ea7..6fed204d7 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png index 9a92395..8f3aa59 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index 9359e5564..52819bd 100644 --- a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png index f3e933f..66cd3e9d 100644 --- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/blink/web_tests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png index 64458de..f7cc94e 100644 --- a/third_party/blink/web_tests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png +++ b/third_party/blink/web_tests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/css/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/mac/fast/css/shadow-multiple-expected.png index 1fb603f0..c7cfce3d 100644 --- a/third_party/blink/web_tests/platform/mac/fast/css/shadow-multiple-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/css/shadow-multiple-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png b/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png index eb9d753..1f9a4e4 100644 --- a/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/mac/images/yuv-decode-eligible/color-profile-layer-expected.png index 38c34f4..d866d9d 100644 --- a/third_party/blink/web_tests/platform/mac/images/yuv-decode-eligible/color-profile-layer-expected.png +++ b/third_party/blink/web_tests/platform/mac/images/yuv-decode-eligible/color-profile-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png index 831971c..1524b09 100644 --- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png +++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png index 2249366..0fa1a613 100644 --- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png +++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png index ca93aa5..da05ec4 100644 --- a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png +++ b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index b0d5eda5c..5d404c3f 100644 --- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/blink/web_tests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png index 843dbae..f17218b9 100644 --- a/third_party/blink/web_tests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png +++ b/third_party/blink/web_tests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png b/third_party/blink/web_tests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png index 11a7ad0..de9a177 100644 --- a/third_party/blink/web_tests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png +++ b/third_party/blink/web_tests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/css/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/win/fast/css/shadow-multiple-expected.png index 26f8393f..51ace56 100644 --- a/third_party/blink/web_tests/platform/win/fast/css/shadow-multiple-expected.png +++ b/third_party/blink/web_tests/platform/win/fast/css/shadow-multiple-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png b/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png index fb89d76..ad44d768 100644 --- a/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png +++ b/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/win/images/yuv-decode-eligible/color-profile-layer-expected.png index 3c38207..ea53e31 100644 --- a/third_party/blink/web_tests/platform/win/images/yuv-decode-eligible/color-profile-layer-expected.png +++ b/third_party/blink/web_tests/platform/win/images/yuv-decode-eligible/color-profile-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/transforms/shadows-expected.png b/third_party/blink/web_tests/platform/win/transforms/shadows-expected.png index ef14df0..5fafff95 100644 --- a/third_party/blink/web_tests/platform/win/transforms/shadows-expected.png +++ b/third_party/blink/web_tests/platform/win/transforms/shadows-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png index 15639ef..5b9ac852 100644 --- a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png +++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png index abd21bb4..8a5d1907 100644 --- a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png +++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png index fb8aa4c..4de7b2969 100644 --- a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png +++ b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index daafc9d..cd45df5 100644 --- a/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index d64bf62..dd9fc52 100644 --- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index d64bf62..dd9fc52 100644 --- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index 73cc1a2..19a3c67 100644 --- a/third_party/blink/web_tests/platform/win7/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index d9975d4a..c19eea8 100644 --- a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png index d9975d4a..c19eea8 100644 --- a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/resources/testdriver-actions.js b/third_party/blink/web_tests/resources/testdriver-actions.js index ef097961..3e5ba74 100644 --- a/third_party/blink/web_tests/resources/testdriver-actions.js +++ b/third_party/blink/web_tests/resources/testdriver-actions.js
@@ -29,7 +29,7 @@ * .keyDown("p") * .keyUp("p"); * - * actions.send(); + * await actions.send(); * * @param {number} [defaultTickDuration] - The default duration of a * tick. Be default this is set ot 16ms, which is one frame time
diff --git a/third_party/blink/web_tests/resources/testharness.js b/third_party/blink/web_tests/resources/testharness.js index ca0a92a..a6f48e1f9 100644 --- a/third_party/blink/web_tests/resources/testharness.js +++ b/third_party/blink/web_tests/resources/testharness.js
@@ -126,7 +126,7 @@ } catch (e) {} } } - if (supports_post_message(w) && w !== self) { + if (w !== self) { w.postMessage(message_arg, "*"); } }); @@ -648,7 +648,7 @@ /** * Create a promise test. * - * Promise tests are tests which are represeted by a promise + * Promise tests are tests which are represented by a promise * object. If the promise is fulfilled the test passes, if it's * rejected the test fails, otherwise the test passes. * @@ -4416,14 +4416,6 @@ const get_stack = function() { var stack = new Error().stack; - // IE11 does not initialize 'Error.stack' until the object is thrown. - if (!stack) { - try { - throw new Error(); - } catch (e) { - stack = e.stack; - } - } // 'Error.stack' is not supported in all browsers/versions if (!stack) { @@ -4652,43 +4644,6 @@ return "Untitled"; } - function supports_post_message(w) - { - var supports; - var type; - // Given IE implements postMessage across nested iframes but not across - // windows or tabs, you can't infer cross-origin communication from the presence - // of postMessage on the current window object only. - // - // Touching the postMessage prop on a window can throw if the window is - // not from the same origin AND post message is not supported in that - // browser. So just doing an existence test here won't do, you also need - // to wrap it in a try..catch block. - try { - type = typeof w.postMessage; - if (type === "function") { - supports = true; - } - - // IE8 supports postMessage, but implements it as a host object which - // returns "object" as its `typeof`. - else if (type === "object") { - supports = true; - } - - // This is the case where postMessage isn't supported AND accessing a - // window property across origins does NOT throw (e.g. old Safari browser). - else { - supports = false; - } - } catch (e) { - // This is the case where postMessage isn't supported AND accessing a - // window property across origins throws (e.g. old Firefox browser). - supports = false; - } - return supports; - } - /** * Setup globals */
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/event-handler-attributes.html b/third_party/blink/web_tests/virtual/mathml-disabled/event-handler-attributes.html new file mode 100644 index 0000000..82ad550 --- /dev/null +++ b/third_party/blink/web_tests/virtual/mathml-disabled/event-handler-attributes.html
@@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> +<body> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script> + ["click", "dblclick", "mouseover"].forEach(eventName => { + const attributeName = `on${eventName}`; + test(t => { + const el = document.createElementNS( + "http://www.w3.org/1998/Math/MathML", + "math" + ); + el.setAttribute(attributeName, `window.${eventName}Happened = true;`); + assert_equals(el[attributeName], null); + }, `The ${attributeName} property on the <math> element must be null when MathML is disabled.`); + test(t => { + const el = document.createElementNS( + "http://www.w3.org/1998/Math/MathML", + "math" + ); + let target = undefined; + el[attributeName] = (e) => { target = e.currentTarget; } + el.dispatchEvent(new Event(name)); + assert_equals(target, undefined); + }, `${eventName} should not be handled by the <math> element when MathML is disabled.`); + }); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline-expected.html b/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline-expected.html new file mode 100644 index 0000000..8fa7b21 --- /dev/null +++ b/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline-expected.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<script src="../../resources/ahem.js"></script> +<html> + <head> + <title>mtable baseline with MathML disabled (reference)</title> + <meta charset="utf-8"/> + <style> + math { + font: 25px/1 Ahem; + display: block; + } + mtext { + display: inline-block; + } + mtable { + display: inline-block; + } + mtr { + display: inline-block; + } + mtd { + display: inline-block; + padding: 0; + } + </style> + </head> + <body> + <p>When MathML is disabled, these three squares should be aligned:</p> + <math><mtext>X</mtext><mtable><mtr><mtd><mtext>X</mtext></mtd></mtr></mtable><mtext>X</mtext></math> + </body> +</html>
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline.html b/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline.html new file mode 100644 index 0000000..00cffd2 --- /dev/null +++ b/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<script src="../../resources/ahem.js"></script> +<html> + <head> + <title>mtable baseline with MathML disabled</title> + <meta charset="utf-8"/> + <style> + math { + font: 25px/1 Ahem; + display: block; + } + mtext { + display: inline-block; + } + mtable { + display: inline-table; + } + mtr { + display: table-row; + } + mtd { + display: table-cell; + padding: 0; + } + </style> + </head> + <body> + <p>When MathML is disabled, these three squares should be aligned:</p> + <math><mtext>X</mtext><mtable><mtr><mtd><mtext>X</mtext></mtd></mtr></mtable><mtext>X</mtext></math> + </body> +</html>
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes-expected.html b/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes-expected.html new file mode 100644 index 0000000..e7a54a68 --- /dev/null +++ b/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes-expected.html
@@ -0,0 +1,25 @@ +<!DOCTYPE html> +<script src="../../resources/ahem.js"></script> +<html> + <head> + <title>presentation attributes with MathML disabled</title> + <meta charset="utf-8"/> + <style> + math { + font: 25px/1 Ahem; + } + math, mtext { + display: inline-block; + } + </style> + </head> + <body> + <p>Presentation attributes should have no effects when MathML is disabled:</p> + <math><mtext>X</mtext><mtext>p</mtext></math> + <math><mtext>X</mtext></math> + <math><mtext>p</mtext></math> + <math><mtext>p</mtext></math> + <math style="text-transform: lowercase"><mtext>P</mtext></math> + <math><mtext style="font-size: math">X</mtext></math> + </body> +</html>
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes.html b/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes.html new file mode 100644 index 0000000..3fd2ef0 --- /dev/null +++ b/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes.html
@@ -0,0 +1,25 @@ +<!DOCTYPE html> +<script src="../../resources/ahem.js"></script> +<html> + <head> + <title>presentation attributes with MathML disabled</title> + <meta charset="utf-8"/> + <style> + math { + font: 25px/1 Ahem; + } + math, mtext { + display: inline-block; + } + </style> + </head> + <body> + <p>Presentation attributes should have no effects when MathML is disabled:</p> + <math dir="rtl"><mtext>X</mtext><mtext>p</mtext></math> + <math><mtext mathsize="200%">X</mtext></math> + <math mathbackground="red"><mtext>p</mtext></math> + <math mathcolor="red"><mtext>p</mtext></math> + <math style="text-transform: lowercase"><mtext mathvariant="normal">P</mtext></math> + <math><mtext style="font-size: math" scriptlevel="+1">X</mtext></math> + </body> +</html>
diff --git a/third_party/blink/web_tests/virtual/partitioned-cookies-first-party-sets/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt b/third_party/blink/web_tests/virtual/partitioned-cookies-first-party-sets/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt index b63df77..3631e54 100644 --- a/third_party/blink/web_tests/virtual/partitioned-cookies-first-party-sets/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt +++ b/third_party/blink/web_tests/virtual/partitioned-cookies-first-party-sets/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
@@ -210,14 +210,14 @@ Running test: getPartitionedCookie Num of cookies 2 -name: __Host-foo, value: bar, domain: example.test, path: /, secure, session, None -name: __Host-foo, value: bar, domain: devtools.test, path: /, secure, session +name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: https://devtools.test, secure, session, None +name: __Host-foo, value: bar, domain: devtools.test, path: /, partitionKey: https://devtools.test, secure, session Running test: deleteAllCookies Running test: getPartitionedCookieFromOpaqueOrigin Num of cookies 1 -name: __Host-foo, value: bar, domain: example.test, path: /, secure, session, None +name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: <opaque>, secure, session, None Running test: deleteAllCookies
diff --git a/third_party/blink/web_tests/virtual/partitioned-cookies/external/wpt/cookies/partitioned-cookies/partitioned-cookies.tentative.https-expected.txt b/third_party/blink/web_tests/virtual/partitioned-cookies/external/wpt/cookies/partitioned-cookies/partitioned-cookies.tentative.https-expected.txt index 2216b08..058e7fc 100644 --- a/third_party/blink/web_tests/virtual/partitioned-cookies/external/wpt/cookies/partitioned-cookies/partitioned-cookies.tentative.https-expected.txt +++ b/third_party/blink/web_tests/virtual/partitioned-cookies/external/wpt/cookies/partitioned-cookies/partitioned-cookies.tentative.https-expected.txt
@@ -4,7 +4,7 @@ PASS Partitioned cookies accessible on the top-level site they are created in via CookieStore PASS Cross-site window opened correctly PASS Partitioned cookies are not accessible on a different top-level site via HTTP -FAIL Partitioned cookies are not accessible on a different top-level site via DOM assert_equals: Expected __Host-pchttp to not be available on a different top-level site expected false but got true -FAIL Partitioned cookies are not accessible on a different top-level site via CookieStore assert_equals: Expected __Host-pchttp to not be available on a different top-level site expected false but got true +PASS Partitioned cookies are not accessible on a different top-level site via DOM +PASS Partitioned cookies are not accessible on a different top-level site via CookieStore Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/cookies/partitioned-cookies/clear-site-data.https-expected.txt b/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/cookies/partitioned-cookies/clear-site-data.https-expected.txt index 5021f0f..5e23b5e 100644 --- a/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/cookies/partitioned-cookies/clear-site-data.https-expected.txt +++ b/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/cookies/partitioned-cookies/clear-site-data.https-expected.txt
@@ -1,6 +1,9 @@ CONSOLE MESSAGE: Clear-Site-Data header on 'https://cookies.not-example.test:8443/cookies/resources/clear-site-data.php': Cleared data types: "cookies". Clearing channel IDs and HTTP authentication cache is currently not supported, as it breaks active network connections. +CONSOLE MESSAGE: Clear-Site-Data header on 'https://cookies.not-example.test:8443/cookies/resources/clear-site-data.php': Cleared data types: "cookies". Clearing channel IDs and HTTP authentication cache is currently not supported, as it breaks active network connections. This is a testharness.js-based test. PASS Clearing partitioned cookies -FAIL Clearing partitioned cookies in another partition assert_equals: expected "" but got "__Host-pc=0" +PASS Clearing partitioned cookies in another partition +PASS Opened page from first partition +PASS Clear-Site-Data does not leak across partitions Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt b/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt index 8939b78..199c2b5f 100644 --- a/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt +++ b/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
@@ -209,15 +209,14 @@ name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: https://notinset.test, secure, session Running test: getPartitionedCookie -Num of cookies 2 -name: __Host-foo, value: bar, domain: example.test, path: /, secure, session, None -name: __Host-foo, value: bar, domain: devtools.test, path: /, secure, session +Num of cookies 1 +name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: https://devtools.test, secure, session, None Running test: deleteAllCookies Running test: getPartitionedCookieFromOpaqueOrigin Num of cookies 1 -name: __Host-foo, value: bar, domain: example.test, path: /, secure, session, None +name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: <opaque>, secure, session, None Running test: deleteAllCookies
diff --git a/third_party/blink/web_tests/virtual/task-tracking/README.md b/third_party/blink/web_tests/virtual/task-tracking/README.md new file mode 100644 index 0000000..5c053acd --- /dev/null +++ b/third_party/blink/web_tests/virtual/task-tracking/README.md
@@ -0,0 +1 @@ +A virtual test suite testing task tracking using a non-web-exposed API (enabled by a flag).
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt similarity index 100% rename from third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/resources/iframe.html b/third_party/blink/web_tests/wpt_internal/task-tracking/resources/iframe.html new file mode 100644 index 0000000..dc546f33 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/task-tracking/resources/iframe.html
@@ -0,0 +1,6 @@ +<script> +window.onmessage = e => { + const port = e.ports[0]; + port.postMessage("Got the port"); +}; +</script>
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-posttask.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-posttask.html new file mode 100644 index 0000000..c7ab6bc --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-posttask.html
@@ -0,0 +1,49 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Verify that setTimeout tasks can be properly tracked.</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +promise_test(async t => { + const scriptId = scheduler.taskId; + + await scheduler.postTask(async () => { + // New task; ID should have changed. + const taskId1 = scheduler.taskId; + let taskId2; + assert_not_equals(scriptId, taskId1); + assert_equals(scheduler.isAncestor(scriptId), "ancestor"); + + await scheduler.postTask(() => { + // New task; IDs should have changed again. + taskId2 = scheduler.taskId; + assert_not_equals(taskId1, taskId2); + assert_equals(scheduler.isAncestor(scriptId), "ancestor"); + assert_equals(scheduler.isAncestor(taskId1), "ancestor"); + }); + + assert_equals(scheduler.isAncestor(taskId1), "ancestor"); + assert_equals(scheduler.isAncestor(taskId2), "not ancestor"); + }); +}, "postTask chain."); + +promise_test(async () => { + const initialId = scheduler.taskId; + await scheduler.postTask(async () => { + const currentTaskId = scheduler.taskId; + assert_equals(scheduler.isAncestor(initialId), "ancestor", + "initialId is an ancestor of the currentTaskId"); + await fetch("/resources/blank.html"); + assert_equals(scheduler.isAncestor(initialId), "ancestor", + "initialId is ancestor of fetch continuation."); + assert_equals(scheduler.isAncestor(currentTaskId), "ancestor", + "currentTaskId is an ancestor of fetch continuation"); + }); +}, "Single postTask with multiple internal awaited tasks"); +</script> +</body> +</html> \ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-promise.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-promise.html new file mode 100644 index 0000000..01a8a89 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-promise.html
@@ -0,0 +1,119 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Verify that promise tasks can be properly tracked.</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> + +promise_test(() => { + return new Promise(async (resolve, reject) => { + const initialId = scheduler.taskId; + await new Promise(r => setTimeout(r, 0)); + queueMicrotask(() => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject("Queued microtask is not a descendant"); + } + }); + }); +}, "A microtask is a descendant of the dispatching task"); + +promise_test(() => { + return new Promise((resolve, reject) => { + const initialId = scheduler.taskId; + fetch("/resources/blank.html") + .then(response => response.text()) + .then(body => { + return new Promise(interimResolve => { + interimResolve(); + }); + }) + .then(() => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject(); + } + }); + }); +}, "A paint task after fetching a resource using promises is a " + + "descendant of initiating task."); + +promise_test(() => { + return new Promise((resolve, reject) => { + const initialId = scheduler.taskId; + fetch("/resources/blank.html") + .then(response => response.text()) + .then(body => { + return new Promise(interimResolve => { + interimResolve(); + }); + }) + .then(() => { + return new Promise(interimResolve => setTimeout(interimResolve, 0)); + }) + .then(() => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject(); + } + }); + }); +}, "A paint task after fetching a resource using promises, including a JS " + + "created promise with a timeout, is a descendant of initiating task."); + +promise_test(() => { + return new Promise(async (resolve, reject) => { + const initialId = scheduler.taskId; + const response = await fetch("/resources/blank.html"); + const body = await response.text(); + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject(); + } + }); +}, "A paint task after fetching a resource using async await is a descendant " + + "of initiating task."); + +promise_test(() => { + return new Promise(async (resolve, reject) => { + const initialId = scheduler.taskId; + let promiseId; + await new Promise(r => setTimeout(r, 10)); + try { + await new Promise((r, rj) => { + promiseId = scheduler.taskId; + rj(); + }); + } catch { + // Carry on. Rejection is expected. The important thing is not to let it + // get to you. + } + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + } catch { + reject("InitialId is not an ancestor"); + } + try { + assert_equals(scheduler.isAncestor(promiseId), "ancestor"); + resolve(); + } catch { + reject("PromiseId is not an ancestor"); + } + }); +}, "Rejected promises properly handle ancestry."); + +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-setinterval.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-setinterval.html new file mode 100644 index 0000000..3d8df75 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-setinterval.html
@@ -0,0 +1,66 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Verify that setInterval tasks can be properly tracked.</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +promise_test(() => { + return new Promise(async (resolve, reject) => { + const initialId = scheduler.taskId; + await new Promise(resolve => setInterval(resolve, 0)); + queueMicrotask(() => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject("Not an ancestor"); + } + }); + }); +}, "An immediate setInterval microtask is a descendant of the dispatching task"); + +promise_test(() => { + return new Promise(async (resolve, reject) => { + const initialId = scheduler.taskId; + await new Promise(resolve => setInterval(resolve, 100)); + queueMicrotask(() => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject("Not an ancestor"); + } + }); + }); +}, "A long setInterval microtask is a descendant of the dispatching task"); + +promise_test(() => { + return new Promise(async (resolve, reject) => { + const initialId = scheduler.taskId; + let counter = 0; + await new Promise(resolve => setInterval(() => { + ++counter; + if (counter > 3) { + resolve(); + } + }, 100)); + queueMicrotask(() => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject("Not an ancestor"); + } + }); + }); +}, "A long setInterval that runs multiple times followed by a microtask is a descendant of the dispatching task"); + +</script> +</body> +</html> + +
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html new file mode 100644 index 0000000..e9681cd --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html
@@ -0,0 +1,78 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Verify that setTimeout tasks can be properly tracked.</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +promise_test(() => { + return new Promise(async (resolve, reject) => { + const initialId = scheduler.taskId; + await new Promise(resolve => setTimeout(resolve, 0)); + queueMicrotask(() => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject("Not an ancestor"); + } + }); + }); +}, "An immediate setTimeout microtask is a descendant of the dispatching task"); + +promise_test(() => { + return new Promise(async (resolve, reject) => { + const initialId = scheduler.taskId; + await new Promise(resolve => setTimeout(resolve, 100)); + queueMicrotask(() => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject("Not an ancestor"); + } + }); + }); +}, "A long setTimeout microtask is a descendant of the dispatching task"); + +promise_test(async () => { + let siblingId; + const initialId = scheduler.taskId; + await new Promise((resolve, reject) => setTimeout(() => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject("Initial task not identified as ancestor of first setTimeout"); + } + resolve(); + }, 10)); + await new Promise((resolve, reject) => setTimeout(() => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject("Initial task not identified as ancestor of second setTimeout"); + } + }, 10)); +}, "An async chain of setTimeouts task properly track their ancestors"); + +promise_test(async () => { + const initialId = scheduler.taskId; + return new Promise((resolve, reject) => { + try { + assert_equals(scheduler.isAncestor(initialId), "ancestor"); + resolve(); + } catch { + reject("The task is not its own ancestor"); + } + }); +}, "A task is its own ancestor"); + +</script> +</body> +</html> +
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js index 8d1461d..7bb9882 100644 --- a/third_party/closure_compiler/externs/file_manager_private.js +++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -22,6 +22,7 @@ TESTING: 'testing', SMB: 'smb', SYSTEM_INTERNAL: 'system_internal', + GUEST_OS: 'guest_os', }; /** @enum {string} */
diff --git a/third_party/dav1d/version/vcs_version.h b/third_party/dav1d/version/vcs_version.h index 88548ec..2f1a458 100644 --- a/third_party/dav1d/version/vcs_version.h +++ b/third_party/dav1d/version/vcs_version.h
@@ -1,2 +1,2 @@ /* auto-generated, do not edit */ -#define DAV1D_VERSION "0.9.2-172-gb1a5189" +#define DAV1D_VERSION "0.9.2-174-g56e7ffc"
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium index d95df12..c4005e8 100644 --- a/third_party/freetype/README.chromium +++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@ Name: FreeType URL: http://www.freetype.org/ -Version: VER-2-11-1-117-g1e2eb6504 -Revision: 1e2eb65048f75c64b68708efed6ce904c31f3b2f +Version: VER-2-11-1-118-g53dfdcd81 +Revision: 53dfdcd8198d2b3201a23c4bad9190519ba918db CPEPrefix: cpe:/a:freetype:freetype:2.11.1 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent JPEG Group) licenses"
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn index 0ba8944..e7db28e 100644 --- a/third_party/nearby/BUILD.gn +++ b/third_party/nearby/BUILD.gn
@@ -209,6 +209,7 @@ ":platform_base_cancellation_flag", ":platform_base_error_code_recorder", ":platform_base_util", + ":platform_impl_shared_file", ":platform_public_comm", ":platform_public_logging", ":platform_public_types", @@ -458,6 +459,7 @@ "src/internal/platform/input_stream.h", "src/internal/platform/listeners.h", "src/internal/platform/nsd_service_info.h", + "src/internal/platform/os_name.h", "src/internal/platform/output_stream.h", "src/internal/platform/payload_id.h", "src/internal/platform/prng.h",
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium index 8cc4678..25b548c 100644 --- a/third_party/nearby/README.chromium +++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@ Name: Nearby Connections Library Short Name: Nearby URL: https://github.com/google/nearby-connections -Version: 31b2aae6c5442c55d819e7a966b8dd996a0edcfa +Version: 517d77f5aa4fea8f4437125830cfc55f84705e3d License: Apache 2.0 License File: LICENSE Security Critical: yes
diff --git a/third_party/rust/autocxx/v0_16/crate/.cargo_vcs_info.json b/third_party/rust/autocxx/v0_16/crate/.cargo_vcs_info.json deleted file mode 100644 index 6a150cc8..0000000 --- a/third_party/rust/autocxx/v0_16/crate/.cargo_vcs_info.json +++ /dev/null
@@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "1c57dc961febd206a8046db5a9badac9f5f23b74" - }, - "path_in_vcs": "" -} \ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/workflows/clippy.yml b/third_party/rust/autocxx/v0_16/crate/.github/workflows/clippy.yml deleted file mode 100644 index b5fd94df..0000000 --- a/third_party/rust/autocxx/v0_16/crate/.github/workflows/clippy.yml +++ /dev/null
@@ -1,20 +0,0 @@ -name: Clippy -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] -jobs: - clippy_check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - components: clippy - override: true - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --all-features --tests
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/workflows/examples.yml b/third_party/rust/autocxx/v0_16/crate/.github/workflows/examples.yml deleted file mode 100644 index 23f8d51..0000000 --- a/third_party/rust/autocxx/v0_16/crate/.github/workflows/examples.yml +++ /dev/null
@@ -1,39 +0,0 @@ -name: Examples - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - name: Checkout with submodules - uses: actions/checkout@v2 - with: - submodules: recursive - - name: Build s2 example - working-directory: ./examples/s2 - run: cargo build --all --verbose - - name: Build steam example - working-directory: ./examples/steam-mini - run: cargo build --all --verbose - - name: Build subclass example - working-directory: ./examples/subclass - run: cargo build --all --verbose - - name: Build pod example - working-directory: ./examples/pod - run: cargo build --all --verbose - - name: Build chromium render-frame-host example - working-directory: ./examples/chromium-fake-render-frame-host - run: cargo build --all --verbose - - name: Build non-trivial-type-on-stack example - working-directory: ./examples/non-trivial-type-on-stack - run: cargo build --all --verbose
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/workflows/rust.yml b/third_party/rust/autocxx/v0_16/crate/.github/workflows/rust.yml deleted file mode 100644 index 1435c7a..0000000 --- a/third_party/rust/autocxx/v0_16/crate/.github/workflows/rust.yml +++ /dev/null
@@ -1,24 +0,0 @@ -name: Rust - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Build - run: cargo build --all --verbose - - name: Install creduce - run: sudo apt-get install creduce - - name: Run tests - run: cargo test --all --verbose
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/workflows/rustfmt.yml b/third_party/rust/autocxx/v0_16/crate/.github/workflows/rustfmt.yml deleted file mode 100644 index 7b5a9f4..0000000 --- a/third_party/rust/autocxx/v0_16/crate/.github/workflows/rustfmt.yml +++ /dev/null
@@ -1,19 +0,0 @@ -name: Rustfmt -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] -jobs: - format: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: rustfmt - override: true - - uses: mbrobbel/rustfmt-check@master - with: - token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/third_party/rust/autocxx/v0_16/crate/.gitignore b/third_party/rust/autocxx/v0_16/crate/.gitignore deleted file mode 100644 index fa8d85ac..0000000 --- a/third_party/rust/autocxx/v0_16/crate/.gitignore +++ /dev/null
@@ -1,2 +0,0 @@ -Cargo.lock -target
diff --git a/third_party/rust/autocxx/v0_16/crate/Cargo.toml.orig b/third_party/rust/autocxx/v0_16/crate/Cargo.toml.orig deleted file mode 100644 index d0113e6..0000000 --- a/third_party/rust/autocxx/v0_16/crate/Cargo.toml.orig +++ /dev/null
@@ -1,46 +0,0 @@ -# Copyright 2020 Google LLC -# -# 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 -# -# https://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. - -[package] -name = "autocxx" -version = "0.16.0" -authors = ["Adrian Taylor <adetaylor@chromium.org>"] -license = "MIT OR Apache-2.0" -description = "Safe autogenerated interop between Rust and C++" -repository = "https://github.com/google/autocxx" -edition = "2021" -keywords = ["ffi"] -categories = ["development-tools::ffi", "api-bindings"] - -# This is stricter about avoiding optional features in dependencies -# such as 'syn'. We build this way such that CI spots any mistakes -# where we are depending on such features. -resolver = "2" - -[dependencies] -autocxx-macro = { path="macro", version="0.16.0" } -autocxx-engine = { path="engine", version="0.16.0" } # so that - # we can refer to autocxx_engine::cxx. But even that isn't sufficient... -cxx = "1.0.54" # ... also needed because expansion of type_id refers to ::cxx -aquamarine = "0.1" # docs -moveit = { version = "0.4", features = [ "cxx" ] } - -[workspace] -members = ["parser", "engine", "gen/cmd", "gen/build", "macro", "demo", "tools/reduce", "integration-tests"] -exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/chromium-fake-render-frame-host", "examples/pod", "examples/non-trivial-type-on-stack"] - -#[patch.crates-io] -#cxx = { path="../cxx" } -#cxx-gen = { path="../cxx/gen/lib" } -#autocxx-bindgen = { path="../bindgen" }
diff --git a/third_party/rust/autocxx/v0_16/crate/README.md b/third_party/rust/autocxx/v0_16/crate/README.md deleted file mode 100644 index 7073473..0000000 --- a/third_party/rust/autocxx/v0_16/crate/README.md +++ /dev/null
@@ -1,195 +0,0 @@ -# Autocxx - -[](https://github.com/google/autocxx) -[](https://crates.io/crates/autocxx) -[](https://docs.rs/autocxx) - -This project is a tool for calling C++ from Rust in a heavily automated, but safe, fashion. - -The intention is that it has all the fluent safety from [cxx](https://cxx.rs) whilst generating interfaces automatically from existing C++ headers using a variant of [bindgen](https://docs.rs/bindgen/latest/bindgen/). Think of autocxx as glue which plugs bindgen into cxx. - -# Overview - -```rust,ignore -autocxx::include_cpp! { - #include "url/origin.h" - generate!("url::Origin") - safety!(unsafe_ffi) -} - -fn main() { - let o = ffi::url::Origin::CreateFromNormalizedTuple("https", - "google.com", 443); - let uri = o.Serialize(); - println!("URI is {}", uri.to_str().unwrap()); -} -``` - -# Getting started - -If you're here, you want to call some C++ from Rust, right? - -You will need: - -* Some C++ header files (`.h` files) -* The C++ "include path". That is, the set of directories containing those headers. (That's not necessarily the directory in which each header _file_ lives; C++ might contain `#include "foo/bar.h"` and so your include path would need to include the directory containing the `foo` directory). -* A list of the APIs (types and functions) from those header files which you wish to make available in Rust. -* Either a Cargo or non-Cargo build system. -* To know how to link the C++ libraries into your Cargo project. This is beyond the scope of what `autocxx` helps with, but one solution is to emit a print from your [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib). -* [LLVM to be installed](https://rust-lang.github.io/rust-bindgen/requirements.html). -* Some patience. This is not a magic solution. C++/Rust interop is hard. Avoid it if you can! - -The rest of this 'getting started' section assumes Cargo - if you're using something else, see the [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html) documentation. - -First, add `autocxx` and `cxx` to your `dependencies` and `autocxx-build` to your `build-dependencies` in your `Cargo.toml`. - -```toml -[dependencies] -autocxx = "0.16.0" -cxx = "1.0" - -[build-dependencies] -autocxx-build = "0.16.0" -``` - -Now, add a `build.rs`. This is where you need your include path: - -```rust,ignore -fn main() { - let path = std::path::PathBuf::from("src"); // include path - let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).expect_build(); - // This assumes all your C++ bindings are in main.rs - b.flag_if_supported("-std=c++14").compile("autocxx-demo"); - println!("cargo:rerun-if-changed=src/main.rs"); - // Add instructions to link to any C++ libraries you need. -} -``` - -Finally, in your `main.rs` you can use the [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html) macro which is the heart of `autocxx`: - -```rust,ignore -use autocxx::prelude::*; - -include_cpp! { - #include "my_header.h" // your header file name - safety!(unsafe) // see details of unsafety policies described in include_cpp - generate!("MyAPIFunction") // add this line for each function or type you wish to generate -} -``` - -You should then find you can call the function by referring to an `ffi` namespace: - -```rust,ignore -fn main() { - println!("Hello, world! - answer from C++ is {}", ffi::MyAPIFunction(4)); -} -``` - -C++ types such as `std::string` and `std::unique_ptr` are represented using the types provided by the marvellous [cxx](https://cxx.rs) library. This provides good ergonomics and safety norms, so unlike with normal `bindgen` bindings, you won't _normally_ need to write `unsafe` code for every function call. - -Some caveats: - -* Not all C++ features are supported. You _will_ come across APIs - possibly many APIs - where autocxx doesn't work. It should emit reasonable diagnostics explaining the problem. See the section on "dealing with failure" in the [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html) documentation. -* `autocxx` can be frustrating when you run up against its limitations. It's designed to allow importing of APIs from complex existing codebases. It's often a better choice to use [cxx](https://cxx.rs) directly. - -A full user manual can be found in the documentation for [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html). See [demo/src/main.rs](demo/src/main.rs) for a basic example, and the [examples](examples/) directory for more. - -# On safety - -This crate mostly intends to follow the lead of the `cxx` crate in where and when `unsafe` is required. But, this crate is opinionated. It believes some unsafety requires more careful review than other bits, along the following spectrum: - -* Rust unsafe code (requires most review) -* Rust code calling C++ with raw pointers -* Rust code calling C++ with shared pointers, or anything else where there can be concurrent mutation -* Rust code calling C++ with unique pointers, where the Rust single-owner model nearly always applies (but we can't _prove_ that the C++ developer isn't doing something weird) -* Rust safe code (requires least review) - -If your project is 90% Rust code, with small bits of C++, don't use this crate. You need something where all C++ interaction is marked with big red "this is terrifying" flags. This crate is aimed at cases where there's 90% C++ and small bits of Rust, and so we want the Rust code to be pragmatically reviewable without the signal:noise ratio of `unsafe` in the Rust code becoming so bad that `unsafe` loses all value. - -See [safety!] in the documentation for more details. - -# Building without cargo - -See instructions in the documentation for [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html). This interop inevitably involves lots of fiddly small functions. It's likely to perform far better if you can achieve cross-language LTO. [This issue](https://github.com/dtolnay/cxx/issues/371) may give some useful hints - see also all the build-related help in [the cxx manual](https://cxx.rs/) which all applies here too. - -# Directory structure - -* `demo` - a very simple demo example -* `examples` - will gradually fill with more complex examples -* `parser` - code which parses a single `include_cpp!` macro. Used by both the macro - (which doesn't do much) and the code generator (which does much more, by means of - `engine` below) -* `engine` - all the core code for actual code generation. -* `macro` - the procedural macro which expands the Rust code. -* `gen/build` - a library to be used from `build.rs` scripts to generate .cc and .h - files from an `include_cxx` section. -* `gen/cmd` - a command-line tool which does the same. -* `src` (outermost project) - a wrapper crate which imports the procedural macro and - a few other things. - -# Where to start reading - -The main algorithm is in `engine/src/lib.rs`, in the function `generate()`. This asks -`bindgen` to generate a heap of Rust code and then passes it into -`engine/src/conversion` to convert it to be a format suitable for input -to `cxx`. - -However, most of the actual code is in `engine/src/conversion/mod.rs`. - -At the moment we're using a slightly branched version of `bindgen` called `autocxx-bindgen`. -It's hoped this is temporary; some of our changes are sufficiently weird that it would be -presumptious to try to get them accepted upstream until we're sure `autocxx` has roughly the right approach. - -# How to develop - -If you're making a change, here's what you need to do to get useful diagnostics etc. -First of all, `cargo run` in the `demo` directory. If it breaks, you don't get much -in the way of useful diagnostics, because `stdout` is swallowed by cargo build scripts. -So, practically speaking, you would almost always move onto running one of the tests -in the test suite. With suitable options, you can get plenty of output. For instance: - -```ignore -RUST_BACKTRACE=1 RUST_LOG=autocxx_engine=info cargo test --all test_cycle_string_full_pipeline -- --nocapture -``` - -This is especially valuable to see the `bindgen` output Rust code, and then the converted Rust code which we pass into cxx. Usually, most problems are due to some mis-conversion somewhere -in `engine/src/conversion`. See [here](https://docs.rs/autocxx-engine/latest/autocxx_engine/struct.IncludeCppEngine.html) for documentation and diagrams on how the engine works. - -# Reporting bugs - -If you've found a problem, and you're reading this, *thank you*! Your diligence -in reporting the bug is much appreciated and will make `autocxx` better. In -order of preference here's how we would like to hear about your problem: - -* Raise a pull request adding a new failing integration test to - `engine/src/integration_tests.rs`. -* Minimize the test using `tools/reduce`, something like this: - `target/debug/autocxx-reduce file -d "safety!(unsafe_ffi)" -d - 'generate_pod!("A")' -I ~/my-include-dir -h my-header.h -p - problem-error-message -- --remove-pass pass_line_markers` - This is a wrapper for the amazing `creduce` which will take thousands of lines - of C++, preprocess it, and then identify the minimum required lines to - reproduce the same problem. -* Use the C++ preprocessor to give a single complete C++ file which demonstrates - the problem, along with the `include_cpp!` directive you use. - Alternatively, run your build using `AUTOCXX_REPRO_CASE=repro.json` which should - put everything we need into `output.h`. If necessary, you can use the `CLANG_PATH` - or `CXX` environment variables to specify the path to the Clang compiler to use. -* Failing all else, build using - `cargo clean -p <your package name> && RUST_LOG=autocxx_engine=info cargo build -vvv` - and send the _entire_ log to us. This will include two key bits of logging: - the C++ bindings as distilled by `bindgen`, and then the version which - we've converted and moulded to be suitable for use by `cxx`. - -# Credits - -David Tolnay did much of the hard work here, by inventing the underlying cxx crate, and in fact nearly all of the parsing infrastructure on which this crate depends. `bindgen` is also awesome. This crate stands on the shoulders of giants! - -#### License and usage notes - -This is not an officially supported Google product. - -<sup> -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. -</sup>
diff --git a/third_party/rust/autocxx/v0_16/crate/docs/contributing.md b/third_party/rust/autocxx/v0_16/crate/docs/contributing.md deleted file mode 100644 index 654a071..0000000 --- a/third_party/rust/autocxx/v0_16/crate/docs/contributing.md +++ /dev/null
@@ -1,28 +0,0 @@ -# How to Contribute - -We'd love to accept your patches and contributions to this project. There are -just a few small guidelines you need to follow. - -## Contributor License Agreement - -Contributions to this project must be accompanied by a Contributor License -Agreement. You (or your employer) retain the copyright to your contribution; -this simply gives us permission to use and redistribute your contributions as -part of the project. Head over to <https://cla.developers.google.com/> to see -your current agreements on file or to sign a new one. - -You generally only need to submit a CLA once, so if you've already submitted one -(even if it was for a different project), you probably don't need to do it -again. - -## Code reviews - -All submissions, including submissions by project members, require review. We -use GitHub pull requests for this purpose. Consult -[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more -information on using pull requests. - -## Community Guidelines - -This project follows [Google's Open Source Community -Guidelines](https://opensource.google/conduct/).
diff --git a/third_party/rust/autocxx/v0_16/crate/src/lib.rs b/third_party/rust/autocxx/v0_16/crate/src/lib.rs deleted file mode 100644 index 6537ba6d..0000000 --- a/third_party/rust/autocxx/v0_16/crate/src/lib.rs +++ /dev/null
@@ -1,837 +0,0 @@ -#![doc = include_str!("../README.md")] - -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -// The crazy macro_rules magic in this file is thanks to dtolnay@ -// and is a way of attaching rustdoc to each of the possible directives -// within the include_cpp outer macro. None of the directives actually -// do anything - all the magic is handled entirely by -// autocxx_macro::include_cpp_impl. - -pub mod subclass; - -#[allow(unused_imports)] // doc cross-reference only -use autocxx_engine::IncludeCppEngine; - -#[cfg_attr(doc, aquamarine::aquamarine)] -/// Include some C++ headers in your Rust project. -/// -/// This macro allows you to include one or more C++ headers within -/// your Rust code, and call their functions fairly naturally. -/// -/// # Examples -/// -/// C++ header (`input.h`): -/// ```cpp -/// #include <cstdint> -/// -/// uint32_t do_math(uint32_t a); -/// ``` -/// -/// Rust code: -/// ``` -/// # use autocxx_macro::include_cpp_impl as include_cpp; -/// include_cpp!( -/// # parse_only!() -/// #include "input.h" -/// generate!("do_math") -/// safety!(unsafe) -/// ); -/// -/// # mod ffi { pub fn do_math(a: u32) -> u32 { a+3 } } -/// # fn main() { -/// ffi::do_math(3); -/// # } -/// ``` -/// -/// The resulting bindings will use idiomatic Rust wrappers for types from the [cxx] -/// crate, for example [cxx::UniquePtr] or [cxx::CxxString]. Due to the care and thought -/// that's gone into the [cxx] crate, such bindings are pleasant and idiomatic to use -/// from Rust, and usually don't require the `unsafe` keyword. -/// -/// # User manual - introduction -/// -/// [`include_cpp`] tries to make it possible to include C++ headers and use declared functions -/// and types as-is. The resulting bindings use wrappers for C++ STL types from the [cxx] -/// crate such as [cxx::UniquePtr] or [cxx::CxxString]. -/// -/// Why, then, do you need a manual? Three reasons: -/// -/// * This manual will describe how to include `autocxx` in your build process. -/// * `autocxx` chooses to generate Rust bindings for C++ APIs in particular ways, -/// over which you have _some_ control. The manual discusses what and how. -/// * The combination of `autocxx` and [`cxx`] are not perfect. There are some STL -/// types and some fundamental C++ features which are not yet supported. Where that occurs, -/// you may need to create some manual bindings or otherwise workaround deficiencies. -/// This manual tells you how to spot such circumstances and work around them. -/// -/// # Overview -/// -/// Here's how to approach autocxx: -/// -/// ```mermaid -/// flowchart TB -/// %%{init:{'flowchart':{'nodeSpacing': 60, 'rankSpacing': 30}}}%% -/// autocxx[Add a dependency on autocxx in your project] -/// which-build([Do you use cargo?]) -/// autocxx--->which-build -/// autocxx-build[Add a dev dependency on autocxx-build] -/// build-rs[In your build.rs, tell autocxx-build about your header include path] -/// autocxx-build--->build-rs -/// which-build-- Yes -->autocxx-build -/// macro[Add include_cpp! macro: list headers and allowlist] -/// build-rs--->macro -/// autocxx-gen[Use autocxx-gen command line tool] -/// which-build-- No -->autocxx-gen -/// autocxx-gen--->macro -/// build[Build] -/// macro--->build -/// check[Confirm generation using cargo expand] -/// build--->check -/// manual[Add manual cxx::bridge for anything missing] -/// check--->manual -/// use[Use generated ffi mod APIs] -/// manual--->use -/// ``` -/// -/// # Configuring the build - if you're using cargo -/// -/// You'll use the `autocxx-build` crate. Simply copy from the -/// [demo example](https://github.com/google/autocxx/blob/main/demo/build.rs). -/// You'll need to provide it: -/// * The list of `.rs` files which will have `include_cpp!` macros present -/// * Your C++ header include path. -/// -/// # Configuring the build - if you're not using cargo -/// -/// See the `autocxx-gen` crate. You'll need to: -/// -/// * Run the `codegen` phase. You'll need to use the [autocxx-gen] -/// tool to process the .rs code into C++ header and -/// implementation files. This will also generate `.rs` side bindings. -/// * Educate the procedural macro about where to find the generated `.rs` bindings. Set the -/// `AUTOCXX_RS` environment variable to a list of directories to search. -/// If you use `autocxx-build`, this happens automatically. (You can alternatively -/// specify `AUTOCXX_RS_FILE` to give a precise filename as opposed to a directory to search, -/// though this isn't recommended unless your build system specifically requires it -/// because it allows only a single `include_cpp!` block per `.rs` file.) -/// -/// ```mermaid -/// flowchart TB -/// s(Rust source with include_cpp!) -/// c(Existing C++ headers) -/// cg(autocxx-gen or autocxx-build) -/// genrs(Generated .rs file) -/// gencpp(Generated .cpp and .h files) -/// rsb(Rust/Cargo build) -/// cppb(C++ build) -/// l(Linker) -/// s --> cg -/// c --> cg -/// cg --> genrs -/// cg --> gencpp -/// m(autocxx-macro) -/// s --> m -/// genrs-. included .->m -/// m --> rsb -/// gencpp --> cppb -/// cppb --> l -/// rsb --> l -/// ``` -/// -/// # The [`include_cpp`] macro -/// -/// Within the braces of the `include_cpp!{...}` macro, you should provide -/// a list of at least the following: -/// -/// * `#include "cpp_header.h"`: a header filename to parse and include -/// * `generate!("type_or_function_name")`: a type or function name whose declaration -/// should be made available to C++. (See the section on Allowlisting, below). -/// * Optionally, `safety!(unsafe)` - see discussion of [`safety`]. -/// -/// Other directives are possible as documented in this crate. -/// -/// Now, try to build your Rust project. `autocxx` may fail to generate bindings -/// for some of the items you specified with [generate] directives: remove -/// those directives for now, then see the next section for advice. -/// -/// # Did it work? How do I deal with failure? -/// -/// Once you've achieved a successful build, you might wonder how to know what -/// bindings have been generated. `cargo expand` will show you. Alternatively, -/// you can get autocompletion within an IDE supported by Rust analyzer. You'll -/// need to enable _both_: -/// * Rust-analyzer: Proc Macro: Enable -/// * Rust-analyzer: Experimental: Proc Attr Macros -/// -/// Either way, you'll find (for sure!) that `autocxx` hasn't been able to generate -/// bindings for all your C++ APIs. This may manifest as a hard failure or a soft -/// failure: -/// * If you specified such an item in a [`generate`] directive (or similar such -/// as [`generate_pod`]) then your build will fail. -/// * If such APIs are methods belonging to a type, `autocxx` will generate other -/// methods for the type but ignore those. -/// -/// In this latter case, you should see helpful messages _in the generated bindings_ -/// as rust documentation explaining what went wrong. -/// -/// If this happens (and it will!) your options are: -/// * Add more, simpler C++ APIs which fulfil the same need but are compatible with -/// `autocxx`. -/// * Write manual bindings. This is most useful if a type is supported by [cxx] -/// but not `autocxx` (for example, at the time of writing `std::array`). See -/// the later section on 'combinining automatic and manual bindings'. -/// -/// # Allowlisting -/// -/// How do you inform autocxx which bindings to generate? There are three -/// strategies: -/// -/// * *Recommended*: provide various [`generate`] directives in the -/// [`include_cpp`] macro. This can specify functions or types. -/// * *Not recommended*: in your `build.rs`, call [`Builder::auto_allowlist`]. -/// This will attempt to spot _uses_ of FFI bindings anywhere in your Rust code -/// and build the allowlist that way. This is experimental and has known limitations. -/// * *Strongly not recommended*: use [`generate_all`]. This will attempt to -/// generate Rust bindings for _any_ C++ type or function discovered in the -/// header files. This is generally a disaster if you're including any -/// remotely complex header file: we'll try to generate bindings for all sorts -/// of STL types. This will be slow, and some may well cause problems. -/// Effectively this is just a debug option to discover such problems. Don't -/// use it! -/// -/// # The generated bindings -/// -/// ## Pointers, references, and so-forth -/// -/// `autocxx` knows how to deal with C++ APIs which take C++ types: -/// * By value -/// * By reference (const or not) -/// * By raw pointer -/// * By `std::unique_ptr` -/// * By `std::shared_ptr` -/// * By `std::weak_ptr` -/// -/// (all of this is because the underlying [`cxx`] crate has such versatility). -/// Some of these have some quirks in the way they're exposed in Rust, described below. -/// -/// ### Passing between C++ and Rust by value -/// -/// Rust is free to move data around at any time. That's _not OK_ for some C++ types -/// which have non-trivial move constructors or destructors. Such types are common -/// in C++ (for example, even C++ `std::string`s) and these types commonly appear -/// in API declarations which we want to make available in Rust. Worse still, Rust -/// has no visibility into whether a C++ type meets these criteria. What do we do? -/// -/// You have a choice: -/// * As standard, any C++ type passed by value will be `std::move`d on the C++ side -/// into a `std::unique_ptr` before being passed to Rust, and similarly moved out -/// of a `std::unique_ptr` when passed from Rust to C++. -/// * If you know that your C++ type can be safely byte-copied, then you can -/// override this behavior by using [`generate_pod`] instead of [`generate`]. -/// -/// There's not a significant ergonomic problem from the use of [`cxx::UniquePtr`]. -/// The main negative of the automatic boxing into [`cxx::UniquePtr`] is performance: -/// specifically, the need to -/// allocate heap cells on the C++ side and move data into and out of them. -/// You don't want to be doing this inside a tight loop (but if you're calling -/// across the C++/Rust boundary in a tight loop, perhaps reconsider that boundary -/// anyway). -/// -/// If you want your type to be transferred between Rust and C++ truly _by value_ -/// then use [`generate_pod`] instead of [`generate`]. -/// -/// Specifically, to be compatible with [`generate_pod`], your C++ type must either: -/// * Lack a move constructor _and_ lack a destructor -/// * Or contain a human promise that it's relocatable, by implementing -/// the C++ trait `IsRelocatable` per the instructions in -/// [cxx.h](https://github.com/dtolnay/cxx/blob/master/include/cxx.h) -/// -/// Otherwise, your build will fail. -/// -/// This doesn't just make a difference to the generated code for the type; -/// it also makes a difference to any functions which take or return that type. -/// If there's a C++ function which takes a struct by value, but that struct -/// is not declared as POD-safe, then we'll generate wrapper functions to move -/// that type into and out of [`cxx::UniquePtr`]s. -/// -/// There is one other option under construction. The `moveit` crate replicates -/// C++ value move and copying semantics in Rust. There is limited early support -/// for `moveit` within autocxx; specifically, you can call C++ constructors -/// to emplace even non-trivial objects on the Rust stack using `moveit`, and -/// then call methods on them or use references to them in other function calls. -/// At present, you can't copy or move such objects - once they're created, -/// that's it, until it's dropped. At present therefore such objects can't be -/// passed into C++ functions which take non-POD types by value. This facility -/// is therefore best avoided for now until it's more complete - but see -/// `examples/non-trivial-type-on-stack` if you want to see how to use it. -/// -/// ### References and pointers -/// -/// We follow [cxx] norms here. Specifically: -/// * A C++ reference becomes a Rust reference -/// * A C++ pointer becomes a Rust pointer. -/// * If a reference is returned with an ambiguous lifetime, we don't generate -/// code for the function -/// * Pointers require use of `unsafe`, references don't necessarily. -/// -/// That last point is key. If your C++ API takes pointers, you're going -/// to have to use `unsafe`. Similarly, if your C++ API returns a pointer, -/// you'll have to use `unsafe` to do anything useful with the pointer in Rust. -/// This is intentional: a pointer from C++ might be subject to concurrent -/// mutation, or it might have a lifetime that could disappear at any moment. -/// As a human, you must promise that you understand the constraints around -/// use of that pointer and that's what the `unsafe` keyword is for. -/// -/// Exactly the same issues apply to C++ references _in theory_, but in practice, -/// they usually don't. Therefore [cxx] has taken the view that we can "trust" -/// a C++ reference to a higher degree than a pointer, and autocxx follows that -/// lead. In practice, of course, references are rarely return values from C++ -/// APIs so we rarely have to navel-gaze about the trustworthiness of a -/// reference. -/// -/// (See also the discussion of [`safety`] - if you haven't specified -/// an unsafety policy, _all_ C++ APIs require `unsafe` so the discussion is moot.) -/// -/// If you're given a C++ object by pointer, and you want to interact with it, -/// you'll need to figure out the guarantees attached to the C++ object - most -/// notably its lifetime. To see some of the decision making process involved -/// see the [Steam example](https://github.com/google/autocxx/tree/main/examples/steam-mini/src/main.rs). -/// -/// ### [`cxx::UniquePtr`]s -/// -/// We use [`cxx::UniquePtr`] in completely the normal way, but there are a few -/// quirks which you're more likely to run into with `autocxx`. -/// -/// * Calling methods: you may need to use [`cxx::UniquePtr::pin_mut`] to get -/// a reference on which you can call a method. -/// * Getting a raw pointer in order to pass to some pre-existing function: -/// at present you need to do: -/// ```rust,ignore -/// let mut a = ffi::A::make_unique(); -/// unsafe { ffi::TakePointerToA(std::pin::Pin::<&mut ffi::A>::into_inner_unchecked(a.pin_mut())) }; -/// ``` -/// This may be simplified in future. -/// -/// ## Construction -/// -/// Types gain a `make_unique` associated function. At present they only -/// gain this if they have an explicit C++ constructor; this is a limitation -/// which should be resolved in future. -/// This will (of course) return a [`cxx::UniquePtr`] containing that type. -/// -/// ## Built-in types -/// -/// The generated code uses `cxx` for interop: see that crate for many important -/// considerations including safety and the list of built-in types, for example -/// [`cxx::UniquePtr`] and [`cxx::CxxString`]. -/// -/// There are almost no `autocxx`-specific types. At present, we do have -/// [`c_int`] and similar, to wrap the integer types whose length -/// varies in C++. It's hoped to contribute full support here to [cxx] -/// in a future change. -/// -/// ## Strings -/// -/// `autocxx` uses [cxx::CxxString]. However, as noted above, we can't -/// just pass a C++ string by value, so we'll box and unbox it automatically -/// such that you're really dealing with `UniquePtr<CxxString>` on the Rust -/// side, even if the API just took or returned a plain old `std::string`. -/// -/// However, to ease ergonomics, functions that accept a `std::string` will -/// actually accept anything that -/// implements a trait called `ffi::ToCppString`. That may either be a -/// `UniquePtr<CxxString>` or just a plain old Rust string - which will be -/// converted transparently to a C++ string. -/// -/// This trait, and its implementations, are not present in the `autocxx` -/// documentation because they're dynamically generated in _your_ code -/// so that they can call through to a `make_string` implementation in -/// the C++ that we're injecting into your C++ build system. -/// -/// (None of that happens if you use [exclude_utilities], so don't do that.) -/// -/// If you need to create a blank `UniquePtr<CxxString>` in Rust, such that -/// (for example) you can pass its mutable reference or pointer into some -/// pre-existing C++ API, call `ffi::make_string("")` which will return -/// a blank `UniquePtr<CxxString>`. -/// -/// Don't attempt to use [cxx::let_cpp_string] which will allocate the -/// string on the stack, and is generally incompatible with the -/// [cxx::UniquePtr]-based approaches we use here. -/// -/// ## Preprocessor symbols -/// -/// `#define` and other preprocessor symbols will appear as constants. -/// At present there is no way to do compile-time disablement of code -/// (equivalent of `#ifdef`). -/// -/// ## Integer types -/// -/// For C++ types with a defined size, just go ahead and use `u64`, `i32` etc. -/// For types such as `int` or `unsigned long`, the hope is that you can -/// eventually use `std::os::raw::c_int` oor `std::os::raw::c_ulong` etc. -/// For now, this doesn't quite work: instead you need to wrap these values -/// in a newtype wrapper such as [c_int] or [c_ulong] in this crate. -/// -/// ## String constants -/// -/// Whether from a preprocessor symbol or from a C++ `char*` constant, -/// strings appear as `[u8]` with a null terminator. To get a Rust string, -/// do this: -/// -/// ```cpp -/// #define BOB "Hello" -/// ``` -/// -/// ``` -/// # mod ffi { pub static BOB: [u8; 6] = [72u8, 101u8, 108u8, 108u8, 111u8, 0u8]; } -/// assert_eq!(std::str::from_utf8(&ffi::BOB).unwrap().trim_end_matches(char::from(0)), "Hello"); -/// ``` -/// -/// ## Namespaces -/// -/// The C++ namespace structure is reflected in mods within the generated -/// ffi mod. However, at present there is an internal limitation that -/// autocxx can't handle multiple symbols with the same identifier, even -/// if they're in different namespaces. This will be fixed in future. -/// -/// ## Overloads - and identifiers ending in digits -/// -/// C++ allows function overloads; Rust doesn't. `autocxx` follows the lead -/// of `bindgen` here and generating overloads as `func`, `func1`, `func2` etc. -/// This is essentially awful without `rust-analyzer` IDE support, which isn't -/// quite there yet. -/// -/// `autocxx` doesn't yet support default paramters. -/// -/// It's fairly likely we'll change the model here in the future, such that -/// we can pass tuples of different parameter types into a single function -/// implementation. -/// -/// ## Forward declarations -/// -/// A type which is incomplete in the C++ headers (i.e. represented only by a forward -/// declaration) can't be held in a `UniquePtr` within Rust (because Rust can't know -/// if it has a destructor that will need to be called if the object is `Drop`ped.) -/// Naturally, such an object can't be passed by value either; it can still be -/// referenced in Rust references. -/// -/// ## Generic types -/// -/// If you're using one of the generic types which is supported natively by cxx, -/// e.g. `std::unique_ptr`, it should work as you expect. For other generic types, -/// we synthesize a concrete Rust type, corresponding to a C++ typedef, for each -/// concrete instantiation of the type. Such generated types are always opaque, -/// and never have methods attached. That's therefore enough to pass them -/// between return types and parameters of other functions within [`cxx::UniquePtr`]s -/// but not really enough to do anything else with these types just yet. Hopefully, -/// this will be improved in future. At present such types have a name -/// `AutocxxConcrete{n}` but this may change in future. -/// -/// ## Exceptions -/// -/// Exceptions are not supported. If your C++ code is compiled with exceptions, -/// you can expect serious runtime explosions. The underlying [cxx] crate has -/// exception support, so it would be possible to add them. -/// -/// # Subclasses -/// -/// There is limited and experimental support for creating Rust subclasses of -/// C++ classes. (Yes, even more experimental than all the rest of this!) -/// See [`subclass::CppSubclass`] for information about how you do this. -/// This is useful primarily if you want to listen out for messages broadcast -/// using the C++ observer/listener pattern. -/// -/// # Mixing manual and automated bindings -/// -/// `autocxx` uses [cxx] underneath, and its build process will happily spot and -/// process and manually-crafted [`cxx::bridge`] mods which you include in your -/// Rust source code. A common pattern good be to use `autocxx` to generate -/// all the bindings possible, then hand-craft a [`cxx::bridge`] mod for the -/// remainder where `autocxx` falls short. -/// -/// To do this, you'll need to use the [ability of one cxx::bridge mod to refer to types from another](https://cxx.rs/extern-c++.html#reusing-existing-binding-types), -/// for example: -/// -/// ```rust,ignore -/// autocxx::include_cpp! { -/// #include "foo.h" -/// safety!(unsafe_ffi) -/// generate!("take_A") -/// generate!("A") -/// } -/// #[cxx::bridge] -/// mod ffi2 { -/// unsafe extern "C++" { -/// include!("foo.h"); -/// type A = crate::ffi::A; -/// fn give_A() -> UniquePtr<A>; // in practice, autocxx could happily do this -/// } -/// } -/// fn main() { -/// let a = ffi2::give_A(); -/// assert_eq!(ffi::take_A(&a), autocxx::c_int(5)); -/// } -/// ``` -/// -/// # Safety -/// -/// # Examples -/// -/// * [Demo](https://github.com/google/autocxx/tree/main/demo) - simplest possible demo -/// * [S2 example](https://github.com/google/autocxx/tree/main/examples/s2) - example using S2 geometry library -/// * [Steam example](https://github.com/google/autocxx/tree/main/examples/steam-mini) - example using (something like) the Steam client library -/// * [Subclass example](https://github.com/google/autocxx/tree/main/examples/subclass) - example using subclasses -/// * [Integration tests](https://github.com/google/autocxx/blob/main/integration-tests/src/tests.rs) -/// - hundreds of small snippets -/// -/// Contributions of more examples to the `examples` directory are much appreciated! -/// -/// # Internals -/// -/// For documentation on how this all actually _works_, see -/// [IncludeCppEngine]. -#[macro_export] -macro_rules! include_cpp { - ( - $(#$include:ident $lit:literal)* - $($mac:ident!($($arg:tt)*))* - ) => { - $($crate::$include!{__docs})* - $($crate::$mac!{__docs})* - $crate::include_cpp_impl! { - $(#include $lit)* - $($mac!($($arg)*))* - } - }; -} - -/// Include a C++ header. A directive to be included inside -/// [include_cpp] - see [include_cpp] for details -#[macro_export] -macro_rules! include { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// Generate Rust bindings for the given C++ type or function. -/// A directive to be included inside -/// [include_cpp] - see [include_cpp] for general information. -/// See also [generate_pod]. -#[macro_export] -macro_rules! generate { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// Generate as "plain old data" and add to allowlist. -/// Generate Rust bindings for the given C++ type such that -/// it can be passed and owned by value in Rust. This only works -/// for C++ types which have trivial move constructors and no -/// destructor - you'll encounter a compile error otherwise. -/// If your type doesn't match that description, use [generate] -/// instead, and own the type using [UniquePtr][autocxx_engine::cxx::UniquePtr]. -/// A directive to be included inside -/// [include_cpp] - see [include_cpp] for general information. -#[macro_export] -macro_rules! generate_pod { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// Generate Rust bindings for all C++ types and functions -/// found. Highly experimental and not recommended. -/// A directive to be included inside -/// [include_cpp] - see [include_cpp] for general information. -/// See also [generate]. -#[macro_export] -macro_rules! generate_all { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// Generate as "plain old data". For use with [generate_all] -/// and similarly experimental. -#[macro_export] -macro_rules! pod { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// Skip the normal generation of a `make_string` function -/// and other utilities which we might generate normally. -/// A directive to be included inside -/// [include_cpp] - see [include_cpp] for general information. -#[macro_export] -macro_rules! exclude_utilities { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// Entirely block some type from appearing in the generated -/// code. This can be useful if there is a type which is not -/// understood by bindgen or autocxx, and incorrect code is -/// otherwise generated. -/// This is 'greedy' in the sense that any functions/methods -/// which take or return such a type will _also_ be blocked. -/// -/// A directive to be included inside -/// [include_cpp] - see [include_cpp] for general information. -#[macro_export] -macro_rules! block { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// Avoid generating implicit constructors for this type. -/// The rules for when to generate C++ implicit constructors -/// are complex, and if autocxx gets it wrong, you can block -/// such constructors using this. -/// -/// A directive to be included inside -/// [include_cpp] - see [include_cpp] for general information. -#[macro_export] -macro_rules! block_constructors { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// The name of the mod to be generated with the FFI code. -/// The default is `ffi`. -/// -/// A directive to be included inside -/// [include_cpp] - see [include_cpp] for general information. -#[macro_export] -macro_rules! name { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// Specifies a global safety policy for functions generated -/// from these headers. By default (without such a `safety!` -/// directive) all such functions are marked as `unsafe` and -/// therefore can only be called within an `unsafe {}` block -/// or some `unsafe` function which you create. -/// -/// Alternatively, by specifying a `safety!` block you can -/// declare that most generated functions are in fact safe. -/// Specifically, you'd specify: -/// `safety!(unsafe)` -/// or -/// `safety!(unsafe_ffi)` -/// These two options are functionally identical. If you're -/// unsure, simply use `unsafe`. The reason for the -/// latter option is if you have code review policies which -/// might want to give a different level of scrutiny to -/// C++ interop as opposed to other types of unsafe Rust code. -/// Maybe in your organization, C++ interop is less scary than -/// a low-level Rust data structure using pointer manipulation. -/// Or maybe it's more scary. Either way, using `unsafe` for -/// the data structure and using `unsafe_ffi` for the C++ -/// interop allows you to apply different linting tools and -/// policies to the different options. -/// -/// Irrespective, C++ code is of course unsafe. It's worth -/// noting that use of C++ can cause unexpected unsafety at -/// a distance in faraway Rust code. As with any use of the -/// `unsafe` keyword in Rust, *you the human* are declaring -/// that you've analyzed all possible ways that the code -/// can be used and you are guaranteeing to the compiler that -/// no badness can occur. Good luck. -/// -/// Generated C++ APIs which use raw pointers remain `unsafe` -/// no matter what policy you choose. -#[macro_export] -macro_rules! safety { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// Whether to avoid generating [`cxx::UniquePtr`] and [`cxx::Vector`] -/// implementations. This is primarily useful for reducing test cases and -/// shouldn't be used in normal operation. -/// -/// A directive to be included inside -/// [include_cpp] - see [include_cpp] for general information. -#[macro_export] -macro_rules! exclude_impls { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// Deprecated - use [`extern_rust_type`] instead. -#[macro_export] -#[deprecated] -macro_rules! rust_type { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// See [`extern_rust::extern_rust_type`]. -#[macro_export] -macro_rules! extern_rust_type { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -/// See [`subclass::subclass`]. -#[macro_export] -macro_rules! subclass { - ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! usage { - (__docs) => {}; - ($($tt:tt)*) => { - compile_error! {r#"usage: include_cpp! { - #include "path/to/header.h" - generate!(...) - generate_pod!(...) - } -"#} - }; -} - -#[doc(hidden)] -pub use autocxx_macro::include_cpp_impl; - -#[doc(hidden)] -pub use autocxx_macro::cpp_semantics; - -macro_rules! ctype_wrapper { - ($r:ident, $c:expr, $d:expr) => { - #[doc=$d] - #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash)] - #[allow(non_camel_case_types)] - #[repr(transparent)] - pub struct $r(pub ::std::os::raw::$r); - - unsafe impl autocxx_engine::cxx::ExternType for $r { - type Id = autocxx_engine::cxx::type_id!($c); - type Kind = autocxx_engine::cxx::kind::Trivial; - } - - impl From<::std::os::raw::$r> for $r { - fn from(val: ::std::os::raw::$r) -> Self { - Self(val) - } - } - - impl From<$r> for ::std::os::raw::$r { - fn from(val: $r) -> Self { - val.0 - } - } - }; -} - -ctype_wrapper!( - c_ulonglong, - "c_ulonglong", - "Newtype wrapper for an unsigned long long" -); -ctype_wrapper!(c_longlong, "c_longlong", "Newtype wrapper for a long long"); -ctype_wrapper!(c_ulong, "c_ulong", "Newtype wrapper for an unsigned long"); -ctype_wrapper!(c_long, "c_long", "Newtype wrapper for a long"); -ctype_wrapper!( - c_ushort, - "c_ushort", - "Newtype wrapper for an unsigned short" -); -ctype_wrapper!(c_short, "c_short", "Newtype wrapper for an short"); -ctype_wrapper!(c_uint, "c_uint", "Newtype wrapper for an unsigned int"); -ctype_wrapper!(c_int, "c_int", "Newtype wrapper for an int"); -ctype_wrapper!(c_uchar, "c_uchar", "Newtype wrapper for an unsigned char"); - -/// Newtype wrapper for a C void. Only useful as a `*c_void` -#[allow(non_camel_case_types)] -#[repr(transparent)] -pub struct c_void(pub ::std::os::raw::c_void); - -unsafe impl autocxx_engine::cxx::ExternType for c_void { - type Id = autocxx_engine::cxx::type_id!(c_void); - type Kind = autocxx_engine::cxx::kind::Trivial; -} - -/// autocxx couldn't generate these bindings. -/// If you come across a method, type or function which refers to this type, -/// it indicates that autocxx couldn't generate that binding. A documentation -/// comment should be attached indicating the reason. -pub struct BindingGenerationFailure { - _unallocatable: [*const u8; 0], - _pinned: core::marker::PhantomData<core::marker::PhantomPinned>, -} - -/// Tools to export Rust code to C++. -// These are in a mod to avoid shadowing the definitions of the -// directives above, which, being macro_rules, are unavoidably -// in the crate root but must be function-style macros to keep -// the include_cpp impl happy. -pub mod extern_rust { - - /// Declare that this is a Rust type which is to be exported to C++. - /// You can use this in two ways: - /// * as an attribute macro on a Rust type, for instance: - /// ``` - /// # use autocxx_macro::extern_rust_type as extern_rust_type; - /// #[extern_rust_type] - /// struct Bar; - /// ``` - /// * as a directive within the [include_cpp] macro, in which case - /// provide the type path in brackets: - /// ``` - /// # use autocxx_macro::include_cpp_impl as include_cpp; - /// include_cpp!( - /// # parse_only!() - /// #include "input.h" - /// extern_rust_type!(Bar) - /// safety!(unsafe) - /// ); - /// struct Bar; - /// ``` - /// These may be used within references in the signatures of C++ functions, - /// for instance. This will contribute to an `extern "Rust"` section of the - /// generated `cxx` bindings, and this type will appear in the C++ header - /// generated for use in C++. - pub use autocxx_macro::extern_rust_type; - - /// Declare that a given function is a Rust function which is to be exported - /// to C++. This is used as an attribute macro on a Rust function, for instance: - /// ``` - /// # use autocxx_macro::extern_rust_function as extern_rust_function; - /// #[extern_rust_function] - /// pub fn call_me_from_cpp() { } - /// ``` - pub use autocxx_macro::extern_rust_function; -} - -/// Equivalent to [`std::convert::AsMut`], but returns a pinned mutable reference -/// such that cxx methods can be called on it. -pub trait PinMut<T>: AsRef<T> { - /// Return a pinned mutable reference to a type. - fn pin_mut(&mut self) -> std::pin::Pin<&mut T>; -} - -/// Imports which you're likely to want to use. -pub mod prelude { - pub use crate::c_int; - pub use crate::c_long; - pub use crate::c_longlong; - pub use crate::c_short; - pub use crate::c_uchar; - pub use crate::c_uint; - pub use crate::c_ulong; - pub use crate::c_ulonglong; - pub use crate::c_ushort; - pub use crate::c_void; - pub use crate::cpp_semantics; - pub use crate::include_cpp; - pub use crate::PinMut; - pub use moveit::moveit; - pub use moveit::new::New; -} - -/// Re-export moveit for ease of consumers. -pub use moveit;
diff --git a/third_party/rust/autocxx/v0_16/crate/tools/upgrade-version.sh b/third_party/rust/autocxx/v0_16/crate/tools/upgrade-version.sh deleted file mode 100755 index 066a2ce..0000000 --- a/third_party/rust/autocxx/v0_16/crate/tools/upgrade-version.sh +++ /dev/null
@@ -1,6 +0,0 @@ -#!/bin/bash - -OLD=$1 -NEW=$2 -find . -type f -name "Cargo.toml" -print0 | xargs -0 sed -i '' -e "s/$OLD/$NEW/g" -find . -type f -name "README.md" -print0 | xargs -0 sed -i '' -e "s/$OLD/$NEW/g"
diff --git a/third_party/rust/autocxx/v0_16/BUILD.gn b/third_party/rust/autocxx/v0_17/BUILD.gn similarity index 83% rename from third_party/rust/autocxx/v0_16/BUILD.gn rename to third_party/rust/autocxx/v0_17/BUILD.gn index 30a4da64..b7ceb6de 100644 --- a/third_party/rust/autocxx/v0_16/BUILD.gn +++ b/third_party/rust/autocxx/v0_17/BUILD.gn
@@ -6,7 +6,7 @@ cargo_crate("lib") { crate_name = "autocxx" - epoch = "0.16" + epoch = "0.17" crate_type = "rlib" crate_root = "crate/src/lib.rs" @@ -16,8 +16,7 @@ edition = "2021" deps = [ "//third_party/rust/aquamarine/v0_1:lib", - "//third_party/rust/autocxx_engine/v0_16:lib", - "//third_party/rust/autocxx_macro/v0_16:lib", + "//third_party/rust/autocxx_macro/v0_17:lib", "//third_party/rust/cxx/v1:lib", "//third_party/rust/moveit/v0_4:lib", ]
diff --git a/third_party/rust/autocxx/v0_16/README.chromium b/third_party/rust/autocxx/v0_17/README.chromium similarity index 90% rename from third_party/rust/autocxx/v0_16/README.chromium rename to third_party/rust/autocxx/v0_17/README.chromium index 05a02e6..13be0a55 100644 --- a/third_party/rust/autocxx/v0_16/README.chromium +++ b/third_party/rust/autocxx/v0_17/README.chromium
@@ -1,6 +1,6 @@ Name: autocxx URL: https://crates.io/crates/autocxx Description: Safe autogenerated interop between Rust and C++ -Version: 0.16.0 +Version: 0.17.2 Security Critical: yes License: Apache 2.0
diff --git a/third_party/rust/autocxx/v0_17/crate/.cargo_vcs_info.json b/third_party/rust/autocxx/v0_17/crate/.cargo_vcs_info.json new file mode 100644 index 0000000..0e2733c --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "5bb748878f97f854bffa61e5a7cd8ebf970f3dcd" + }, + "path_in_vcs": "" +} \ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/ISSUE_TEMPLATE.md b/third_party/rust/autocxx/v0_17/crate/.github/ISSUE_TEMPLATE.md similarity index 100% rename from third_party/rust/autocxx/v0_16/crate/.github/ISSUE_TEMPLATE.md rename to third_party/rust/autocxx/v0_17/crate/.github/ISSUE_TEMPLATE.md
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/PULL_REQUEST_TEMPLATE.md b/third_party/rust/autocxx/v0_17/crate/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from third_party/rust/autocxx/v0_16/crate/.github/PULL_REQUEST_TEMPLATE.md rename to third_party/rust/autocxx/v0_17/crate/.github/PULL_REQUEST_TEMPLATE.md
diff --git a/third_party/rust/autocxx/v0_17/crate/.github/workflows/ci.yml b/third_party/rust/autocxx/v0_17/crate/.github/workflows/ci.yml new file mode 100644 index 0000000..45c8723 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/.github/workflows/ci.yml
@@ -0,0 +1,215 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + schedule: [cron: "40 1 * * *"] + workflow_dispatch: + +env: + RUST_BACKTRACE: short + # CI builds don't benefit very much from this and it has bugs + CARGO_INCREMENTAL: 0 + # We can't use a debugger in CI, and this makes builds faster and the cache + # smaller. (TODO: use -Cdebuginfo=0 if it doesn't make backtraces useless) + RUSTFLAGS: -Cdebuginfo=1 + CARGO_TERM_COLOR: always + +jobs: + test: + name: Test ${{matrix.name || format('Rust {0}', matrix.rust)}} + runs-on: ${{matrix.os || 'ubuntu'}}-latest + + strategy: + fail-fast: false + + matrix: + include: + - rust: nightly + - rust: beta + - rust: stable + - name: macOS + rust: nightly + os: macos + - name: Windows (gnu) + rust: nightly-x86_64-pc-windows-gnu + os: windows + - name: Windows (msvc) + rust: nightly-x86_64-pc-windows-msvc + os: windows + flags: /EHsc + env: + CXXFLAGS: ${{matrix.flags}} + RUSTFLAGS: --cfg deny_warnings -Dwarnings + + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + components: rustfmt + # The `{ sharedKey: ... }` allows different actions to share the cache. + - uses: Swatinem/rust-cache@v1 + with: { sharedKey: fullBuild } + # For operating systems that have it packaged, install creduce + - name: Install creduce (Linux) + if: matrix.os == '' + run: sudo apt-get install creduce + - name: Install creduce (MacOS) + if: matrix.os == 'macOS' + run: brew install creduce + - name: Set LIBCLANG_PATH (Windows) + # Windows github action doesn't set the path for clang, so set it + # See https://github.com/rust-lang/rust-bindgen/issues/1797 + if: matrix.os == 'windows' + run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV + - name: Exclude failing targets and tests + # no creduce on Windows, so exclude tests needing creduce there + run: | + echo RUSTFLAGS=$RUSTFLAGS >> $GITHUB_ENV + echo ::set-output name=exclude::${{runner.os == 'Windows' && '--exclude autocxx-reduce --exclude autocxx-gen' || ''}} + env: + # non-linux failures https://github.com/google/autocxx/issues/819 + # beta failing tests https://github.com/google/autocxx/issues/818 + RUSTFLAGS: ${{matrix.name == 'Windows (msvc)' && '--cfg skip_windows_msvc_failing_tests' || ''}} ${{matrix.name == 'Windows (gnu)' && '--cfg skip_windows_gnu_failing_tests' || ''}} + id: testsuite + shell: bash + - run: cargo test --workspace ${{steps.testsuite.outputs.exclude}} + + examples: + name: Examples ${{matrix.name || format('Rust {0}', matrix.rust)}} + runs-on: ${{matrix.os || 'ubuntu'}}-latest + strategy: + fail-fast: false + matrix: + include: + - rust: nightly + - rust: beta + - rust: stable + - name: macOS + rust: nightly + os: macos + - name: Windows (gnu) + rust: nightly-x86_64-pc-windows-gnu + os: windows + - name: Windows (msvc) + rust: nightly-x86_64-pc-windows-msvc + os: windows + flags: /EHsc + env: + CXXFLAGS: ${{matrix.flags}} + RUSTFLAGS: --cfg deny_warnings -Dwarnings + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + components: rustfmt + - uses: Swatinem/rust-cache@v1 + with: { sharedKey: fullBuild } + - name: Set LIBCLANG_PATH (Windows) + # Windows github action doesn't set the path for clang, so set it + # See https://github.com/rust-lang/rust-bindgen/issues/1797 + if: matrix.os == 'windows' + run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV + - name: Build s2 example + working-directory: ./examples/s2 + # s2 doesn't link on Windows + if: matrix.os != 'windows' + run: cargo build + - name: Build steam example + working-directory: ./examples/steam-mini + run: cargo build + - name: Build subclass example + working-directory: ./examples/subclass + run: cargo build + - name: Build pod example + working-directory: ./examples/pod + run: cargo build + - name: Build chromium render-frame-host example + working-directory: ./examples/chromium-fake-render-frame-host + # chromium-fake-render-frame-host doesn't link on Windows + if: matrix.os != 'windows' + run: cargo build + - name: Build non-trivial-type-on-stack example + working-directory: ./examples/non-trivial-type-on-stack + run: cargo build + + sanitizer: + name: Address Sanitizer + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + # Need nightly rust. + - uses: hecrj/setup-rust-action@v1 + with: + rust-version: nightly + components: rust-src + - uses: Swatinem/rust-cache@v1 + - name: Tests with asan + env: + RUSTFLAGS: -Zsanitizer=address -Cdebuginfo=0 + RUSTDOCFLAGS: -Zsanitizer=address + ASAN_OPTIONS: "detect_stack_use_after_return=1:detect_leaks=0" + # Work around https://github.com/rust-lang/rust/issues/59125 by + # disabling backtraces. In an ideal world we'd probably suppress the + # leak sanitization, but we don't care about backtraces here, so long + # as the other tests have them. + RUST_BACKTRACE: "0" + run: cargo -Z build-std test --workspace --target x86_64-unknown-linux-gnu + + # Clippy check + clippy: + name: Clippy + runs-on: ubuntu-latest + env: + CARGO_TERM_COLOR: always + steps: + - uses: actions/checkout@v2 + - uses: hecrj/setup-rust-action@v1 + with: + components: clippy + - uses: Swatinem/rust-cache@v1 + - run: cargo clippy --workspace --tests -- -Dclippy::all + + # Mention outdated dependencies + outdated: + name: Outdated + runs-on: ubuntu-latest + env: + CARGO_TERM_COLOR: always + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/install@cargo-outdated + - run: cargo outdated -R -w + + # Check rustfmt is good + fmt: + name: Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: hecrj/setup-rust-action@v1 + with: + components: rustfmt + - run: cargo fmt --all -- --check + + # Detect cases where documentation links don't resolve and such. + doc: + name: Docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: hecrj/setup-rust-action@v1 + - uses: Swatinem/rust-cache@v1 + with: { sharedKey: fullBuild } + - run: | + for package in $(cargo metadata --no-deps --format-version=1 | jq -r '.packages[] | .name'); do + cargo rustdoc --color always -p "$package" -- -D warnings + done + env: { RUSTDOCFLAGS: -Dwarnings }
diff --git a/third_party/rust/autocxx/v0_17/crate/.github/workflows/site.yml b/third_party/rust/autocxx/v0_17/crate/.github/workflows/site.yml new file mode 100644 index 0000000..0dd4237 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/.github/workflows/site.yml
@@ -0,0 +1,43 @@ +name: github pages + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + + - name: Build preprocessor + run: cargo build + working-directory: tools/mdbook-preprocessor + + - name: Setup mdBook + uses: peaceiris/actions-mdbook@v1 + with: + mdbook-version: 'latest' + + - name: Install mdbook-linkcheck + run: cargo install mdbook-linkcheck + + - name: Install mdbook-mermaid + run: | + curl -LSfs https://japaric.github.io/trust/install.sh | \ + sh -s -- --git badboy/mdbook-mermaid + + - run: mdbook build + working-directory: book + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.ref == 'refs/heads/main' }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./book/build/html + #cname: autocxx.rs
diff --git a/third_party/rust/autocxx/v0_17/crate/.gitignore b/third_party/rust/autocxx/v0_17/crate/.gitignore new file mode 100644 index 0000000..af6bc430 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/.gitignore
@@ -0,0 +1,2 @@ +target +.vscode/
diff --git a/third_party/rust/autocxx/v0_16/crate/.gitmodules b/third_party/rust/autocxx/v0_17/crate/.gitmodules similarity index 100% rename from third_party/rust/autocxx/v0_16/crate/.gitmodules rename to third_party/rust/autocxx/v0_17/crate/.gitmodules
diff --git a/third_party/rust/autocxx/v0_16/crate/Cargo.toml b/third_party/rust/autocxx/v0_17/crate/Cargo.toml similarity index 91% rename from third_party/rust/autocxx/v0_16/crate/Cargo.toml rename to third_party/rust/autocxx/v0_17/crate/Cargo.toml index 5b0d94a..a10da5a2 100644 --- a/third_party/rust/autocxx/v0_16/crate/Cargo.toml +++ b/third_party/rust/autocxx/v0_17/crate/Cargo.toml
@@ -12,9 +12,10 @@ [package] edition = "2021" name = "autocxx" -version = "0.16.0" +version = "0.17.2" authors = ["Adrian Taylor <adetaylor@chromium.org>"] description = "Safe autogenerated interop between Rust and C++" +homepage = "https://autocxx.rs" keywords = ["ffi"] categories = [ "development-tools::ffi", @@ -27,11 +28,8 @@ [dependencies.aquamarine] version = "0.1" -[dependencies.autocxx-engine] -version = "0.16.0" - [dependencies.autocxx-macro] -version = "0.16.0" +version = "0.17.2" [dependencies.cxx] version = "1.0.54"
diff --git a/third_party/rust/autocxx/v0_17/crate/Cargo.toml.orig b/third_party/rust/autocxx/v0_17/crate/Cargo.toml.orig new file mode 100644 index 0000000..8ea82ad5 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/Cargo.toml.orig
@@ -0,0 +1,40 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +[package] +name = "autocxx" +version = "0.17.2" +authors = ["Adrian Taylor <adetaylor@chromium.org>"] +license = "MIT OR Apache-2.0" +description = "Safe autogenerated interop between Rust and C++" +repository = "https://github.com/google/autocxx" +homepage = "https://autocxx.rs" +edition = "2021" +keywords = ["ffi"] +categories = ["development-tools::ffi", "api-bindings"] + +# This is stricter about avoiding optional features in dependencies +# such as 'syn'. We build this way such that CI spots any mistakes +# where we are depending on such features. +resolver = "2" + +[dependencies] +autocxx-macro = { path="macro", version="0.17.2" } +cxx = "1.0.54" # ... also needed because expansion of type_id refers to ::cxx +aquamarine = "0.1" # docs +moveit = { version = "0.4", features = [ "cxx" ] } + +[workspace] +members = ["parser", "engine", "gen/cmd", "gen/build", "macro", "demo", "tools/reduce", "tools/mdbook-preprocessor", "integration-tests"] +exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/chromium-fake-render-frame-host", "examples/pod", "examples/non-trivial-type-on-stack"] + +#[patch.crates-io] +#cxx = { path="../cxx" } +#cxx-gen = { path="../cxx/gen/lib" } +#autocxx-bindgen = { path="../bindgen" } +#moveit = { path="../moveit" }
diff --git a/third_party/rust/autocxx/v0_16/crate/LICENSE-APACHE b/third_party/rust/autocxx/v0_17/crate/LICENSE-APACHE similarity index 100% rename from third_party/rust/autocxx/v0_16/crate/LICENSE-APACHE rename to third_party/rust/autocxx/v0_17/crate/LICENSE-APACHE
diff --git a/third_party/rust/autocxx/v0_16/crate/LICENSE-MIT b/third_party/rust/autocxx/v0_17/crate/LICENSE-MIT similarity index 100% rename from third_party/rust/autocxx/v0_16/crate/LICENSE-MIT rename to third_party/rust/autocxx/v0_17/crate/LICENSE-MIT
diff --git a/third_party/rust/autocxx/v0_17/crate/README.md b/third_party/rust/autocxx/v0_17/crate/README.md new file mode 100644 index 0000000..99a68b4 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/README.md
@@ -0,0 +1,37 @@ +# Autocxx + +[](https://github.com/google/autocxx) +[](https://crates.io/crates/autocxx) +[](https://docs.rs/autocxx) + +This project is a tool for calling C++ from Rust in a heavily automated, but safe, fashion. + +The intention is that it has all the fluent safety from [cxx](https://cxx.rs) whilst generating interfaces automatically from existing C++ headers using a variant of [bindgen](https://docs.rs/bindgen/latest/bindgen/). Think of autocxx as glue which plugs bindgen into cxx. + +For full documentation, see [the manual](https://google.github.io/autocxx/). + +# Overview + +```rust,ignore +autocxx::include_cpp! { + #include "url/origin.h" + generate!("url::Origin") + safety!(unsafe_ffi) +} + +fn main() { + let o = ffi::url::Origin::CreateFromNormalizedTuple("https", + "google.com", 443); + let uri = o.Serialize(); + println!("URI is {}", uri.to_str().unwrap()); +} +``` + +#### License and usage notes + +This is not an officially supported Google product. + +<sup> +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. +</sup>
diff --git a/third_party/rust/autocxx/v0_17/crate/book/.gitignore b/third_party/rust/autocxx/v0_17/crate/book/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/.gitignore
@@ -0,0 +1 @@ +build
diff --git a/third_party/rust/autocxx/v0_17/crate/book/README.md b/third_party/rust/autocxx/v0_17/crate/book/README.md new file mode 100644 index 0000000..c883a45 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/README.md
@@ -0,0 +1,10 @@ +Published automatically to https://autocxx.rs from master branch. + +To build and view locally: + +- Install [mdBook] and some preprocessors: `cargo install mdbook mdbook-mermaid mdbook-linkcheck`. +- Build our custom preprocessor: run `cargo build` in `tools/mdbook-preprocessor` +- Run `mdbook build` in this directory. +- Open the generated *build/html/index.html*. + +[mdBook]: https://github.com/rust-lang/mdBook
diff --git a/third_party/rust/autocxx/v0_17/crate/book/book.toml b/third_party/rust/autocxx/v0_17/crate/book/book.toml new file mode 100644 index 0000000..b557cc6 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/book.toml
@@ -0,0 +1,25 @@ +[book] +title = "Rust ♡ Existing C++" +authors = ["Adrian Taylor"] +description = "autocxx — safe interop between Rust and existing C++" + +[rust] +edition = "2018" + +[build] +build-dir = "build" +create-missing = false + +[preprocessor.autocxx] +command = "../target/debug/autocxx-mdbook-preprocessor" + +[preprocessor.mermaid] +command = "mdbook-mermaid" + +[output.html] +cname = "google.github.io/autocxx" +git-repository-url = "https://github.com/google/autocxx" +additional-js = ["mermaid.min.js", "mermaid-init.js"] + +[output.linkcheck] +warning-policy = "error"
diff --git a/third_party/rust/autocxx/v0_17/crate/book/mermaid-init.js b/third_party/rust/autocxx/v0_17/crate/book/mermaid-init.js new file mode 100644 index 0000000..313a6e8 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/mermaid-init.js
@@ -0,0 +1 @@ +mermaid.initialize({startOnLoad:true});
diff --git a/third_party/rust/autocxx/v0_17/crate/book/mermaid.min.js b/third_party/rust/autocxx/v0_17/crate/book/mermaid.min.js new file mode 100644 index 0000000..d45942f3 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/mermaid.min.js
@@ -0,0 +1,4 @@ +/* MIT Licensed. Copyright (c) 2014 - 2021 Knut Sveidqvist */ +/*! For license information please see https://github.com/mermaid-js/mermaid/blob/8.13.10/LICENSE */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.mermaid=e():t.mermaid=e()}("undefined"!=typeof self?self:this,(function(){return(()=>{var t={1362:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,6],n=[1,7],r=[1,8],i=[1,9],a=[1,12],o=[1,11],s=[1,15,24],c=[1,19],u=[1,31],l=[1,34],h=[1,32],f=[1,33],d=[1,35],p=[1,36],y=[1,37],g=[1,38],m=[1,41],v=[1,42],b=[1,43],_=[1,44],x=[15,24],w=[1,56],k=[1,57],T=[1,58],E=[1,59],C=[1,60],S=[1,61],A=[15,24,31,38,39,47,50,51,52,53,54,55,60,62],M=[15,24,29,31,38,39,43,47,50,51,52,53,54,55,60,62,77,78,79,80],N=[7,8,9,10,15,18,22,24],D=[47,77,78,79,80],O=[47,54,55,77,78,79,80],B=[47,50,51,52,53,77,78,79,80],L=[15,24,31],I=[1,93],R={trace:function(){},yy:{},symbols_:{error:2,start:3,mermaidDoc:4,direction:5,directive:6,direction_tb:7,direction_bt:8,direction_rl:9,direction_lr:10,graphConfig:11,openDirective:12,typeDirective:13,closeDirective:14,NEWLINE:15,":":16,argDirective:17,open_directive:18,type_directive:19,arg_directive:20,close_directive:21,CLASS_DIAGRAM:22,statements:23,EOF:24,statement:25,className:26,alphaNumToken:27,classLiteralName:28,GENERICTYPE:29,relationStatement:30,LABEL:31,classStatement:32,methodStatement:33,annotationStatement:34,clickStatement:35,cssClassStatement:36,CLASS:37,STYLE_SEPARATOR:38,STRUCT_START:39,members:40,STRUCT_STOP:41,ANNOTATION_START:42,ANNOTATION_END:43,MEMBER:44,SEPARATOR:45,relation:46,STR:47,relationType:48,lineType:49,AGGREGATION:50,EXTENSION:51,COMPOSITION:52,DEPENDENCY:53,LINE:54,DOTTED_LINE:55,CALLBACK:56,LINK:57,LINK_TARGET:58,CLICK:59,CALLBACK_NAME:60,CALLBACK_ARGS:61,HREF:62,CSSCLASS:63,commentToken:64,textToken:65,graphCodeTokens:66,textNoTagsToken:67,TAGSTART:68,TAGEND:69,"==":70,"--":71,PCT:72,DEFAULT:73,SPACE:74,MINUS:75,keywords:76,UNICODE_TEXT:77,NUM:78,ALPHA:79,BQUOTE_STR:80,$accept:0,$end:1},terminals_:{2:"error",7:"direction_tb",8:"direction_bt",9:"direction_rl",10:"direction_lr",15:"NEWLINE",16:":",18:"open_directive",19:"type_directive",20:"arg_directive",21:"close_directive",22:"CLASS_DIAGRAM",24:"EOF",29:"GENERICTYPE",31:"LABEL",37:"CLASS",38:"STYLE_SEPARATOR",39:"STRUCT_START",41:"STRUCT_STOP",42:"ANNOTATION_START",43:"ANNOTATION_END",44:"MEMBER",45:"SEPARATOR",47:"STR",50:"AGGREGATION",51:"EXTENSION",52:"COMPOSITION",53:"DEPENDENCY",54:"LINE",55:"DOTTED_LINE",56:"CALLBACK",57:"LINK",58:"LINK_TARGET",59:"CLICK",60:"CALLBACK_NAME",61:"CALLBACK_ARGS",62:"HREF",63:"CSSCLASS",66:"graphCodeTokens",68:"TAGSTART",69:"TAGEND",70:"==",71:"--",72:"PCT",73:"DEFAULT",74:"SPACE",75:"MINUS",76:"keywords",77:"UNICODE_TEXT",78:"NUM",79:"ALPHA",80:"BQUOTE_STR"},productions_:[0,[3,1],[3,1],[3,2],[5,1],[5,1],[5,1],[5,1],[4,1],[6,4],[6,6],[12,1],[13,1],[17,1],[14,1],[11,4],[23,1],[23,2],[23,3],[26,1],[26,1],[26,2],[26,2],[26,2],[25,1],[25,2],[25,1],[25,1],[25,1],[25,1],[25,1],[25,1],[25,1],[32,2],[32,4],[32,5],[32,7],[34,4],[40,1],[40,2],[33,1],[33,2],[33,1],[33,1],[30,3],[30,4],[30,4],[30,5],[46,3],[46,2],[46,2],[46,1],[48,1],[48,1],[48,1],[48,1],[49,1],[49,1],[35,3],[35,4],[35,3],[35,4],[35,4],[35,5],[35,3],[35,4],[35,4],[35,5],[35,3],[35,4],[35,4],[35,5],[36,3],[64,1],[64,1],[65,1],[65,1],[65,1],[65,1],[65,1],[65,1],[65,1],[67,1],[67,1],[67,1],[67,1],[27,1],[27,1],[27,1],[28,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:r.setDirection("TB");break;case 5:r.setDirection("BT");break;case 6:r.setDirection("RL");break;case 7:r.setDirection("LR");break;case 11:r.parseDirective("%%{","open_directive");break;case 12:r.parseDirective(a[s],"type_directive");break;case 13:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 14:r.parseDirective("}%%","close_directive","class");break;case 19:case 20:this.$=a[s];break;case 21:this.$=a[s-1]+a[s];break;case 22:case 23:this.$=a[s-1]+"~"+a[s];break;case 24:r.addRelation(a[s]);break;case 25:a[s-1].title=r.cleanupLabel(a[s]),r.addRelation(a[s-1]);break;case 33:r.addClass(a[s]);break;case 34:r.addClass(a[s-2]),r.setCssClass(a[s-2],a[s]);break;case 35:r.addClass(a[s-3]),r.addMembers(a[s-3],a[s-1]);break;case 36:r.addClass(a[s-5]),r.setCssClass(a[s-5],a[s-3]),r.addMembers(a[s-5],a[s-1]);break;case 37:r.addAnnotation(a[s],a[s-2]);break;case 38:this.$=[a[s]];break;case 39:a[s].push(a[s-1]),this.$=a[s];break;case 40:case 42:case 43:break;case 41:r.addMember(a[s-1],r.cleanupLabel(a[s]));break;case 44:this.$={id1:a[s-2],id2:a[s],relation:a[s-1],relationTitle1:"none",relationTitle2:"none"};break;case 45:this.$={id1:a[s-3],id2:a[s],relation:a[s-1],relationTitle1:a[s-2],relationTitle2:"none"};break;case 46:this.$={id1:a[s-3],id2:a[s],relation:a[s-2],relationTitle1:"none",relationTitle2:a[s-1]};break;case 47:this.$={id1:a[s-4],id2:a[s],relation:a[s-2],relationTitle1:a[s-3],relationTitle2:a[s-1]};break;case 48:this.$={type1:a[s-2],type2:a[s],lineType:a[s-1]};break;case 49:this.$={type1:"none",type2:a[s],lineType:a[s-1]};break;case 50:this.$={type1:a[s-1],type2:"none",lineType:a[s]};break;case 51:this.$={type1:"none",type2:"none",lineType:a[s]};break;case 52:this.$=r.relationType.AGGREGATION;break;case 53:this.$=r.relationType.EXTENSION;break;case 54:this.$=r.relationType.COMPOSITION;break;case 55:this.$=r.relationType.DEPENDENCY;break;case 56:this.$=r.lineType.LINE;break;case 57:this.$=r.lineType.DOTTED_LINE;break;case 58:case 64:this.$=a[s-2],r.setClickEvent(a[s-1],a[s]);break;case 59:case 65:this.$=a[s-3],r.setClickEvent(a[s-2],a[s-1]),r.setTooltip(a[s-2],a[s]);break;case 60:case 68:this.$=a[s-2],r.setLink(a[s-1],a[s]);break;case 61:case 69:this.$=a[s-3],r.setLink(a[s-2],a[s-1],a[s]);break;case 62:case 70:this.$=a[s-3],r.setLink(a[s-2],a[s-1]),r.setTooltip(a[s-2],a[s]);break;case 63:case 71:this.$=a[s-4],r.setLink(a[s-3],a[s-2],a[s]),r.setTooltip(a[s-3],a[s-1]);break;case 66:this.$=a[s-3],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 67:this.$=a[s-4],r.setClickEvent(a[s-3],a[s-2],a[s-1]),r.setTooltip(a[s-3],a[s]);break;case 72:r.setCssClass(a[s-1],a[s])}},table:[{3:1,4:2,5:3,6:4,7:e,8:n,9:r,10:i,11:5,12:10,18:a,22:o},{1:[3]},{1:[2,1]},{1:[2,2]},{3:13,4:2,5:3,6:4,7:e,8:n,9:r,10:i,11:5,12:10,18:a,22:o},{1:[2,8]},t(s,[2,4]),t(s,[2,5]),t(s,[2,6]),t(s,[2,7]),{13:14,19:[1,15]},{15:[1,16]},{19:[2,11]},{1:[2,3]},{14:17,16:[1,18],21:c},t([16,21],[2,12]),{5:29,6:28,7:e,8:n,9:r,10:i,12:10,18:a,23:20,25:21,26:30,27:39,28:40,30:22,32:23,33:24,34:25,35:26,36:27,37:u,42:l,44:h,45:f,56:d,57:p,59:y,63:g,77:m,78:v,79:b,80:_},{15:[1,45]},{17:46,20:[1,47]},{15:[2,14]},{24:[1,48]},{15:[1,49],24:[2,16]},t(x,[2,24],{31:[1,50]}),t(x,[2,26]),t(x,[2,27]),t(x,[2,28]),t(x,[2,29]),t(x,[2,30]),t(x,[2,31]),t(x,[2,32]),t(x,[2,40],{46:51,48:54,49:55,31:[1,53],47:[1,52],50:w,51:k,52:T,53:E,54:C,55:S}),{26:62,27:39,28:40,77:m,78:v,79:b,80:_},t(x,[2,42]),t(x,[2,43]),{27:63,77:m,78:v,79:b},{26:64,27:39,28:40,77:m,78:v,79:b,80:_},{26:65,27:39,28:40,77:m,78:v,79:b,80:_},{26:66,27:39,28:40,77:m,78:v,79:b,80:_},{47:[1,67]},t(A,[2,19],{27:39,28:40,26:68,29:[1,69],77:m,78:v,79:b,80:_}),t(A,[2,20],{29:[1,70]}),t(M,[2,86]),t(M,[2,87]),t(M,[2,88]),t([15,24,29,31,38,39,47,50,51,52,53,54,55,60,62],[2,89]),t(N,[2,9]),{14:71,21:c},{21:[2,13]},{1:[2,15]},{5:29,6:28,7:e,8:n,9:r,10:i,12:10,18:a,23:72,24:[2,17],25:21,26:30,27:39,28:40,30:22,32:23,33:24,34:25,35:26,36:27,37:u,42:l,44:h,45:f,56:d,57:p,59:y,63:g,77:m,78:v,79:b,80:_},t(x,[2,25]),{26:73,27:39,28:40,47:[1,74],77:m,78:v,79:b,80:_},{46:75,48:54,49:55,50:w,51:k,52:T,53:E,54:C,55:S},t(x,[2,41]),{49:76,54:C,55:S},t(D,[2,51],{48:77,50:w,51:k,52:T,53:E}),t(O,[2,52]),t(O,[2,53]),t(O,[2,54]),t(O,[2,55]),t(B,[2,56]),t(B,[2,57]),t(x,[2,33],{38:[1,78],39:[1,79]}),{43:[1,80]},{47:[1,81]},{47:[1,82]},{60:[1,83],62:[1,84]},{27:85,77:m,78:v,79:b},t(A,[2,21]),t(A,[2,22]),t(A,[2,23]),{15:[1,86]},{24:[2,18]},t(L,[2,44]),{26:87,27:39,28:40,77:m,78:v,79:b,80:_},{26:88,27:39,28:40,47:[1,89],77:m,78:v,79:b,80:_},t(D,[2,50],{48:90,50:w,51:k,52:T,53:E}),t(D,[2,49]),{27:91,77:m,78:v,79:b},{40:92,44:I},{26:94,27:39,28:40,77:m,78:v,79:b,80:_},t(x,[2,58],{47:[1,95]}),t(x,[2,60],{47:[1,97],58:[1,96]}),t(x,[2,64],{47:[1,98],61:[1,99]}),t(x,[2,68],{47:[1,101],58:[1,100]}),t(x,[2,72]),t(N,[2,10]),t(L,[2,46]),t(L,[2,45]),{26:102,27:39,28:40,77:m,78:v,79:b,80:_},t(D,[2,48]),t(x,[2,34],{39:[1,103]}),{41:[1,104]},{40:105,41:[2,38],44:I},t(x,[2,37]),t(x,[2,59]),t(x,[2,61]),t(x,[2,62],{58:[1,106]}),t(x,[2,65]),t(x,[2,66],{47:[1,107]}),t(x,[2,69]),t(x,[2,70],{58:[1,108]}),t(L,[2,47]),{40:109,44:I},t(x,[2,35]),{41:[2,39]},t(x,[2,63]),t(x,[2,67]),t(x,[2,71]),{41:[1,110]},t(x,[2,36])],defaultActions:{2:[2,1],3:[2,2],5:[2,8],12:[2,11],13:[2,3],19:[2,14],47:[2,13],48:[2,15],72:[2,18],105:[2,39]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},F={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),18;case 1:return 7;case 2:return 8;case 3:return 9;case 4:return 10;case 5:return this.begin("type_directive"),19;case 6:return this.popState(),this.begin("arg_directive"),16;case 7:return this.popState(),this.popState(),21;case 8:return 20;case 9:case 10:case 12:case 19:break;case 11:return 15;case 13:case 14:return 22;case 15:return this.begin("struct"),39;case 16:return"EOF_IN_STRUCT";case 17:return"OPEN_IN_STRUCT";case 18:return this.popState(),41;case 20:return"MEMBER";case 21:return 37;case 22:return 63;case 23:return 56;case 24:return 57;case 25:return 59;case 26:return 42;case 27:return 43;case 28:this.begin("generic");break;case 29:case 32:case 35:case 38:case 41:case 44:this.popState();break;case 30:return"GENERICTYPE";case 31:this.begin("string");break;case 33:return"STR";case 34:this.begin("bqstring");break;case 36:return"BQUOTE_STR";case 37:this.begin("href");break;case 39:return 62;case 40:this.begin("callback_name");break;case 42:this.popState(),this.begin("callback_args");break;case 43:return 60;case 45:return 61;case 46:case 47:case 48:case 49:return 58;case 50:case 51:return 51;case 52:case 53:return 53;case 54:return 52;case 55:return 50;case 56:return 54;case 57:return 55;case 58:return 31;case 59:return 38;case 60:return 75;case 61:return"DOT";case 62:return"PLUS";case 63:return 72;case 64:case 65:return"EQUALS";case 66:return 79;case 67:return"PUNCTUATION";case 68:return 78;case 69:return 77;case 70:return 74;case 71:return 24}},rules:[/^(?:%%\{)/,/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:((?:(?!\}%%)[^:.])*))/,/^(?::)/,/^(?:\}%%)/,/^(?:((?:(?!\}%%).|\n)*))/,/^(?:%%(?!\{)*[^\n]*(\r?\n?)+)/,/^(?:%%[^\n]*(\r?\n)*)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:classDiagram-v2\b)/,/^(?:classDiagram\b)/,/^(?:[{])/,/^(?:$)/,/^(?:[{])/,/^(?:[}])/,/^(?:[\n])/,/^(?:[^{}\n]*)/,/^(?:class\b)/,/^(?:cssClass\b)/,/^(?:callback\b)/,/^(?:link\b)/,/^(?:click\b)/,/^(?:<<)/,/^(?:>>)/,/^(?:[~])/,/^(?:[~])/,/^(?:[^~]*)/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:[`])/,/^(?:[`])/,/^(?:[^`]+)/,/^(?:href[\s]+["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:\s*<\|)/,/^(?:\s*\|>)/,/^(?:\s*>)/,/^(?:\s*<)/,/^(?:\s*\*)/,/^(?:\s*o\b)/,/^(?:--)/,/^(?:\.\.)/,/^(?::{1}[^:\n;]+)/,/^(?::{3})/,/^(?:-)/,/^(?:\.)/,/^(?:\+)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:\w+)/,/^(?:[!"#$%&'*+,-.`?\\/])/,/^(?:[0-9]+)/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\s)/,/^(?:$)/],conditions:{arg_directive:{rules:[7,8],inclusive:!1},type_directive:{rules:[6,7],inclusive:!1},open_directive:{rules:[5],inclusive:!1},callback_args:{rules:[44,45],inclusive:!1},callback_name:{rules:[41,42,43],inclusive:!1},href:{rules:[38,39],inclusive:!1},struct:{rules:[16,17,18,19,20],inclusive:!1},generic:{rules:[29,30],inclusive:!1},bqstring:{rules:[35,36],inclusive:!1},string:{rules:[32,33],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,9,10,11,12,13,14,15,21,22,23,24,25,26,27,28,31,34,37,40,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71],inclusive:!0}}};function P(){this.yy={}}return R.lexer=F,P.prototype=R,R.Parser=P,new P}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(8218).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},5890:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,5],r=[6,9,11,23,41],i=[1,17],a=[1,20],o=[1,25],s=[1,26],c=[1,27],u=[1,28],l=[1,37],h=[23,38,39],f=[4,6,9,11,23,41],d=[34,35,36,37],p=[22,29],y=[1,55],g={trace:function(){},yy:{},symbols_:{error:2,start:3,ER_DIAGRAM:4,document:5,EOF:6,directive:7,line:8,SPACE:9,statement:10,NEWLINE:11,openDirective:12,typeDirective:13,closeDirective:14,":":15,argDirective:16,entityName:17,relSpec:18,role:19,BLOCK_START:20,attributes:21,BLOCK_STOP:22,ALPHANUM:23,attribute:24,attributeType:25,attributeName:26,attributeKeyType:27,attributeComment:28,ATTRIBUTE_WORD:29,ATTRIBUTE_KEY:30,COMMENT:31,cardinality:32,relType:33,ZERO_OR_ONE:34,ZERO_OR_MORE:35,ONE_OR_MORE:36,ONLY_ONE:37,NON_IDENTIFYING:38,IDENTIFYING:39,WORD:40,open_directive:41,type_directive:42,arg_directive:43,close_directive:44,$accept:0,$end:1},terminals_:{2:"error",4:"ER_DIAGRAM",6:"EOF",9:"SPACE",11:"NEWLINE",15:":",20:"BLOCK_START",22:"BLOCK_STOP",23:"ALPHANUM",29:"ATTRIBUTE_WORD",30:"ATTRIBUTE_KEY",31:"COMMENT",34:"ZERO_OR_ONE",35:"ZERO_OR_MORE",36:"ONE_OR_MORE",37:"ONLY_ONE",38:"NON_IDENTIFYING",39:"IDENTIFYING",40:"WORD",41:"open_directive",42:"type_directive",43:"arg_directive",44:"close_directive"},productions_:[0,[3,3],[3,2],[5,0],[5,2],[8,2],[8,1],[8,1],[8,1],[7,4],[7,6],[10,1],[10,5],[10,4],[10,3],[10,1],[17,1],[21,1],[21,2],[24,2],[24,3],[24,3],[24,4],[25,1],[26,1],[27,1],[28,1],[18,3],[32,1],[32,1],[32,1],[32,1],[33,1],[33,1],[19,1],[19,1],[12,1],[13,1],[16,1],[14,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:break;case 3:case 7:case 8:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:case 16:case 23:case 24:case 25:case 35:this.$=a[s];break;case 12:r.addEntity(a[s-4]),r.addEntity(a[s-2]),r.addRelationship(a[s-4],a[s],a[s-2],a[s-3]);break;case 13:r.addEntity(a[s-3]),r.addAttributes(a[s-3],a[s-1]);break;case 14:r.addEntity(a[s-2]);break;case 15:r.addEntity(a[s]);break;case 17:this.$=[a[s]];break;case 18:a[s].push(a[s-1]),this.$=a[s];break;case 19:this.$={attributeType:a[s-1],attributeName:a[s]};break;case 20:this.$={attributeType:a[s-2],attributeName:a[s-1],attributeKeyType:a[s]};break;case 21:this.$={attributeType:a[s-2],attributeName:a[s-1],attributeComment:a[s]};break;case 22:this.$={attributeType:a[s-3],attributeName:a[s-2],attributeKeyType:a[s-1],attributeComment:a[s]};break;case 26:case 34:this.$=a[s].replace(/"/g,"");break;case 27:this.$={cardA:a[s],relType:a[s-1],cardB:a[s-2]};break;case 28:this.$=r.Cardinality.ZERO_OR_ONE;break;case 29:this.$=r.Cardinality.ZERO_OR_MORE;break;case 30:this.$=r.Cardinality.ONE_OR_MORE;break;case 31:this.$=r.Cardinality.ONLY_ONE;break;case 32:this.$=r.Identification.NON_IDENTIFYING;break;case 33:this.$=r.Identification.IDENTIFYING;break;case 36:r.parseDirective("%%{","open_directive");break;case 37:r.parseDirective(a[s],"type_directive");break;case 38:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 39:r.parseDirective("}%%","close_directive","er")}},table:[{3:1,4:e,7:3,12:4,41:n},{1:[3]},t(r,[2,3],{5:6}),{3:7,4:e,7:3,12:4,41:n},{13:8,42:[1,9]},{42:[2,36]},{6:[1,10],7:15,8:11,9:[1,12],10:13,11:[1,14],12:4,17:16,23:i,41:n},{1:[2,2]},{14:18,15:[1,19],44:a},t([15,44],[2,37]),t(r,[2,8],{1:[2,1]}),t(r,[2,4]),{7:15,10:21,12:4,17:16,23:i,41:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,11]),t(r,[2,15],{18:22,32:24,20:[1,23],34:o,35:s,36:c,37:u}),t([6,9,11,15,20,23,34,35,36,37,41],[2,16]),{11:[1,29]},{16:30,43:[1,31]},{11:[2,39]},t(r,[2,5]),{17:32,23:i},{21:33,22:[1,34],24:35,25:36,29:l},{33:38,38:[1,39],39:[1,40]},t(h,[2,28]),t(h,[2,29]),t(h,[2,30]),t(h,[2,31]),t(f,[2,9]),{14:41,44:a},{44:[2,38]},{15:[1,42]},{22:[1,43]},t(r,[2,14]),{21:44,22:[2,17],24:35,25:36,29:l},{26:45,29:[1,46]},{29:[2,23]},{32:47,34:o,35:s,36:c,37:u},t(d,[2,32]),t(d,[2,33]),{11:[1,48]},{19:49,23:[1,51],40:[1,50]},t(r,[2,13]),{22:[2,18]},t(p,[2,19],{27:52,28:53,30:[1,54],31:y}),t([22,29,30,31],[2,24]),{23:[2,27]},t(f,[2,10]),t(r,[2,12]),t(r,[2,34]),t(r,[2,35]),t(p,[2,20],{28:56,31:y}),t(p,[2,21]),t([22,29,31],[2,25]),t(p,[2,26]),t(p,[2,22])],defaultActions:{5:[2,36],7:[2,2],20:[2,39],31:[2,38],37:[2,23],44:[2,18],47:[2,27]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},m={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),41;case 1:return this.begin("type_directive"),42;case 2:return this.popState(),this.begin("arg_directive"),15;case 3:return this.popState(),this.popState(),44;case 4:return 43;case 5:case 6:case 8:case 13:case 17:break;case 7:return 11;case 9:return 9;case 10:return 40;case 11:return 4;case 12:return this.begin("block"),20;case 14:return 30;case 15:return 29;case 16:return 31;case 18:return this.popState(),22;case 19:case 32:return e.yytext[0];case 20:case 24:return 34;case 21:case 25:return 35;case 22:case 26:return 36;case 23:return 37;case 27:case 29:case 30:return 38;case 28:return 39;case 31:return 23;case 33:return 6}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:[\s]+)/i,/^(?:"[^"]*")/i,/^(?:erDiagram\b)/i,/^(?:\{)/i,/^(?:\s+)/i,/^(?:(?:PK)|(?:FK))/i,/^(?:[A-Za-z][A-Za-z0-9\-_]*)/i,/^(?:"[^"]*")/i,/^(?:[\n]+)/i,/^(?:\})/i,/^(?:.)/i,/^(?:\|o\b)/i,/^(?:\}o\b)/i,/^(?:\}\|)/i,/^(?:\|\|)/i,/^(?:o\|)/i,/^(?:o\{)/i,/^(?:\|\{)/i,/^(?:\.\.)/i,/^(?:--)/i,/^(?:\.-)/i,/^(?:-\.)/i,/^(?:[A-Za-z][A-Za-z0-9\-_]*)/i,/^(?:.)/i,/^(?:$)/i],conditions:{open_directive:{rules:[1],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},block:{rules:[13,14,15,16,17,18,19],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,20,21,22,23,24,25,26,27,28,29,30,31,32,33],inclusive:!0}}};function v(){this.yy={}}return g.lexer=m,v.prototype=g,g.Parser=v,new v}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(8009).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},3602:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,9],n=[1,7],r=[1,6],i=[1,8],a=[1,20,21,22,23,38,47,59,60,79,80,81,82,83,84,88,98,99,102,104,105,111,112,113,114,115,116,117,118,119,120],o=[2,10],s=[1,20],c=[1,21],u=[1,22],l=[1,23],h=[1,30],f=[1,59],d=[1,45],p=[1,49],y=[1,33],g=[1,34],m=[1,35],v=[1,36],b=[1,37],_=[1,53],x=[1,60],w=[1,48],k=[1,50],T=[1,52],E=[1,56],C=[1,57],S=[1,38],A=[1,39],M=[1,40],N=[1,41],D=[1,58],O=[1,47],B=[1,51],L=[1,54],I=[1,55],R=[1,46],F=[1,63],P=[1,68],j=[1,20,21,22,23,38,42,47,59,60,79,80,81,82,83,84,88,98,99,102,104,105,111,112,113,114,115,116,117,118,119,120],Y=[1,72],z=[1,71],U=[1,73],q=[20,21,23,74,75],H=[1,94],$=[1,99],W=[1,102],V=[1,103],G=[1,96],X=[1,101],Z=[1,104],Q=[1,97],K=[1,109],J=[1,108],tt=[1,98],et=[1,100],nt=[1,105],rt=[1,106],it=[1,107],at=[1,110],ot=[20,21,22,23,74,75],st=[20,21,22,23,48,74,75],ct=[20,21,22,23,40,47,48,50,52,54,56,58,59,60,62,64,66,67,69,74,75,84,88,98,99,102,104,105,115,116,117,118,119,120],ut=[20,21,23],lt=[20,21,23,47,59,60,74,75,84,88,98,99,102,104,105,115,116,117,118,119,120],ht=[1,12,20,21,22,23,24,38,42,47,59,60,79,80,81,82,83,84,88,98,99,102,104,105,111,112,113,114,115,116,117,118,119,120],ft=[47,59,60,84,88,98,99,102,104,105,115,116,117,118,119,120],dt=[1,143],pt=[1,151],yt=[1,152],gt=[1,153],mt=[1,154],vt=[1,138],bt=[1,139],_t=[1,135],xt=[1,146],wt=[1,147],kt=[1,148],Tt=[1,149],Et=[1,150],Ct=[1,155],St=[1,156],At=[1,141],Mt=[1,144],Nt=[1,140],Dt=[1,137],Ot=[20,21,22,23,38,42,47,59,60,79,80,81,82,83,84,88,98,99,102,104,105,111,112,113,114,115,116,117,118,119,120],Bt=[1,159],Lt=[20,21,22,23,26,47,59,60,84,98,99,102,104,105,115,116,117,118,119,120],It=[20,21,22,23,24,26,38,40,41,42,47,51,53,55,57,59,60,61,63,65,66,68,70,74,75,79,80,81,82,83,84,85,88,98,99,102,104,105,106,107,115,116,117,118,119,120],Rt=[12,21,22,24],Ft=[22,99],Pt=[1,242],jt=[1,237],Yt=[1,238],zt=[1,246],Ut=[1,243],qt=[1,240],Ht=[1,239],$t=[1,241],Wt=[1,244],Vt=[1,245],Gt=[1,247],Xt=[1,265],Zt=[20,21,23,99],Qt=[20,21,22,23,59,60,79,95,98,99,102,103,104,105,106],Kt={trace:function(){},yy:{},symbols_:{error:2,start:3,mermaidDoc:4,directive:5,openDirective:6,typeDirective:7,closeDirective:8,separator:9,":":10,argDirective:11,open_directive:12,type_directive:13,arg_directive:14,close_directive:15,graphConfig:16,document:17,line:18,statement:19,SEMI:20,NEWLINE:21,SPACE:22,EOF:23,GRAPH:24,NODIR:25,DIR:26,FirstStmtSeperator:27,ending:28,endToken:29,spaceList:30,spaceListNewline:31,verticeStatement:32,styleStatement:33,linkStyleStatement:34,classDefStatement:35,classStatement:36,clickStatement:37,subgraph:38,text:39,SQS:40,SQE:41,end:42,direction:43,link:44,node:45,vertex:46,AMP:47,STYLE_SEPARATOR:48,idString:49,PS:50,PE:51,"(-":52,"-)":53,STADIUMSTART:54,STADIUMEND:55,SUBROUTINESTART:56,SUBROUTINEEND:57,VERTEX_WITH_PROPS_START:58,ALPHA:59,COLON:60,PIPE:61,CYLINDERSTART:62,CYLINDEREND:63,DIAMOND_START:64,DIAMOND_STOP:65,TAGEND:66,TRAPSTART:67,TRAPEND:68,INVTRAPSTART:69,INVTRAPEND:70,linkStatement:71,arrowText:72,TESTSTR:73,START_LINK:74,LINK:75,textToken:76,STR:77,keywords:78,STYLE:79,LINKSTYLE:80,CLASSDEF:81,CLASS:82,CLICK:83,DOWN:84,UP:85,textNoTags:86,textNoTagsToken:87,DEFAULT:88,stylesOpt:89,alphaNum:90,CALLBACKNAME:91,CALLBACKARGS:92,HREF:93,LINK_TARGET:94,HEX:95,numList:96,INTERPOLATE:97,NUM:98,COMMA:99,style:100,styleComponent:101,MINUS:102,UNIT:103,BRKT:104,DOT:105,PCT:106,TAGSTART:107,alphaNumToken:108,idStringToken:109,alphaNumStatement:110,direction_tb:111,direction_bt:112,direction_rl:113,direction_lr:114,PUNCTUATION:115,UNICODE_TEXT:116,PLUS:117,EQUALS:118,MULT:119,UNDERSCORE:120,graphCodeTokens:121,ARROW_CROSS:122,ARROW_POINT:123,ARROW_CIRCLE:124,ARROW_OPEN:125,QUOTE:126,$accept:0,$end:1},terminals_:{2:"error",10:":",12:"open_directive",13:"type_directive",14:"arg_directive",15:"close_directive",20:"SEMI",21:"NEWLINE",22:"SPACE",23:"EOF",24:"GRAPH",25:"NODIR",26:"DIR",38:"subgraph",40:"SQS",41:"SQE",42:"end",47:"AMP",48:"STYLE_SEPARATOR",50:"PS",51:"PE",52:"(-",53:"-)",54:"STADIUMSTART",55:"STADIUMEND",56:"SUBROUTINESTART",57:"SUBROUTINEEND",58:"VERTEX_WITH_PROPS_START",59:"ALPHA",60:"COLON",61:"PIPE",62:"CYLINDERSTART",63:"CYLINDEREND",64:"DIAMOND_START",65:"DIAMOND_STOP",66:"TAGEND",67:"TRAPSTART",68:"TRAPEND",69:"INVTRAPSTART",70:"INVTRAPEND",73:"TESTSTR",74:"START_LINK",75:"LINK",77:"STR",79:"STYLE",80:"LINKSTYLE",81:"CLASSDEF",82:"CLASS",83:"CLICK",84:"DOWN",85:"UP",88:"DEFAULT",91:"CALLBACKNAME",92:"CALLBACKARGS",93:"HREF",94:"LINK_TARGET",95:"HEX",97:"INTERPOLATE",98:"NUM",99:"COMMA",102:"MINUS",103:"UNIT",104:"BRKT",105:"DOT",106:"PCT",107:"TAGSTART",111:"direction_tb",112:"direction_bt",113:"direction_rl",114:"direction_lr",115:"PUNCTUATION",116:"UNICODE_TEXT",117:"PLUS",118:"EQUALS",119:"MULT",120:"UNDERSCORE",122:"ARROW_CROSS",123:"ARROW_POINT",124:"ARROW_CIRCLE",125:"ARROW_OPEN",126:"QUOTE"},productions_:[0,[3,1],[3,2],[5,4],[5,6],[6,1],[7,1],[11,1],[8,1],[4,2],[17,0],[17,2],[18,1],[18,1],[18,1],[18,1],[18,1],[16,2],[16,2],[16,2],[16,3],[28,2],[28,1],[29,1],[29,1],[29,1],[27,1],[27,1],[27,2],[31,2],[31,2],[31,1],[31,1],[30,2],[30,1],[19,2],[19,2],[19,2],[19,2],[19,2],[19,2],[19,9],[19,6],[19,4],[19,1],[9,1],[9,1],[9,1],[32,3],[32,4],[32,2],[32,1],[45,1],[45,5],[45,3],[46,4],[46,6],[46,4],[46,4],[46,4],[46,8],[46,4],[46,4],[46,4],[46,6],[46,4],[46,4],[46,4],[46,4],[46,4],[46,1],[44,2],[44,3],[44,3],[44,1],[44,3],[71,1],[72,3],[39,1],[39,2],[39,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[86,1],[86,2],[35,5],[35,5],[36,5],[37,2],[37,4],[37,3],[37,5],[37,2],[37,4],[37,4],[37,6],[37,2],[37,4],[37,2],[37,4],[37,4],[37,6],[33,5],[33,5],[34,5],[34,5],[34,9],[34,9],[34,7],[34,7],[96,1],[96,3],[89,1],[89,3],[100,1],[100,2],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[76,1],[76,1],[76,1],[76,1],[76,1],[76,1],[87,1],[87,1],[87,1],[87,1],[49,1],[49,2],[90,1],[90,2],[110,1],[110,1],[110,1],[110,1],[43,1],[43,1],[43,1],[43,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 5:r.parseDirective("%%{","open_directive");break;case 6:r.parseDirective(a[s],"type_directive");break;case 7:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 8:r.parseDirective("}%%","close_directive","flowchart");break;case 10:case 36:case 37:case 38:case 39:case 40:this.$=[];break;case 11:a[s]!==[]&&a[s-1].push(a[s]),this.$=a[s-1];break;case 12:case 78:case 80:case 92:case 148:case 150:case 151:case 74:case 146:this.$=a[s];break;case 19:r.setDirection("TB"),this.$="TB";break;case 20:r.setDirection(a[s-1]),this.$=a[s-1];break;case 35:this.$=a[s-1].nodes;break;case 41:this.$=r.addSubGraph(a[s-6],a[s-1],a[s-4]);break;case 42:this.$=r.addSubGraph(a[s-3],a[s-1],a[s-3]);break;case 43:this.$=r.addSubGraph(void 0,a[s-1],void 0);break;case 48:r.addLink(a[s-2].stmt,a[s],a[s-1]),this.$={stmt:a[s],nodes:a[s].concat(a[s-2].nodes)};break;case 49:r.addLink(a[s-3].stmt,a[s-1],a[s-2]),this.$={stmt:a[s-1],nodes:a[s-1].concat(a[s-3].nodes)};break;case 50:this.$={stmt:a[s-1],nodes:a[s-1]};break;case 51:this.$={stmt:a[s],nodes:a[s]};break;case 52:case 119:case 121:this.$=[a[s]];break;case 53:this.$=a[s-4].concat(a[s]);break;case 54:this.$=[a[s-2]],r.setClass(a[s-2],a[s]);break;case 55:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"square");break;case 56:this.$=a[s-5],r.addVertex(a[s-5],a[s-2],"circle");break;case 57:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"ellipse");break;case 58:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"stadium");break;case 59:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"subroutine");break;case 60:this.$=a[s-7],r.addVertex(a[s-7],a[s-1],"rect",void 0,void 0,void 0,Object.fromEntries([[a[s-5],a[s-3]]]));break;case 61:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"cylinder");break;case 62:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"round");break;case 63:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"diamond");break;case 64:this.$=a[s-5],r.addVertex(a[s-5],a[s-2],"hexagon");break;case 65:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"odd");break;case 66:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"trapezoid");break;case 67:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"inv_trapezoid");break;case 68:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"lean_right");break;case 69:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"lean_left");break;case 70:this.$=a[s],r.addVertex(a[s]);break;case 71:a[s-1].text=a[s],this.$=a[s-1];break;case 72:case 73:a[s-2].text=a[s-1],this.$=a[s-2];break;case 75:var c=r.destructLink(a[s],a[s-2]);this.$={type:c.type,stroke:c.stroke,length:c.length,text:a[s-1]};break;case 76:c=r.destructLink(a[s]),this.$={type:c.type,stroke:c.stroke,length:c.length};break;case 77:this.$=a[s-1];break;case 79:case 93:case 149:case 147:this.$=a[s-1]+""+a[s];break;case 94:case 95:this.$=a[s-4],r.addClass(a[s-2],a[s]);break;case 96:this.$=a[s-4],r.setClass(a[s-2],a[s]);break;case 97:case 105:this.$=a[s-1],r.setClickEvent(a[s-1],a[s]);break;case 98:case 106:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-2]),r.setTooltip(a[s-3],a[s]);break;case 99:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 100:this.$=a[s-4],r.setClickEvent(a[s-4],a[s-3],a[s-2]),r.setTooltip(a[s-4],a[s]);break;case 101:case 107:this.$=a[s-1],r.setLink(a[s-1],a[s]);break;case 102:case 108:this.$=a[s-3],r.setLink(a[s-3],a[s-2]),r.setTooltip(a[s-3],a[s]);break;case 103:case 109:this.$=a[s-3],r.setLink(a[s-3],a[s-2],a[s]);break;case 104:case 110:this.$=a[s-5],r.setLink(a[s-5],a[s-4],a[s]),r.setTooltip(a[s-5],a[s-2]);break;case 111:this.$=a[s-4],r.addVertex(a[s-2],void 0,void 0,a[s]);break;case 112:case 114:this.$=a[s-4],r.updateLink(a[s-2],a[s]);break;case 113:this.$=a[s-4],r.updateLink([a[s-2]],a[s]);break;case 115:this.$=a[s-8],r.updateLinkInterpolate([a[s-6]],a[s-2]),r.updateLink([a[s-6]],a[s]);break;case 116:this.$=a[s-8],r.updateLinkInterpolate(a[s-6],a[s-2]),r.updateLink(a[s-6],a[s]);break;case 117:this.$=a[s-6],r.updateLinkInterpolate([a[s-4]],a[s]);break;case 118:this.$=a[s-6],r.updateLinkInterpolate(a[s-4],a[s]);break;case 120:case 122:a[s-2].push(a[s]),this.$=a[s-2];break;case 124:this.$=a[s-1]+a[s];break;case 152:this.$="v";break;case 153:this.$="-";break;case 154:this.$={stmt:"dir",value:"TB"};break;case 155:this.$={stmt:"dir",value:"BT"};break;case 156:this.$={stmt:"dir",value:"RL"};break;case 157:this.$={stmt:"dir",value:"LR"}}},table:[{3:1,4:2,5:3,6:5,12:e,16:4,21:n,22:r,24:i},{1:[3]},{1:[2,1]},{3:10,4:2,5:3,6:5,12:e,16:4,21:n,22:r,24:i},t(a,o,{17:11}),{7:12,13:[1,13]},{16:14,21:n,22:r,24:i},{16:15,21:n,22:r,24:i},{25:[1,16],26:[1,17]},{13:[2,5]},{1:[2,2]},{1:[2,9],18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,43:31,45:32,46:42,47:f,49:43,59:d,60:p,79:y,80:g,81:m,82:v,83:b,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,111:S,112:A,113:M,114:N,115:D,116:O,117:B,118:L,119:I,120:R},{8:61,10:[1,62],15:F},t([10,15],[2,6]),t(a,[2,17]),t(a,[2,18]),t(a,[2,19]),{20:[1,65],21:[1,66],22:P,27:64,30:67},t(j,[2,11]),t(j,[2,12]),t(j,[2,13]),t(j,[2,14]),t(j,[2,15]),t(j,[2,16]),{9:69,20:Y,21:z,23:U,44:70,71:74,74:[1,75],75:[1,76]},{9:77,20:Y,21:z,23:U},{9:78,20:Y,21:z,23:U},{9:79,20:Y,21:z,23:U},{9:80,20:Y,21:z,23:U},{9:81,20:Y,21:z,23:U},{9:83,20:Y,21:z,22:[1,82],23:U},t(j,[2,44]),t(q,[2,51],{30:84,22:P}),{22:[1,85]},{22:[1,86]},{22:[1,87]},{22:[1,88]},{26:H,47:$,59:W,60:V,77:[1,92],84:G,90:91,91:[1,89],93:[1,90],98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(j,[2,154]),t(j,[2,155]),t(j,[2,156]),t(j,[2,157]),t(ot,[2,52],{48:[1,111]}),t(st,[2,70],{109:123,40:[1,112],47:f,50:[1,113],52:[1,114],54:[1,115],56:[1,116],58:[1,117],59:d,60:p,62:[1,118],64:[1,119],66:[1,120],67:[1,121],69:[1,122],84:_,88:x,98:w,99:k,102:T,104:E,105:C,115:D,116:O,117:B,118:L,119:I,120:R}),t(ct,[2,146]),t(ct,[2,171]),t(ct,[2,172]),t(ct,[2,173]),t(ct,[2,174]),t(ct,[2,175]),t(ct,[2,176]),t(ct,[2,177]),t(ct,[2,178]),t(ct,[2,179]),t(ct,[2,180]),t(ct,[2,181]),t(ct,[2,182]),t(ct,[2,183]),t(ct,[2,184]),t(ct,[2,185]),t(ct,[2,186]),{9:124,20:Y,21:z,23:U},{11:125,14:[1,126]},t(ut,[2,8]),t(a,[2,20]),t(a,[2,26]),t(a,[2,27]),{21:[1,127]},t(lt,[2,34],{30:128,22:P}),t(j,[2,35]),{45:129,46:42,47:f,49:43,59:d,60:p,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,115:D,116:O,117:B,118:L,119:I,120:R},t(ht,[2,45]),t(ht,[2,46]),t(ht,[2,47]),t(ft,[2,74],{72:130,61:[1,132],73:[1,131]}),{22:dt,24:pt,26:yt,38:gt,39:133,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t([47,59,60,61,73,84,88,98,99,102,104,105,115,116,117,118,119,120],[2,76]),t(j,[2,36]),t(j,[2,37]),t(j,[2,38]),t(j,[2,39]),t(j,[2,40]),{22:dt,24:pt,26:yt,38:gt,39:157,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(Ot,o,{17:158}),t(q,[2,50],{47:Bt}),{26:H,47:$,59:W,60:V,84:G,90:160,95:[1,161],98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},{88:[1,162],96:163,98:[1,164]},{26:H,47:$,59:W,60:V,84:G,88:[1,165],90:166,98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},{26:H,47:$,59:W,60:V,84:G,90:167,98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(ut,[2,97],{22:[1,168],92:[1,169]}),t(ut,[2,101],{22:[1,170]}),t(ut,[2,105],{108:95,110:172,22:[1,171],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,115:tt,116:et,117:nt,118:rt,119:it,120:at}),t(ut,[2,107],{22:[1,173]}),t(Lt,[2,148]),t(Lt,[2,150]),t(Lt,[2,151]),t(Lt,[2,152]),t(Lt,[2,153]),t(It,[2,158]),t(It,[2,159]),t(It,[2,160]),t(It,[2,161]),t(It,[2,162]),t(It,[2,163]),t(It,[2,164]),t(It,[2,165]),t(It,[2,166]),t(It,[2,167]),t(It,[2,168]),t(It,[2,169]),t(It,[2,170]),{47:f,49:174,59:d,60:p,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,115:D,116:O,117:B,118:L,119:I,120:R},{22:dt,24:pt,26:yt,38:gt,39:175,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:177,42:mt,47:$,50:[1,176],59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:178,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:179,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:180,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{59:[1,181]},{22:dt,24:pt,26:yt,38:gt,39:182,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:183,42:mt,47:$,59:W,60:V,64:[1,184],66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:185,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:186,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:187,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(ct,[2,147]),t(Rt,[2,3]),{8:188,15:F},{15:[2,7]},t(a,[2,28]),t(lt,[2,33]),t(q,[2,48],{30:189,22:P}),t(ft,[2,71],{22:[1,190]}),{22:[1,191]},{22:dt,24:pt,26:yt,38:gt,39:192,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,66:vt,74:bt,75:[1,193],76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(It,[2,78]),t(It,[2,80]),t(It,[2,136]),t(It,[2,137]),t(It,[2,138]),t(It,[2,139]),t(It,[2,140]),t(It,[2,141]),t(It,[2,142]),t(It,[2,143]),t(It,[2,144]),t(It,[2,145]),t(It,[2,81]),t(It,[2,82]),t(It,[2,83]),t(It,[2,84]),t(It,[2,85]),t(It,[2,86]),t(It,[2,87]),t(It,[2,88]),t(It,[2,89]),t(It,[2,90]),t(It,[2,91]),{9:196,20:Y,21:z,22:dt,23:U,24:pt,26:yt,38:gt,40:[1,195],42:mt,47:$,59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,197],43:31,45:32,46:42,47:f,49:43,59:d,60:p,79:y,80:g,81:m,82:v,83:b,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,111:S,112:A,113:M,114:N,115:D,116:O,117:B,118:L,119:I,120:R},{22:P,30:198},{22:[1,199],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,108:95,110:172,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:[1,200]},{22:[1,201]},{22:[1,202],99:[1,203]},t(Ft,[2,119]),{22:[1,204]},{22:[1,205],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,108:95,110:172,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:[1,206],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,108:95,110:172,115:tt,116:et,117:nt,118:rt,119:it,120:at},{77:[1,207]},t(ut,[2,99],{22:[1,208]}),{77:[1,209],94:[1,210]},{77:[1,211]},t(Lt,[2,149]),{77:[1,212],94:[1,213]},t(ot,[2,54],{109:123,47:f,59:d,60:p,84:_,88:x,98:w,99:k,102:T,104:E,105:C,115:D,116:O,117:B,118:L,119:I,120:R}),{22:dt,24:pt,26:yt,38:gt,41:[1,214],42:mt,47:$,59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:215,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,51:[1,216],59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,53:[1,217],59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,55:[1,218],59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,57:[1,219],59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{60:[1,220]},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,63:[1,221],66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,65:[1,222],66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:223,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,41:[1,224],42:mt,47:$,59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,66:vt,68:[1,225],70:[1,226],74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,66:vt,68:[1,228],70:[1,227],74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{9:229,20:Y,21:z,23:U},t(q,[2,49],{47:Bt}),t(ft,[2,73]),t(ft,[2,72]),{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,61:[1,230],66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(ft,[2,75]),t(It,[2,79]),{22:dt,24:pt,26:yt,38:gt,39:231,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(Ot,o,{17:232}),t(j,[2,43]),{46:233,47:f,49:43,59:d,60:p,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,115:D,116:O,117:B,118:L,119:I,120:R},{22:Pt,59:jt,60:Yt,79:zt,89:234,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{22:Pt,59:jt,60:Yt,79:zt,89:248,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{22:Pt,59:jt,60:Yt,79:zt,89:249,95:Ut,97:[1,250],98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{22:Pt,59:jt,60:Yt,79:zt,89:251,95:Ut,97:[1,252],98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{98:[1,253]},{22:Pt,59:jt,60:Yt,79:zt,89:254,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{22:Pt,59:jt,60:Yt,79:zt,89:255,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{26:H,47:$,59:W,60:V,84:G,90:256,98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(ut,[2,98]),{77:[1,257]},t(ut,[2,102],{22:[1,258]}),t(ut,[2,103]),t(ut,[2,106]),t(ut,[2,108],{22:[1,259]}),t(ut,[2,109]),t(st,[2,55]),{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,51:[1,260],59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(st,[2,62]),t(st,[2,57]),t(st,[2,58]),t(st,[2,59]),{59:[1,261]},t(st,[2,61]),t(st,[2,63]),{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,65:[1,262],66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(st,[2,65]),t(st,[2,66]),t(st,[2,68]),t(st,[2,67]),t(st,[2,69]),t(Rt,[2,4]),t([22,47,59,60,84,88,98,99,102,104,105,115,116,117,118,119,120],[2,77]),{22:dt,24:pt,26:yt,38:gt,41:[1,263],42:mt,47:$,59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,264],43:31,45:32,46:42,47:f,49:43,59:d,60:p,79:y,80:g,81:m,82:v,83:b,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,111:S,112:A,113:M,114:N,115:D,116:O,117:B,118:L,119:I,120:R},t(ot,[2,53]),t(ut,[2,111],{99:Xt}),t(Zt,[2,121],{101:266,22:Pt,59:jt,60:Yt,79:zt,95:Ut,98:qt,102:Ht,103:$t,104:Wt,105:Vt,106:Gt}),t(Qt,[2,123]),t(Qt,[2,125]),t(Qt,[2,126]),t(Qt,[2,127]),t(Qt,[2,128]),t(Qt,[2,129]),t(Qt,[2,130]),t(Qt,[2,131]),t(Qt,[2,132]),t(Qt,[2,133]),t(Qt,[2,134]),t(Qt,[2,135]),t(ut,[2,112],{99:Xt}),t(ut,[2,113],{99:Xt}),{22:[1,267]},t(ut,[2,114],{99:Xt}),{22:[1,268]},t(Ft,[2,120]),t(ut,[2,94],{99:Xt}),t(ut,[2,95],{99:Xt}),t(ut,[2,96],{108:95,110:172,26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,115:tt,116:et,117:nt,118:rt,119:it,120:at}),t(ut,[2,100]),{94:[1,269]},{94:[1,270]},{51:[1,271]},{61:[1,272]},{65:[1,273]},{9:274,20:Y,21:z,23:U},t(j,[2,42]),{22:Pt,59:jt,60:Yt,79:zt,95:Ut,98:qt,100:275,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},t(Qt,[2,124]),{26:H,47:$,59:W,60:V,84:G,90:276,98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},{26:H,47:$,59:W,60:V,84:G,90:277,98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(ut,[2,104]),t(ut,[2,110]),t(st,[2,56]),{22:dt,24:pt,26:yt,38:gt,39:278,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(st,[2,64]),t(Ot,o,{17:279}),t(Zt,[2,122],{101:266,22:Pt,59:jt,60:Yt,79:zt,95:Ut,98:qt,102:Ht,103:$t,104:Wt,105:Vt,106:Gt}),t(ut,[2,117],{108:95,110:172,22:[1,280],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,115:tt,116:et,117:nt,118:rt,119:it,120:at}),t(ut,[2,118],{108:95,110:172,22:[1,281],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,115:tt,116:et,117:nt,118:rt,119:it,120:at}),{22:dt,24:pt,26:yt,38:gt,41:[1,282],42:mt,47:$,59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,283],43:31,45:32,46:42,47:f,49:43,59:d,60:p,79:y,80:g,81:m,82:v,83:b,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,111:S,112:A,113:M,114:N,115:D,116:O,117:B,118:L,119:I,120:R},{22:Pt,59:jt,60:Yt,79:zt,89:284,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{22:Pt,59:jt,60:Yt,79:zt,89:285,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},t(st,[2,60]),t(j,[2,41]),t(ut,[2,115],{99:Xt}),t(ut,[2,116],{99:Xt})],defaultActions:{2:[2,1],9:[2,5],10:[2,2],126:[2,7]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},Jt={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),12;case 1:return this.begin("type_directive"),13;case 2:return this.popState(),this.begin("arg_directive"),10;case 3:return this.popState(),this.popState(),15;case 4:return 14;case 5:case 6:break;case 7:this.begin("string");break;case 8:case 17:case 20:case 23:case 26:this.popState();break;case 9:return"STR";case 10:return 79;case 11:return 88;case 12:return 80;case 13:return 97;case 14:return 81;case 15:return 82;case 16:this.begin("href");break;case 18:return 93;case 19:this.begin("callbackname");break;case 21:this.popState(),this.begin("callbackargs");break;case 22:return 91;case 24:return 92;case 25:this.begin("click");break;case 27:return 83;case 28:case 29:return t.lex.firstGraph()&&this.begin("dir"),24;case 30:return 38;case 31:return 42;case 32:case 33:case 34:case 35:return 94;case 36:return this.popState(),25;case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:return this.popState(),26;case 47:return 111;case 48:return 112;case 49:return 113;case 50:return 114;case 51:return 98;case 52:return 104;case 53:return 48;case 54:return 60;case 55:return 47;case 56:return 20;case 57:return 99;case 58:return 119;case 59:case 60:case 61:return 75;case 62:case 63:case 64:return 74;case 65:return 52;case 66:return 53;case 67:return 54;case 68:return 55;case 69:return 56;case 70:return 57;case 71:return 58;case 72:return 62;case 73:return 63;case 74:return 102;case 75:return 105;case 76:return 120;case 77:return 117;case 78:return 106;case 79:case 80:return 118;case 81:return 107;case 82:return 66;case 83:return 85;case 84:return"SEP";case 85:return 84;case 86:return 59;case 87:return 68;case 88:return 67;case 89:return 70;case 90:return 69;case 91:return 115;case 92:return 116;case 93:return 61;case 94:return 50;case 95:return 51;case 96:return 40;case 97:return 41;case 98:return 64;case 99:return 65;case 100:return 126;case 101:return 21;case 102:return 22;case 103:return 23}},rules:[/^(?:%%\{)/,/^(?:((?:(?!\}%%)[^:.])*))/,/^(?::)/,/^(?:\}%%)/,/^(?:((?:(?!\}%%).|\n)*))/,/^(?:%%(?!\{)[^\n]*)/,/^(?:[^\}]%%[^\n]*)/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:style\b)/,/^(?:default\b)/,/^(?:linkStyle\b)/,/^(?:interpolate\b)/,/^(?:classDef\b)/,/^(?:class\b)/,/^(?:href[\s]+["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:click[\s]+)/,/^(?:[\s\n])/,/^(?:[^\s\n]*)/,/^(?:graph\b)/,/^(?:flowchart\b)/,/^(?:subgraph\b)/,/^(?:end\b\s*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:(\r?\n)*\s*\n)/,/^(?:\s*LR\b)/,/^(?:\s*RL\b)/,/^(?:\s*TB\b)/,/^(?:\s*BT\b)/,/^(?:\s*TD\b)/,/^(?:\s*BR\b)/,/^(?:\s*<)/,/^(?:\s*>)/,/^(?:\s*\^)/,/^(?:\s*v\b)/,/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:[0-9]+)/,/^(?:#)/,/^(?::::)/,/^(?::)/,/^(?:&)/,/^(?:;)/,/^(?:,)/,/^(?:\*)/,/^(?:\s*[xo<]?--+[-xo>]\s*)/,/^(?:\s*[xo<]?==+[=xo>]\s*)/,/^(?:\s*[xo<]?-?\.+-[xo>]?\s*)/,/^(?:\s*[xo<]?--\s*)/,/^(?:\s*[xo<]?==\s*)/,/^(?:\s*[xo<]?-\.\s*)/,/^(?:\(-)/,/^(?:-\))/,/^(?:\(\[)/,/^(?:\]\))/,/^(?:\[\[)/,/^(?:\]\])/,/^(?:\[\|)/,/^(?:\[\()/,/^(?:\)\])/,/^(?:-)/,/^(?:\.)/,/^(?:[\_])/,/^(?:\+)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:<)/,/^(?:>)/,/^(?:\^)/,/^(?:\\\|)/,/^(?:v\b)/,/^(?:[A-Za-z]+)/,/^(?:\\\])/,/^(?:\[\/)/,/^(?:\/\])/,/^(?:\[\\)/,/^(?:[!"#$%&'*+,-.`?\\_/])/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\|)/,/^(?:\()/,/^(?:\))/,/^(?:\[)/,/^(?:\])/,/^(?:\{)/,/^(?:\})/,/^(?:")/,/^(?:(\r?\n)+)/,/^(?:\s)/,/^(?:$)/],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},callbackargs:{rules:[23,24],inclusive:!1},callbackname:{rules:[20,21,22],inclusive:!1},href:{rules:[17,18],inclusive:!1},click:{rules:[26,27],inclusive:!1},vertex:{rules:[],inclusive:!1},dir:{rules:[36,37,38,39,40,41,42,43,44,45,46],inclusive:!1},string:{rules:[8,9],inclusive:!1},INITIAL:{rules:[0,5,6,7,10,11,12,13,14,15,16,19,25,28,29,30,31,32,33,34,35,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103],inclusive:!0}}};function te(){this.yy={}}return Kt.lexer=Jt,te.prototype=Kt,Kt.Parser=te,new te}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(5354).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},9959:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,3],n=[1,5],r=[7,9,11,12,13,14,15,16,17,18,19,20,22,29,34],i=[1,15],a=[1,16],o=[1,17],s=[1,18],c=[1,19],u=[1,20],l=[1,21],h=[1,22],f=[1,23],d=[1,25],p=[1,27],y=[1,30],g=[5,7,9,11,12,13,14,15,16,17,18,19,20,22,29,34],m={trace:function(){},yy:{},symbols_:{error:2,start:3,directive:4,gantt:5,document:6,EOF:7,line:8,SPACE:9,statement:10,NL:11,dateFormat:12,inclusiveEndDates:13,topAxis:14,axisFormat:15,excludes:16,includes:17,todayMarker:18,title:19,section:20,clickStatement:21,taskTxt:22,taskData:23,openDirective:24,typeDirective:25,closeDirective:26,":":27,argDirective:28,click:29,callbackname:30,callbackargs:31,href:32,clickStatementDebug:33,open_directive:34,type_directive:35,arg_directive:36,close_directive:37,$accept:0,$end:1},terminals_:{2:"error",5:"gantt",7:"EOF",9:"SPACE",11:"NL",12:"dateFormat",13:"inclusiveEndDates",14:"topAxis",15:"axisFormat",16:"excludes",17:"includes",18:"todayMarker",19:"title",20:"section",22:"taskTxt",23:"taskData",27:":",29:"click",30:"callbackname",31:"callbackargs",32:"href",34:"open_directive",35:"type_directive",36:"arg_directive",37:"close_directive"},productions_:[0,[3,2],[3,3],[6,0],[6,2],[8,2],[8,1],[8,1],[8,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,2],[10,1],[4,4],[4,6],[21,2],[21,3],[21,3],[21,4],[21,3],[21,4],[21,2],[33,2],[33,3],[33,3],[33,4],[33,3],[33,4],[33,2],[24,1],[25,1],[28,1],[26,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 2:return a[s-1];case 3:case 7:case 8:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:this.$=a[s];break;case 9:r.setDateFormat(a[s].substr(11)),this.$=a[s].substr(11);break;case 10:r.enableInclusiveEndDates(),this.$=a[s].substr(18);break;case 11:r.TopAxis(),this.$=a[s].substr(8);break;case 12:r.setAxisFormat(a[s].substr(11)),this.$=a[s].substr(11);break;case 13:r.setExcludes(a[s].substr(9)),this.$=a[s].substr(9);break;case 14:r.setIncludes(a[s].substr(9)),this.$=a[s].substr(9);break;case 15:r.setTodayMarker(a[s].substr(12)),this.$=a[s].substr(12);break;case 16:r.setTitle(a[s].substr(6)),this.$=a[s].substr(6);break;case 17:r.addSection(a[s].substr(8)),this.$=a[s].substr(8);break;case 19:r.addTask(a[s-1],a[s]),this.$="task";break;case 23:this.$=a[s-1],r.setClickEvent(a[s-1],a[s],null);break;case 24:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 25:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],null),r.setLink(a[s-2],a[s]);break;case 26:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-2],a[s-1]),r.setLink(a[s-3],a[s]);break;case 27:this.$=a[s-2],r.setClickEvent(a[s-2],a[s],null),r.setLink(a[s-2],a[s-1]);break;case 28:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-1],a[s]),r.setLink(a[s-3],a[s-2]);break;case 29:this.$=a[s-1],r.setLink(a[s-1],a[s]);break;case 30:case 36:this.$=a[s-1]+" "+a[s];break;case 31:case 32:case 34:this.$=a[s-2]+" "+a[s-1]+" "+a[s];break;case 33:case 35:this.$=a[s-3]+" "+a[s-2]+" "+a[s-1]+" "+a[s];break;case 37:r.parseDirective("%%{","open_directive");break;case 38:r.parseDirective(a[s],"type_directive");break;case 39:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 40:r.parseDirective("}%%","close_directive","gantt")}},table:[{3:1,4:2,5:e,24:4,34:n},{1:[3]},{3:6,4:2,5:e,24:4,34:n},t(r,[2,3],{6:7}),{25:8,35:[1,9]},{35:[2,37]},{1:[2,1]},{4:26,7:[1,10],8:11,9:[1,12],10:13,11:[1,14],12:i,13:a,14:o,15:s,16:c,17:u,18:l,19:h,20:f,21:24,22:d,24:4,29:p,34:n},{26:28,27:[1,29],37:y},t([27,37],[2,38]),t(r,[2,8],{1:[2,2]}),t(r,[2,4]),{4:26,10:31,12:i,13:a,14:o,15:s,16:c,17:u,18:l,19:h,20:f,21:24,22:d,24:4,29:p,34:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,9]),t(r,[2,10]),t(r,[2,11]),t(r,[2,12]),t(r,[2,13]),t(r,[2,14]),t(r,[2,15]),t(r,[2,16]),t(r,[2,17]),t(r,[2,18]),{23:[1,32]},t(r,[2,20]),{30:[1,33],32:[1,34]},{11:[1,35]},{28:36,36:[1,37]},{11:[2,40]},t(r,[2,5]),t(r,[2,19]),t(r,[2,23],{31:[1,38],32:[1,39]}),t(r,[2,29],{30:[1,40]}),t(g,[2,21]),{26:41,37:y},{37:[2,39]},t(r,[2,24],{32:[1,42]}),t(r,[2,25]),t(r,[2,27],{31:[1,43]}),{11:[1,44]},t(r,[2,26]),t(r,[2,28]),t(g,[2,22])],defaultActions:{5:[2,37],6:[2,1],30:[2,40],37:[2,39]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},v={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),34;case 1:return this.begin("type_directive"),35;case 2:return this.popState(),this.begin("arg_directive"),27;case 3:return this.popState(),this.popState(),37;case 4:return 36;case 5:case 6:case 7:case 9:case 10:case 11:break;case 8:return 11;case 12:this.begin("href");break;case 13:case 16:case 19:case 22:this.popState();break;case 14:return 32;case 15:this.begin("callbackname");break;case 17:this.popState(),this.begin("callbackargs");break;case 18:return 30;case 20:return 31;case 21:this.begin("click");break;case 23:return 29;case 24:return 5;case 25:return 12;case 26:return 13;case 27:return 14;case 28:return 15;case 29:return 17;case 30:return 16;case 31:return 18;case 32:return"date";case 33:return 19;case 34:return 20;case 35:return 22;case 36:return 23;case 37:return 27;case 38:return 7;case 39:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)*[^\n]*)/i,/^(?:[^\}]%%*[^\n]*)/i,/^(?:%%*[^\n]*[\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:href[\s]+["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:call[\s]+)/i,/^(?:\([\s]*\))/i,/^(?:\()/i,/^(?:[^(]*)/i,/^(?:\))/i,/^(?:[^)]*)/i,/^(?:click[\s]+)/i,/^(?:[\s\n])/i,/^(?:[^\s\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:inclusiveEndDates\b)/i,/^(?:topAxis\b)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:includes\s[^#\n;]+)/i,/^(?:excludes\s[^#\n;]+)/i,/^(?:todayMarker\s[^\n;]+)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},callbackargs:{rules:[19,20],inclusive:!1},callbackname:{rules:[16,17,18],inclusive:!1},href:{rules:[13,14],inclusive:!1},click:{rules:[22,23],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,15,21,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39],inclusive:!0}}};function b(){this.yy={}}return m.lexer=v,b.prototype=m,m.Parser=b,new b}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(6878).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},2553:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[2,3],n=[1,7],r=[7,12,15,17,19,20,21],i=[7,11,12,15,17,19,20,21],a=[2,20],o=[1,32],s={trace:function(){},yy:{},symbols_:{error:2,start:3,GG:4,":":5,document:6,EOF:7,DIR:8,options:9,body:10,OPT:11,NL:12,line:13,statement:14,COMMIT:15,commit_arg:16,BRANCH:17,ID:18,CHECKOUT:19,MERGE:20,RESET:21,reset_arg:22,STR:23,HEAD:24,reset_parents:25,CARET:26,$accept:0,$end:1},terminals_:{2:"error",4:"GG",5:":",7:"EOF",8:"DIR",11:"OPT",12:"NL",15:"COMMIT",17:"BRANCH",18:"ID",19:"CHECKOUT",20:"MERGE",21:"RESET",23:"STR",24:"HEAD",26:"CARET"},productions_:[0,[3,4],[3,5],[6,0],[6,2],[9,2],[9,1],[10,0],[10,2],[13,2],[13,1],[14,2],[14,2],[14,2],[14,2],[14,2],[16,0],[16,1],[22,2],[22,2],[25,0],[25,2]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:return a[s-1];case 2:return r.setDirection(a[s-3]),a[s-1];case 4:r.setOptions(a[s-1]),this.$=a[s];break;case 5:a[s-1]+=a[s],this.$=a[s-1];break;case 7:this.$=[];break;case 8:a[s-1].push(a[s]),this.$=a[s-1];break;case 9:this.$=a[s-1];break;case 11:r.commit(a[s]);break;case 12:r.branch(a[s]);break;case 13:r.checkout(a[s]);break;case 14:r.merge(a[s]);break;case 15:r.reset(a[s]);break;case 16:this.$="";break;case 17:this.$=a[s];break;case 18:this.$=a[s-1]+":"+a[s];break;case 19:this.$=a[s-1]+":"+r.count,r.count=0;break;case 20:r.count=0;break;case 21:r.count+=1}},table:[{3:1,4:[1,2]},{1:[3]},{5:[1,3],8:[1,4]},{6:5,7:e,9:6,12:n},{5:[1,8]},{7:[1,9]},t(r,[2,7],{10:10,11:[1,11]}),t(i,[2,6]),{6:12,7:e,9:6,12:n},{1:[2,1]},{7:[2,4],12:[1,15],13:13,14:14,15:[1,16],17:[1,17],19:[1,18],20:[1,19],21:[1,20]},t(i,[2,5]),{7:[1,21]},t(r,[2,8]),{12:[1,22]},t(r,[2,10]),{12:[2,16],16:23,23:[1,24]},{18:[1,25]},{18:[1,26]},{18:[1,27]},{18:[1,30],22:28,24:[1,29]},{1:[2,2]},t(r,[2,9]),{12:[2,11]},{12:[2,17]},{12:[2,12]},{12:[2,13]},{12:[2,14]},{12:[2,15]},{12:a,25:31,26:o},{12:a,25:33,26:o},{12:[2,18]},{12:a,25:34,26:o},{12:[2,19]},{12:[2,21]}],defaultActions:{9:[2,1],21:[2,2],23:[2,11],24:[2,17],25:[2,12],26:[2,13],27:[2,14],28:[2,15],31:[2,18],33:[2,19],34:[2,21]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},c={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return 12;case 1:case 2:case 3:break;case 4:return 4;case 5:return 15;case 6:return 17;case 7:return 20;case 8:return 21;case 9:return 19;case 10:case 11:return 8;case 12:return 5;case 13:return 26;case 14:this.begin("options");break;case 15:case 18:this.popState();break;case 16:return 11;case 17:this.begin("string");break;case 19:return 23;case 20:return 18;case 21:return 7}},rules:[/^(?:(\r?\n)+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:gitGraph\b)/i,/^(?:commit\b)/i,/^(?:branch\b)/i,/^(?:merge\b)/i,/^(?:reset\b)/i,/^(?:checkout\b)/i,/^(?:LR\b)/i,/^(?:BT\b)/i,/^(?::)/i,/^(?:\^)/i,/^(?:options\r?\n)/i,/^(?:end\r?\n)/i,/^(?:[^\n]+\r?\n)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[a-zA-Z][-_\.a-zA-Z0-9]*[-_a-zA-Z0-9])/i,/^(?:$)/i],conditions:{options:{rules:[15,16],inclusive:!1},string:{rules:[18,19],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,20,21],inclusive:!0}}};function u(){this.yy={}}return s.lexer=c,u.prototype=s,s.Parser=u,new u}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(8183).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},6765:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[6,9,10],n={trace:function(){},yy:{},symbols_:{error:2,start:3,info:4,document:5,EOF:6,line:7,statement:8,NL:9,showInfo:10,$accept:0,$end:1},terminals_:{2:"error",4:"info",6:"EOF",9:"NL",10:"showInfo"},productions_:[0,[3,3],[5,0],[5,2],[7,1],[7,1],[8,1]],performAction:function(t,e,n,r,i,a,o){switch(a.length,i){case 1:return r;case 4:break;case 6:r.setInfo(!0)}},table:[{3:1,4:[1,2]},{1:[3]},t(e,[2,2],{5:3}),{6:[1,4],7:5,8:6,9:[1,7],10:[1,8]},{1:[2,1]},t(e,[2,3]),t(e,[2,4]),t(e,[2,5]),t(e,[2,6])],defaultActions:{4:[2,1]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},r={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return 4;case 1:return 9;case 2:return"space";case 3:return 10;case 4:return 6;case 5:return"TXT"}},rules:[/^(?:info\b)/i,/^(?:[\s\n\r]+)/i,/^(?:[\s]+)/i,/^(?:showInfo\b)/i,/^(?:$)/i,/^(?:.)/i],conditions:{INITIAL:{rules:[0,1,2,3,4,5],inclusive:!0}}};function i(){this.yy={}}return n.lexer=r,i.prototype=n,n.Parser=i,new i}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(1428).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},7062:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,4],n=[1,5],r=[1,6],i=[1,7],a=[1,9],o=[1,11,13,20,21,22,23],s=[2,5],c=[1,6,11,13,20,21,22,23],u=[20,21,22],l=[2,8],h=[1,18],f=[1,19],d=[1,24],p=[6,20,21,22,23],y={trace:function(){},yy:{},symbols_:{error:2,start:3,eol:4,directive:5,PIE:6,document:7,showData:8,line:9,statement:10,txt:11,value:12,title:13,title_value:14,openDirective:15,typeDirective:16,closeDirective:17,":":18,argDirective:19,NEWLINE:20,";":21,EOF:22,open_directive:23,type_directive:24,arg_directive:25,close_directive:26,$accept:0,$end:1},terminals_:{2:"error",6:"PIE",8:"showData",11:"txt",12:"value",13:"title",14:"title_value",18:":",20:"NEWLINE",21:";",22:"EOF",23:"open_directive",24:"type_directive",25:"arg_directive",26:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[3,3],[7,0],[7,2],[9,2],[10,0],[10,2],[10,2],[10,1],[5,3],[5,5],[4,1],[4,1],[4,1],[15,1],[16,1],[19,1],[17,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:r.setShowData(!0);break;case 7:this.$=a[s-1];break;case 9:r.addSection(a[s-1],r.cleanupValue(a[s]));break;case 10:this.$=a[s].trim(),r.setTitle(this.$);break;case 17:r.parseDirective("%%{","open_directive");break;case 18:r.parseDirective(a[s],"type_directive");break;case 19:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 20:r.parseDirective("}%%","close_directive","pie")}},table:[{3:1,4:2,5:3,6:e,15:8,20:n,21:r,22:i,23:a},{1:[3]},{3:10,4:2,5:3,6:e,15:8,20:n,21:r,22:i,23:a},{3:11,4:2,5:3,6:e,15:8,20:n,21:r,22:i,23:a},t(o,s,{7:12,8:[1,13]}),t(c,[2,14]),t(c,[2,15]),t(c,[2,16]),{16:14,24:[1,15]},{24:[2,17]},{1:[2,1]},{1:[2,2]},t(u,l,{15:8,9:16,10:17,5:20,1:[2,3],11:h,13:f,23:a}),t(o,s,{7:21}),{17:22,18:[1,23],26:d},t([18,26],[2,18]),t(o,[2,6]),{4:25,20:n,21:r,22:i},{12:[1,26]},{14:[1,27]},t(u,[2,11]),t(u,l,{15:8,9:16,10:17,5:20,1:[2,4],11:h,13:f,23:a}),t(p,[2,12]),{19:28,25:[1,29]},t(p,[2,20]),t(o,[2,7]),t(u,[2,9]),t(u,[2,10]),{17:30,26:d},{26:[2,19]},t(p,[2,13])],defaultActions:{9:[2,17],10:[2,1],11:[2,2],29:[2,19]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},g={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),23;case 1:return this.begin("type_directive"),24;case 2:return this.popState(),this.begin("arg_directive"),18;case 3:return this.popState(),this.popState(),26;case 4:return 25;case 5:case 6:case 8:case 9:break;case 7:return 20;case 10:return this.begin("title"),13;case 11:return this.popState(),"title_value";case 12:this.begin("string");break;case 13:this.popState();break;case 14:return"txt";case 15:return 6;case 16:return 8;case 17:return"value";case 18:return 22}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:[\s]+)/i,/^(?:title\b)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:pie\b)/i,/^(?:showData\b)/i,/^(?::[\s]*[\d]+(?:\.[\d]+)?)/i,/^(?:$)/i],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},title:{rules:[11],inclusive:!1},string:{rules:[13,14],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,12,15,16,17,18],inclusive:!0}}};function m(){this.yy={}}return y.lexer=g,m.prototype=y,y.Parser=m,new m}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(4551).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},3176:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,3],n=[1,5],r=[1,17],i=[2,10],a=[1,21],o=[1,22],s=[1,23],c=[1,24],u=[1,25],l=[1,26],h=[1,19],f=[1,27],d=[1,28],p=[1,31],y=[66,67],g=[5,8,14,35,36,37,38,39,40,48,55,57,66,67],m=[5,6,8,14,35,36,37,38,39,40,48,66,67],v=[1,51],b=[1,52],_=[1,53],x=[1,54],w=[1,55],k=[1,56],T=[1,57],E=[57,58],C=[1,69],S=[1,65],A=[1,66],M=[1,67],N=[1,68],D=[1,70],O=[1,74],B=[1,75],L=[1,72],I=[1,73],R=[5,8,14,35,36,37,38,39,40,48,66,67],F={trace:function(){},yy:{},symbols_:{error:2,start:3,directive:4,NEWLINE:5,RD:6,diagram:7,EOF:8,openDirective:9,typeDirective:10,closeDirective:11,":":12,argDirective:13,open_directive:14,type_directive:15,arg_directive:16,close_directive:17,requirementDef:18,elementDef:19,relationshipDef:20,requirementType:21,requirementName:22,STRUCT_START:23,requirementBody:24,ID:25,COLONSEP:26,id:27,TEXT:28,text:29,RISK:30,riskLevel:31,VERIFYMTHD:32,verifyType:33,STRUCT_STOP:34,REQUIREMENT:35,FUNCTIONAL_REQUIREMENT:36,INTERFACE_REQUIREMENT:37,PERFORMANCE_REQUIREMENT:38,PHYSICAL_REQUIREMENT:39,DESIGN_CONSTRAINT:40,LOW_RISK:41,MED_RISK:42,HIGH_RISK:43,VERIFY_ANALYSIS:44,VERIFY_DEMONSTRATION:45,VERIFY_INSPECTION:46,VERIFY_TEST:47,ELEMENT:48,elementName:49,elementBody:50,TYPE:51,type:52,DOCREF:53,ref:54,END_ARROW_L:55,relationship:56,LINE:57,END_ARROW_R:58,CONTAINS:59,COPIES:60,DERIVES:61,SATISFIES:62,VERIFIES:63,REFINES:64,TRACES:65,unqString:66,qString:67,$accept:0,$end:1},terminals_:{2:"error",5:"NEWLINE",6:"RD",8:"EOF",12:":",14:"open_directive",15:"type_directive",16:"arg_directive",17:"close_directive",23:"STRUCT_START",25:"ID",26:"COLONSEP",28:"TEXT",30:"RISK",32:"VERIFYMTHD",34:"STRUCT_STOP",35:"REQUIREMENT",36:"FUNCTIONAL_REQUIREMENT",37:"INTERFACE_REQUIREMENT",38:"PERFORMANCE_REQUIREMENT",39:"PHYSICAL_REQUIREMENT",40:"DESIGN_CONSTRAINT",41:"LOW_RISK",42:"MED_RISK",43:"HIGH_RISK",44:"VERIFY_ANALYSIS",45:"VERIFY_DEMONSTRATION",46:"VERIFY_INSPECTION",47:"VERIFY_TEST",48:"ELEMENT",51:"TYPE",53:"DOCREF",55:"END_ARROW_L",57:"LINE",58:"END_ARROW_R",59:"CONTAINS",60:"COPIES",61:"DERIVES",62:"SATISFIES",63:"VERIFIES",64:"REFINES",65:"TRACES",66:"unqString",67:"qString"},productions_:[0,[3,3],[3,2],[3,4],[4,3],[4,5],[9,1],[10,1],[13,1],[11,1],[7,0],[7,2],[7,2],[7,2],[7,2],[7,2],[18,5],[24,5],[24,5],[24,5],[24,5],[24,2],[24,1],[21,1],[21,1],[21,1],[21,1],[21,1],[21,1],[31,1],[31,1],[31,1],[33,1],[33,1],[33,1],[33,1],[19,5],[50,5],[50,5],[50,2],[50,1],[20,5],[20,5],[56,1],[56,1],[56,1],[56,1],[56,1],[56,1],[56,1],[22,1],[22,1],[27,1],[27,1],[29,1],[29,1],[49,1],[49,1],[52,1],[52,1],[54,1],[54,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 6:r.parseDirective("%%{","open_directive");break;case 7:r.parseDirective(a[s],"type_directive");break;case 8:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 9:r.parseDirective("}%%","close_directive","pie");break;case 10:this.$=[];break;case 16:r.addRequirement(a[s-3],a[s-4]);break;case 17:r.setNewReqId(a[s-2]);break;case 18:r.setNewReqText(a[s-2]);break;case 19:r.setNewReqRisk(a[s-2]);break;case 20:r.setNewReqVerifyMethod(a[s-2]);break;case 23:this.$=r.RequirementType.REQUIREMENT;break;case 24:this.$=r.RequirementType.FUNCTIONAL_REQUIREMENT;break;case 25:this.$=r.RequirementType.INTERFACE_REQUIREMENT;break;case 26:this.$=r.RequirementType.PERFORMANCE_REQUIREMENT;break;case 27:this.$=r.RequirementType.PHYSICAL_REQUIREMENT;break;case 28:this.$=r.RequirementType.DESIGN_CONSTRAINT;break;case 29:this.$=r.RiskLevel.LOW_RISK;break;case 30:this.$=r.RiskLevel.MED_RISK;break;case 31:this.$=r.RiskLevel.HIGH_RISK;break;case 32:this.$=r.VerifyType.VERIFY_ANALYSIS;break;case 33:this.$=r.VerifyType.VERIFY_DEMONSTRATION;break;case 34:this.$=r.VerifyType.VERIFY_INSPECTION;break;case 35:this.$=r.VerifyType.VERIFY_TEST;break;case 36:r.addElement(a[s-3]);break;case 37:r.setNewElementType(a[s-2]);break;case 38:r.setNewElementDocRef(a[s-2]);break;case 41:r.addRelationship(a[s-2],a[s],a[s-4]);break;case 42:r.addRelationship(a[s-2],a[s-4],a[s]);break;case 43:this.$=r.Relationships.CONTAINS;break;case 44:this.$=r.Relationships.COPIES;break;case 45:this.$=r.Relationships.DERIVES;break;case 46:this.$=r.Relationships.SATISFIES;break;case 47:this.$=r.Relationships.VERIFIES;break;case 48:this.$=r.Relationships.REFINES;break;case 49:this.$=r.Relationships.TRACES}},table:[{3:1,4:2,6:e,9:4,14:n},{1:[3]},{3:7,4:2,5:[1,6],6:e,9:4,14:n},{5:[1,8]},{10:9,15:[1,10]},{15:[2,6]},{3:11,4:2,6:e,9:4,14:n},{1:[2,2]},{4:16,5:r,7:12,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{11:29,12:[1,30],17:p},t([12,17],[2,7]),{1:[2,1]},{8:[1,32]},{4:16,5:r,7:33,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{4:16,5:r,7:34,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{4:16,5:r,7:35,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{4:16,5:r,7:36,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{4:16,5:r,7:37,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{22:38,66:[1,39],67:[1,40]},{49:41,66:[1,42],67:[1,43]},{55:[1,44],57:[1,45]},t(y,[2,23]),t(y,[2,24]),t(y,[2,25]),t(y,[2,26]),t(y,[2,27]),t(y,[2,28]),t(g,[2,52]),t(g,[2,53]),t(m,[2,4]),{13:46,16:[1,47]},t(m,[2,9]),{1:[2,3]},{8:[2,11]},{8:[2,12]},{8:[2,13]},{8:[2,14]},{8:[2,15]},{23:[1,48]},{23:[2,50]},{23:[2,51]},{23:[1,49]},{23:[2,56]},{23:[2,57]},{56:50,59:v,60:b,61:_,62:x,63:w,64:k,65:T},{56:58,59:v,60:b,61:_,62:x,63:w,64:k,65:T},{11:59,17:p},{17:[2,8]},{5:[1,60]},{5:[1,61]},{57:[1,62]},t(E,[2,43]),t(E,[2,44]),t(E,[2,45]),t(E,[2,46]),t(E,[2,47]),t(E,[2,48]),t(E,[2,49]),{58:[1,63]},t(m,[2,5]),{5:C,24:64,25:S,28:A,30:M,32:N,34:D},{5:O,34:B,50:71,51:L,53:I},{27:76,66:f,67:d},{27:77,66:f,67:d},t(R,[2,16]),{26:[1,78]},{26:[1,79]},{26:[1,80]},{26:[1,81]},{5:C,24:82,25:S,28:A,30:M,32:N,34:D},t(R,[2,22]),t(R,[2,36]),{26:[1,83]},{26:[1,84]},{5:O,34:B,50:85,51:L,53:I},t(R,[2,40]),t(R,[2,41]),t(R,[2,42]),{27:86,66:f,67:d},{29:87,66:[1,88],67:[1,89]},{31:90,41:[1,91],42:[1,92],43:[1,93]},{33:94,44:[1,95],45:[1,96],46:[1,97],47:[1,98]},t(R,[2,21]),{52:99,66:[1,100],67:[1,101]},{54:102,66:[1,103],67:[1,104]},t(R,[2,39]),{5:[1,105]},{5:[1,106]},{5:[2,54]},{5:[2,55]},{5:[1,107]},{5:[2,29]},{5:[2,30]},{5:[2,31]},{5:[1,108]},{5:[2,32]},{5:[2,33]},{5:[2,34]},{5:[2,35]},{5:[1,109]},{5:[2,58]},{5:[2,59]},{5:[1,110]},{5:[2,60]},{5:[2,61]},{5:C,24:111,25:S,28:A,30:M,32:N,34:D},{5:C,24:112,25:S,28:A,30:M,32:N,34:D},{5:C,24:113,25:S,28:A,30:M,32:N,34:D},{5:C,24:114,25:S,28:A,30:M,32:N,34:D},{5:O,34:B,50:115,51:L,53:I},{5:O,34:B,50:116,51:L,53:I},t(R,[2,17]),t(R,[2,18]),t(R,[2,19]),t(R,[2,20]),t(R,[2,37]),t(R,[2,38])],defaultActions:{5:[2,6],7:[2,2],11:[2,1],32:[2,3],33:[2,11],34:[2,12],35:[2,13],36:[2,14],37:[2,15],39:[2,50],40:[2,51],42:[2,56],43:[2,57],47:[2,8],88:[2,54],89:[2,55],91:[2,29],92:[2,30],93:[2,31],95:[2,32],96:[2,33],97:[2,34],98:[2,35],100:[2,58],101:[2,59],103:[2,60],104:[2,61]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},P={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),14;case 1:return this.begin("type_directive"),15;case 2:return this.popState(),this.begin("arg_directive"),12;case 3:return this.popState(),this.popState(),17;case 4:return 16;case 5:return 5;case 6:case 7:case 8:break;case 9:return 8;case 10:return 6;case 11:return 23;case 12:return 34;case 13:return 26;case 14:return 25;case 15:return 28;case 16:return 30;case 17:return 32;case 18:return 35;case 19:return 36;case 20:return 37;case 21:return 38;case 22:return 39;case 23:return 40;case 24:return 41;case 25:return 42;case 26:return 43;case 27:return 44;case 28:return 45;case 29:return 46;case 30:return 47;case 31:return 48;case 32:return 59;case 33:return 60;case 34:return 61;case 35:return 62;case 36:return 63;case 37:return 64;case 38:return 65;case 39:return 51;case 40:return 53;case 41:return 55;case 42:return 58;case 43:return 57;case 44:this.begin("string");break;case 45:this.popState();break;case 46:return"qString";case 47:return e.yytext=e.yytext.trim(),66}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:(\r?\n)+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:$)/i,/^(?:requirementDiagram\b)/i,/^(?:\{)/i,/^(?:\})/i,/^(?::)/i,/^(?:id\b)/i,/^(?:text\b)/i,/^(?:risk\b)/i,/^(?:verifyMethod\b)/i,/^(?:requirement\b)/i,/^(?:functionalRequirement\b)/i,/^(?:interfaceRequirement\b)/i,/^(?:performanceRequirement\b)/i,/^(?:physicalRequirement\b)/i,/^(?:designConstraint\b)/i,/^(?:low\b)/i,/^(?:medium\b)/i,/^(?:high\b)/i,/^(?:analysis\b)/i,/^(?:demonstration\b)/i,/^(?:inspection\b)/i,/^(?:test\b)/i,/^(?:element\b)/i,/^(?:contains\b)/i,/^(?:copies\b)/i,/^(?:derives\b)/i,/^(?:satisfies\b)/i,/^(?:verifies\b)/i,/^(?:refines\b)/i,/^(?:traces\b)/i,/^(?:type\b)/i,/^(?:docref\b)/i,/^(?:<-)/i,/^(?:->)/i,/^(?:-)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[\w][^\r\n\{\<\>\-\=]*)/i],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},unqString:{rules:[],inclusive:!1},token:{rules:[],inclusive:!1},string:{rules:[45,46],inclusive:!1},INITIAL:{rules:[0,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,33,34,35,36,37,38,39,40,41,42,43,44,47],inclusive:!0}}};function j(){this.yy={}}return F.lexer=P,j.prototype=F,F.Parser=j,new j}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(8800).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},6876:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,3],r=[1,5],i=[1,7],a=[2,5],o=[1,15],s=[1,17],c=[1,18],u=[1,19],l=[1,21],h=[1,22],f=[1,23],d=[1,29],p=[1,30],y=[1,31],g=[1,32],m=[1,33],v=[1,34],b=[1,37],_=[1,38],x=[1,39],w=[1,40],k=[1,41],T=[1,42],E=[1,45],C=[1,4,5,16,20,22,23,24,30,32,33,34,35,36,38,40,41,42,46,47,48,49,57,67],S=[1,58],A=[4,5,16,20,22,23,24,30,32,33,34,35,36,38,42,46,47,48,49,57,67],M=[4,5,16,20,22,23,24,30,32,33,34,35,36,38,41,42,46,47,48,49,57,67],N=[4,5,16,20,22,23,24,30,32,33,34,35,36,38,40,42,46,47,48,49,57,67],D=[55,56,57],O=[1,4,5,7,16,20,22,23,24,30,32,33,34,35,36,38,40,41,42,46,47,48,49,57,67],B={trace:function(){},yy:{},symbols_:{error:2,start:3,SPACE:4,NEWLINE:5,directive:6,SD:7,document:8,line:9,statement:10,openDirective:11,typeDirective:12,closeDirective:13,":":14,argDirective:15,participant:16,actor:17,AS:18,restOfLine:19,participant_actor:20,signal:21,autonumber:22,activate:23,deactivate:24,note_statement:25,links_statement:26,link_statement:27,properties_statement:28,details_statement:29,title:30,text2:31,loop:32,end:33,rect:34,opt:35,alt:36,else_sections:37,par:38,par_sections:39,and:40,else:41,note:42,placement:43,over:44,actor_pair:45,links:46,link:47,properties:48,details:49,spaceList:50,",":51,left_of:52,right_of:53,signaltype:54,"+":55,"-":56,ACTOR:57,SOLID_OPEN_ARROW:58,DOTTED_OPEN_ARROW:59,SOLID_ARROW:60,DOTTED_ARROW:61,SOLID_CROSS:62,DOTTED_CROSS:63,SOLID_POINT:64,DOTTED_POINT:65,TXT:66,open_directive:67,type_directive:68,arg_directive:69,close_directive:70,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NEWLINE",7:"SD",14:":",16:"participant",18:"AS",19:"restOfLine",20:"participant_actor",22:"autonumber",23:"activate",24:"deactivate",30:"title",32:"loop",33:"end",34:"rect",35:"opt",36:"alt",38:"par",40:"and",41:"else",42:"note",44:"over",46:"links",47:"link",48:"properties",49:"details",51:",",52:"left_of",53:"right_of",55:"+",56:"-",57:"ACTOR",58:"SOLID_OPEN_ARROW",59:"DOTTED_OPEN_ARROW",60:"SOLID_ARROW",61:"DOTTED_ARROW",62:"SOLID_CROSS",63:"DOTTED_CROSS",64:"SOLID_POINT",65:"DOTTED_POINT",66:"TXT",67:"open_directive",68:"type_directive",69:"arg_directive",70:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[3,2],[8,0],[8,2],[9,2],[9,1],[9,1],[6,4],[6,6],[10,5],[10,3],[10,5],[10,3],[10,2],[10,1],[10,3],[10,3],[10,2],[10,2],[10,2],[10,2],[10,2],[10,3],[10,4],[10,4],[10,4],[10,4],[10,4],[10,1],[39,1],[39,4],[37,1],[37,4],[25,4],[25,4],[26,3],[27,3],[28,3],[29,3],[50,2],[50,1],[45,3],[45,1],[43,1],[43,1],[21,5],[21,5],[21,4],[17,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[31,1],[11,1],[12,1],[15,1],[13,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:return r.apply(a[s]),a[s];case 5:case 9:this.$=[];break;case 6:a[s-1].push(a[s]),this.$=a[s-1];break;case 7:case 8:case 45:this.$=a[s];break;case 12:a[s-3].type="addParticipant",a[s-3].description=r.parseMessage(a[s-1]),this.$=a[s-3];break;case 13:a[s-1].type="addParticipant",this.$=a[s-1];break;case 14:a[s-3].type="addActor",a[s-3].description=r.parseMessage(a[s-1]),this.$=a[s-3];break;case 15:a[s-1].type="addActor",this.$=a[s-1];break;case 17:r.enableSequenceNumbers();break;case 18:this.$={type:"activeStart",signalType:r.LINETYPE.ACTIVE_START,actor:a[s-1]};break;case 19:this.$={type:"activeEnd",signalType:r.LINETYPE.ACTIVE_END,actor:a[s-1]};break;case 25:this.$=[{type:"setTitle",text:a[s-1]}];break;case 26:a[s-1].unshift({type:"loopStart",loopText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.LOOP_START}),a[s-1].push({type:"loopEnd",loopText:a[s-2],signalType:r.LINETYPE.LOOP_END}),this.$=a[s-1];break;case 27:a[s-1].unshift({type:"rectStart",color:r.parseMessage(a[s-2]),signalType:r.LINETYPE.RECT_START}),a[s-1].push({type:"rectEnd",color:r.parseMessage(a[s-2]),signalType:r.LINETYPE.RECT_END}),this.$=a[s-1];break;case 28:a[s-1].unshift({type:"optStart",optText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.OPT_START}),a[s-1].push({type:"optEnd",optText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.OPT_END}),this.$=a[s-1];break;case 29:a[s-1].unshift({type:"altStart",altText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.ALT_START}),a[s-1].push({type:"altEnd",signalType:r.LINETYPE.ALT_END}),this.$=a[s-1];break;case 30:a[s-1].unshift({type:"parStart",parText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.PAR_START}),a[s-1].push({type:"parEnd",signalType:r.LINETYPE.PAR_END}),this.$=a[s-1];break;case 33:this.$=a[s-3].concat([{type:"and",parText:r.parseMessage(a[s-1]),signalType:r.LINETYPE.PAR_AND},a[s]]);break;case 35:this.$=a[s-3].concat([{type:"else",altText:r.parseMessage(a[s-1]),signalType:r.LINETYPE.ALT_ELSE},a[s]]);break;case 36:this.$=[a[s-1],{type:"addNote",placement:a[s-2],actor:a[s-1].actor,text:a[s]}];break;case 37:a[s-2]=[].concat(a[s-1],a[s-1]).slice(0,2),a[s-2][0]=a[s-2][0].actor,a[s-2][1]=a[s-2][1].actor,this.$=[a[s-1],{type:"addNote",placement:r.PLACEMENT.OVER,actor:a[s-2].slice(0,2),text:a[s]}];break;case 38:this.$=[a[s-1],{type:"addLinks",actor:a[s-1].actor,text:a[s]}];break;case 39:this.$=[a[s-1],{type:"addALink",actor:a[s-1].actor,text:a[s]}];break;case 40:this.$=[a[s-1],{type:"addProperties",actor:a[s-1].actor,text:a[s]}];break;case 41:this.$=[a[s-1],{type:"addDetails",actor:a[s-1].actor,text:a[s]}];break;case 44:this.$=[a[s-2],a[s]];break;case 46:this.$=r.PLACEMENT.LEFTOF;break;case 47:this.$=r.PLACEMENT.RIGHTOF;break;case 48:this.$=[a[s-4],a[s-1],{type:"addMessage",from:a[s-4].actor,to:a[s-1].actor,signalType:a[s-3],msg:a[s]},{type:"activeStart",signalType:r.LINETYPE.ACTIVE_START,actor:a[s-1]}];break;case 49:this.$=[a[s-4],a[s-1],{type:"addMessage",from:a[s-4].actor,to:a[s-1].actor,signalType:a[s-3],msg:a[s]},{type:"activeEnd",signalType:r.LINETYPE.ACTIVE_END,actor:a[s-4]}];break;case 50:this.$=[a[s-3],a[s-1],{type:"addMessage",from:a[s-3].actor,to:a[s-1].actor,signalType:a[s-2],msg:a[s]}];break;case 51:this.$={type:"addParticipant",actor:a[s]};break;case 52:this.$=r.LINETYPE.SOLID_OPEN;break;case 53:this.$=r.LINETYPE.DOTTED_OPEN;break;case 54:this.$=r.LINETYPE.SOLID;break;case 55:this.$=r.LINETYPE.DOTTED;break;case 56:this.$=r.LINETYPE.SOLID_CROSS;break;case 57:this.$=r.LINETYPE.DOTTED_CROSS;break;case 58:this.$=r.LINETYPE.SOLID_POINT;break;case 59:this.$=r.LINETYPE.DOTTED_POINT;break;case 60:this.$=r.parseMessage(a[s].trim().substring(1));break;case 61:r.parseDirective("%%{","open_directive");break;case 62:r.parseDirective(a[s],"type_directive");break;case 63:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 64:r.parseDirective("}%%","close_directive","sequence")}},table:[{3:1,4:e,5:n,6:4,7:r,11:6,67:i},{1:[3]},{3:8,4:e,5:n,6:4,7:r,11:6,67:i},{3:9,4:e,5:n,6:4,7:r,11:6,67:i},{3:10,4:e,5:n,6:4,7:r,11:6,67:i},t([1,4,5,16,20,22,23,24,30,32,34,35,36,38,42,46,47,48,49,57,67],a,{8:11}),{12:12,68:[1,13]},{68:[2,61]},{1:[2,1]},{1:[2,2]},{1:[2,3]},{1:[2,4],4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,34:y,35:g,36:m,38:v,42:b,46:_,47:x,48:w,49:k,57:T,67:i},{13:43,14:[1,44],70:E},t([14,70],[2,62]),t(C,[2,6]),{6:35,10:46,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,34:y,35:g,36:m,38:v,42:b,46:_,47:x,48:w,49:k,57:T,67:i},t(C,[2,8]),t(C,[2,9]),{17:47,57:T},{17:48,57:T},{5:[1,49]},t(C,[2,17]),{17:50,57:T},{17:51,57:T},{5:[1,52]},{5:[1,53]},{5:[1,54]},{5:[1,55]},{5:[1,56]},{31:57,66:S},{19:[1,59]},{19:[1,60]},{19:[1,61]},{19:[1,62]},{19:[1,63]},t(C,[2,31]),{54:64,58:[1,65],59:[1,66],60:[1,67],61:[1,68],62:[1,69],63:[1,70],64:[1,71],65:[1,72]},{43:73,44:[1,74],52:[1,75],53:[1,76]},{17:77,57:T},{17:78,57:T},{17:79,57:T},{17:80,57:T},t([5,18,51,58,59,60,61,62,63,64,65,66],[2,51]),{5:[1,81]},{15:82,69:[1,83]},{5:[2,64]},t(C,[2,7]),{5:[1,85],18:[1,84]},{5:[1,87],18:[1,86]},t(C,[2,16]),{5:[1,88]},{5:[1,89]},t(C,[2,20]),t(C,[2,21]),t(C,[2,22]),t(C,[2,23]),t(C,[2,24]),{5:[1,90]},{5:[2,60]},t(A,a,{8:91}),t(A,a,{8:92}),t(A,a,{8:93}),t(M,a,{37:94,8:95}),t(N,a,{39:96,8:97}),{17:100,55:[1,98],56:[1,99],57:T},t(D,[2,52]),t(D,[2,53]),t(D,[2,54]),t(D,[2,55]),t(D,[2,56]),t(D,[2,57]),t(D,[2,58]),t(D,[2,59]),{17:101,57:T},{17:103,45:102,57:T},{57:[2,46]},{57:[2,47]},{31:104,66:S},{31:105,66:S},{31:106,66:S},{31:107,66:S},t(O,[2,10]),{13:108,70:E},{70:[2,63]},{19:[1,109]},t(C,[2,13]),{19:[1,110]},t(C,[2,15]),t(C,[2,18]),t(C,[2,19]),t(C,[2,25]),{4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,33:[1,111],34:y,35:g,36:m,38:v,42:b,46:_,47:x,48:w,49:k,57:T,67:i},{4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,33:[1,112],34:y,35:g,36:m,38:v,42:b,46:_,47:x,48:w,49:k,57:T,67:i},{4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,33:[1,113],34:y,35:g,36:m,38:v,42:b,46:_,47:x,48:w,49:k,57:T,67:i},{33:[1,114]},{4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,33:[2,34],34:y,35:g,36:m,38:v,41:[1,115],42:b,46:_,47:x,48:w,49:k,57:T,67:i},{33:[1,116]},{4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,33:[2,32],34:y,35:g,36:m,38:v,40:[1,117],42:b,46:_,47:x,48:w,49:k,57:T,67:i},{17:118,57:T},{17:119,57:T},{31:120,66:S},{31:121,66:S},{31:122,66:S},{51:[1,123],66:[2,45]},{5:[2,38]},{5:[2,39]},{5:[2,40]},{5:[2,41]},{5:[1,124]},{5:[1,125]},{5:[1,126]},t(C,[2,26]),t(C,[2,27]),t(C,[2,28]),t(C,[2,29]),{19:[1,127]},t(C,[2,30]),{19:[1,128]},{31:129,66:S},{31:130,66:S},{5:[2,50]},{5:[2,36]},{5:[2,37]},{17:131,57:T},t(O,[2,11]),t(C,[2,12]),t(C,[2,14]),t(M,a,{8:95,37:132}),t(N,a,{8:97,39:133}),{5:[2,48]},{5:[2,49]},{66:[2,44]},{33:[2,35]},{33:[2,33]}],defaultActions:{7:[2,61],8:[2,1],9:[2,2],10:[2,3],45:[2,64],58:[2,60],75:[2,46],76:[2,47],83:[2,63],104:[2,38],105:[2,39],106:[2,40],107:[2,41],120:[2,50],121:[2,36],122:[2,37],129:[2,48],130:[2,49],131:[2,44],132:[2,35],133:[2,33]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},L={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),67;case 1:return this.begin("type_directive"),68;case 2:return this.popState(),this.begin("arg_directive"),14;case 3:return this.popState(),this.popState(),70;case 4:return 69;case 5:case 39:case 52:return 5;case 6:case 7:case 8:case 9:case 10:break;case 11:return this.begin("ID"),16;case 12:return this.begin("ID"),20;case 13:return e.yytext=e.yytext.trim(),this.begin("ALIAS"),57;case 14:return this.popState(),this.popState(),this.begin("LINE"),18;case 15:return this.popState(),this.popState(),5;case 16:return this.begin("LINE"),32;case 17:return this.begin("LINE"),34;case 18:return this.begin("LINE"),35;case 19:return this.begin("LINE"),36;case 20:return this.begin("LINE"),41;case 21:return this.begin("LINE"),38;case 22:return this.begin("LINE"),40;case 23:return this.popState(),19;case 24:return 33;case 25:return 52;case 26:return 53;case 27:return 46;case 28:return 47;case 29:return 48;case 30:return 49;case 31:return 44;case 32:return 42;case 33:return this.begin("ID"),23;case 34:return this.begin("ID"),24;case 35:return 30;case 36:return 7;case 37:return 22;case 38:return 51;case 40:return e.yytext=e.yytext.trim(),57;case 41:return 60;case 42:return 61;case 43:return 58;case 44:return 59;case 45:return 62;case 46:return 63;case 47:return 64;case 48:return 65;case 49:return 66;case 50:return 55;case 51:return 56;case 53:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:participant\b)/i,/^(?:actor\b)/i,/^(?:[^\->:\n,;]+?(?=((?!\n)\s)+as(?!\n)\s|[#\n;]|$))/i,/^(?:as\b)/i,/^(?:(?:))/i,/^(?:loop\b)/i,/^(?:rect\b)/i,/^(?:opt\b)/i,/^(?:alt\b)/i,/^(?:else\b)/i,/^(?:par\b)/i,/^(?:and\b)/i,/^(?:(?:[:]?(?:no)?wrap)?[^#\n;]*)/i,/^(?:end\b)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:links\b)/i,/^(?:link\b)/i,/^(?:properties\b)/i,/^(?:details\b)/i,/^(?:over\b)/i,/^(?:note\b)/i,/^(?:activate\b)/i,/^(?:deactivate\b)/i,/^(?:title\b)/i,/^(?:sequenceDiagram\b)/i,/^(?:autonumber\b)/i,/^(?:,)/i,/^(?:;)/i,/^(?:[^\+\->:\n,;]+((?!(-x|--x|-\)|--\)))[\-]*[^\+\->:\n,;]+)*)/i,/^(?:->>)/i,/^(?:-->>)/i,/^(?:->)/i,/^(?:-->)/i,/^(?:-[x])/i,/^(?:--[x])/i,/^(?:-[\)])/i,/^(?:--[\)])/i,/^(?::(?:(?:no)?wrap)?[^#\n;]+)/i,/^(?:\+)/i,/^(?:-)/i,/^(?:$)/i,/^(?:.)/i],conditions:{open_directive:{rules:[1,8],inclusive:!1},type_directive:{rules:[2,3,8],inclusive:!1},arg_directive:{rules:[3,4,8],inclusive:!1},ID:{rules:[7,8,13],inclusive:!1},ALIAS:{rules:[7,8,14,15],inclusive:!1},LINE:{rules:[7,8,23],inclusive:!1},INITIAL:{rules:[0,5,6,8,9,10,11,12,16,17,18,19,20,21,22,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53],inclusive:!0}}};function I(){this.yy={}}return B.lexer=L,I.prototype=B,B.Parser=I,new I}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(1993).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},3584:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,3],r=[1,5],i=[1,7],a=[2,5],o=[1,15],s=[1,17],c=[1,19],u=[1,20],l=[1,21],h=[1,22],f=[1,30],d=[1,23],p=[1,24],y=[1,25],g=[1,26],m=[1,27],v=[1,32],b=[1,33],_=[1,34],x=[1,35],w=[1,31],k=[1,38],T=[1,4,5,14,15,17,19,20,22,23,24,25,26,27,36,37,38,39,42,45],E=[1,4,5,12,13,14,15,17,19,20,22,23,24,25,26,27,36,37,38,39,42,45],C=[1,4,5,7,14,15,17,19,20,22,23,24,25,26,27,36,37,38,39,42,45],S=[4,5,14,15,17,19,20,22,23,24,25,26,27,36,37,38,39,42,45],A={trace:function(){},yy:{},symbols_:{error:2,start:3,SPACE:4,NL:5,directive:6,SD:7,document:8,line:9,statement:10,idStatement:11,DESCR:12,"--\x3e":13,HIDE_EMPTY:14,scale:15,WIDTH:16,COMPOSIT_STATE:17,STRUCT_START:18,STRUCT_STOP:19,STATE_DESCR:20,AS:21,ID:22,FORK:23,JOIN:24,CHOICE:25,CONCURRENT:26,note:27,notePosition:28,NOTE_TEXT:29,direction:30,openDirective:31,typeDirective:32,closeDirective:33,":":34,argDirective:35,direction_tb:36,direction_bt:37,direction_rl:38,direction_lr:39,eol:40,";":41,EDGE_STATE:42,left_of:43,right_of:44,open_directive:45,type_directive:46,arg_directive:47,close_directive:48,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NL",7:"SD",12:"DESCR",13:"--\x3e",14:"HIDE_EMPTY",15:"scale",16:"WIDTH",17:"COMPOSIT_STATE",18:"STRUCT_START",19:"STRUCT_STOP",20:"STATE_DESCR",21:"AS",22:"ID",23:"FORK",24:"JOIN",25:"CHOICE",26:"CONCURRENT",27:"note",29:"NOTE_TEXT",34:":",36:"direction_tb",37:"direction_bt",38:"direction_rl",39:"direction_lr",41:";",42:"EDGE_STATE",43:"left_of",44:"right_of",45:"open_directive",46:"type_directive",47:"arg_directive",48:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[3,2],[8,0],[8,2],[9,2],[9,1],[9,1],[10,1],[10,2],[10,3],[10,4],[10,1],[10,2],[10,1],[10,4],[10,3],[10,6],[10,1],[10,1],[10,1],[10,1],[10,4],[10,4],[10,1],[10,1],[6,3],[6,5],[30,1],[30,1],[30,1],[30,1],[40,1],[40,1],[11,1],[11,1],[28,1],[28,1],[31,1],[32,1],[35,1],[33,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:return r.setRootDoc(a[s]),a[s];case 5:this.$=[];break;case 6:"nl"!=a[s]&&(a[s-1].push(a[s]),this.$=a[s-1]);break;case 7:case 8:case 36:case 37:this.$=a[s];break;case 9:this.$="nl";break;case 10:this.$={stmt:"state",id:a[s],type:"default",description:""};break;case 11:this.$={stmt:"state",id:a[s-1],type:"default",description:r.trimColon(a[s])};break;case 12:this.$={stmt:"relation",state1:{stmt:"state",id:a[s-2],type:"default",description:""},state2:{stmt:"state",id:a[s],type:"default",description:""}};break;case 13:this.$={stmt:"relation",state1:{stmt:"state",id:a[s-3],type:"default",description:""},state2:{stmt:"state",id:a[s-1],type:"default",description:""},description:a[s].substr(1).trim()};break;case 17:this.$={stmt:"state",id:a[s-3],type:"default",description:"",doc:a[s-1]};break;case 18:var c=a[s],u=a[s-2].trim();if(a[s].match(":")){var l=a[s].split(":");c=l[0],u=[u,l[1]]}this.$={stmt:"state",id:c,type:"default",description:u};break;case 19:this.$={stmt:"state",id:a[s-3],type:"default",description:a[s-5],doc:a[s-1]};break;case 20:this.$={stmt:"state",id:a[s],type:"fork"};break;case 21:this.$={stmt:"state",id:a[s],type:"join"};break;case 22:this.$={stmt:"state",id:a[s],type:"choice"};break;case 23:this.$={stmt:"state",id:r.getDividerId(),type:"divider"};break;case 24:this.$={stmt:"state",id:a[s-1].trim(),note:{position:a[s-2].trim(),text:a[s].trim()}};break;case 30:r.setDirection("TB"),this.$={stmt:"dir",value:"TB"};break;case 31:r.setDirection("BT"),this.$={stmt:"dir",value:"BT"};break;case 32:r.setDirection("RL"),this.$={stmt:"dir",value:"RL"};break;case 33:r.setDirection("LR"),this.$={stmt:"dir",value:"LR"};break;case 40:r.parseDirective("%%{","open_directive");break;case 41:r.parseDirective(a[s],"type_directive");break;case 42:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 43:r.parseDirective("}%%","close_directive","state")}},table:[{3:1,4:e,5:n,6:4,7:r,31:6,45:i},{1:[3]},{3:8,4:e,5:n,6:4,7:r,31:6,45:i},{3:9,4:e,5:n,6:4,7:r,31:6,45:i},{3:10,4:e,5:n,6:4,7:r,31:6,45:i},t([1,4,5,14,15,17,20,22,23,24,25,26,27,36,37,38,39,42,45],a,{8:11}),{32:12,46:[1,13]},{46:[2,40]},{1:[2,1]},{1:[2,2]},{1:[2,3]},{1:[2,4],4:o,5:s,6:28,9:14,10:16,11:18,14:c,15:u,17:l,20:h,22:f,23:d,24:p,25:y,26:g,27:m,30:29,31:6,36:v,37:b,38:_,39:x,42:w,45:i},{33:36,34:[1,37],48:k},t([34,48],[2,41]),t(T,[2,6]),{6:28,10:39,11:18,14:c,15:u,17:l,20:h,22:f,23:d,24:p,25:y,26:g,27:m,30:29,31:6,36:v,37:b,38:_,39:x,42:w,45:i},t(T,[2,8]),t(T,[2,9]),t(T,[2,10],{12:[1,40],13:[1,41]}),t(T,[2,14]),{16:[1,42]},t(T,[2,16],{18:[1,43]}),{21:[1,44]},t(T,[2,20]),t(T,[2,21]),t(T,[2,22]),t(T,[2,23]),{28:45,29:[1,46],43:[1,47],44:[1,48]},t(T,[2,26]),t(T,[2,27]),t(E,[2,36]),t(E,[2,37]),t(T,[2,30]),t(T,[2,31]),t(T,[2,32]),t(T,[2,33]),t(C,[2,28]),{35:49,47:[1,50]},t(C,[2,43]),t(T,[2,7]),t(T,[2,11]),{11:51,22:f,42:w},t(T,[2,15]),t(S,a,{8:52}),{22:[1,53]},{22:[1,54]},{21:[1,55]},{22:[2,38]},{22:[2,39]},{33:56,48:k},{48:[2,42]},t(T,[2,12],{12:[1,57]}),{4:o,5:s,6:28,9:14,10:16,11:18,14:c,15:u,17:l,19:[1,58],20:h,22:f,23:d,24:p,25:y,26:g,27:m,30:29,31:6,36:v,37:b,38:_,39:x,42:w,45:i},t(T,[2,18],{18:[1,59]}),{29:[1,60]},{22:[1,61]},t(C,[2,29]),t(T,[2,13]),t(T,[2,17]),t(S,a,{8:62}),t(T,[2,24]),t(T,[2,25]),{4:o,5:s,6:28,9:14,10:16,11:18,14:c,15:u,17:l,19:[1,63],20:h,22:f,23:d,24:p,25:y,26:g,27:m,30:29,31:6,36:v,37:b,38:_,39:x,42:w,45:i},t(T,[2,19])],defaultActions:{7:[2,40],8:[2,1],9:[2,2],10:[2,3],47:[2,38],48:[2,39],50:[2,42]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},M={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:case 26:return 36;case 1:case 27:return 37;case 2:case 28:return 38;case 3:case 29:return 39;case 4:return this.begin("open_directive"),45;case 5:return this.begin("type_directive"),46;case 6:return this.popState(),this.begin("arg_directive"),34;case 7:return this.popState(),this.popState(),48;case 8:return 47;case 9:case 10:case 12:case 13:case 14:case 15:case 39:case 45:break;case 11:case 59:return 5;case 16:return this.pushState("SCALE"),15;case 17:return 16;case 18:case 33:case 36:this.popState();break;case 19:this.pushState("STATE");break;case 20:case 23:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),23;case 21:case 24:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),24;case 22:case 25:return this.popState(),e.yytext=e.yytext.slice(0,-10).trim(),25;case 30:this.begin("STATE_STRING");break;case 31:return this.popState(),this.pushState("STATE_ID"),"AS";case 32:case 47:return this.popState(),"ID";case 34:return"STATE_DESCR";case 35:return 17;case 37:return this.popState(),this.pushState("struct"),18;case 38:return this.popState(),19;case 40:return this.begin("NOTE"),27;case 41:return this.popState(),this.pushState("NOTE_ID"),43;case 42:return this.popState(),this.pushState("NOTE_ID"),44;case 43:this.popState(),this.pushState("FLOATING_NOTE");break;case 44:return this.popState(),this.pushState("FLOATING_NOTE_ID"),"AS";case 46:return"NOTE_TEXT";case 48:return this.popState(),this.pushState("NOTE_TEXT"),22;case 49:return this.popState(),e.yytext=e.yytext.substr(2).trim(),29;case 50:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),29;case 51:case 52:return 7;case 53:return 14;case 54:return 42;case 55:return 22;case 56:return e.yytext=e.yytext.trim(),12;case 57:return 13;case 58:return 26;case 60:return"INVALID"}},rules:[/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:[\s]+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:scale\s+)/i,/^(?:\d+)/i,/^(?:\s+width\b)/i,/^(?:state\s+)/i,/^(?:.*<<fork>>)/i,/^(?:.*<<join>>)/i,/^(?:.*<<choice>>)/i,/^(?:.*\[\[fork\]\])/i,/^(?:.*\[\[join\]\])/i,/^(?:.*\[\[choice\]\])/i,/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:["])/i,/^(?:\s*as\s+)/i,/^(?:[^\n\{]*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n\s\{]+)/i,/^(?:\n)/i,/^(?:\{)/i,/^(?:\})/i,/^(?:[\n])/i,/^(?:note\s+)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:")/i,/^(?:\s*as\s*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n]*)/i,/^(?:\s*[^:\n\s\-]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:[\s\S]*?end note\b)/i,/^(?:stateDiagram\s+)/i,/^(?:stateDiagram-v2\s+)/i,/^(?:hide empty description\b)/i,/^(?:\[\*\])/i,/^(?:[^:\n\s\-\{]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:-->)/i,/^(?:--)/i,/^(?:$)/i,/^(?:.)/i],conditions:{LINE:{rules:[13,14],inclusive:!1},close_directive:{rules:[13,14],inclusive:!1},arg_directive:{rules:[7,8,13,14],inclusive:!1},type_directive:{rules:[6,7,13,14],inclusive:!1},open_directive:{rules:[5,13,14],inclusive:!1},struct:{rules:[13,14,19,26,27,28,29,38,39,40,54,55,56,57,58],inclusive:!1},FLOATING_NOTE_ID:{rules:[47],inclusive:!1},FLOATING_NOTE:{rules:[44,45,46],inclusive:!1},NOTE_TEXT:{rules:[49,50],inclusive:!1},NOTE_ID:{rules:[48],inclusive:!1},NOTE:{rules:[41,42,43],inclusive:!1},SCALE:{rules:[17,18],inclusive:!1},ALIAS:{rules:[],inclusive:!1},STATE_ID:{rules:[32],inclusive:!1},STATE_STRING:{rules:[33,34],inclusive:!1},FORK_STATE:{rules:[],inclusive:!1},STATE:{rules:[13,14,20,21,22,23,24,25,30,31,35,36,37],inclusive:!1},ID:{rules:[13,14],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,9,10,11,12,14,15,16,19,37,40,51,52,53,54,55,56,57,59,60],inclusive:!0}}};function N(){this.yy={}}return A.lexer=M,N.prototype=A,A.Parser=N,new N}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(3069).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},9763:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,5],r=[6,9,11,17,18,19,21],i=[1,15],a=[1,16],o=[1,17],s=[1,21],c=[4,6,9,11,17,18,19,21],u={trace:function(){},yy:{},symbols_:{error:2,start:3,journey:4,document:5,EOF:6,directive:7,line:8,SPACE:9,statement:10,NEWLINE:11,openDirective:12,typeDirective:13,closeDirective:14,":":15,argDirective:16,title:17,section:18,taskName:19,taskData:20,open_directive:21,type_directive:22,arg_directive:23,close_directive:24,$accept:0,$end:1},terminals_:{2:"error",4:"journey",6:"EOF",9:"SPACE",11:"NEWLINE",15:":",17:"title",18:"section",19:"taskName",20:"taskData",21:"open_directive",22:"type_directive",23:"arg_directive",24:"close_directive"},productions_:[0,[3,3],[3,2],[5,0],[5,2],[8,2],[8,1],[8,1],[8,1],[7,4],[7,6],[10,1],[10,1],[10,2],[10,1],[12,1],[13,1],[16,1],[14,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:return a[s-1];case 3:case 7:case 8:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:this.$=a[s];break;case 11:r.setTitle(a[s].substr(6)),this.$=a[s].substr(6);break;case 12:r.addSection(a[s].substr(8)),this.$=a[s].substr(8);break;case 13:r.addTask(a[s-1],a[s]),this.$="task";break;case 15:r.parseDirective("%%{","open_directive");break;case 16:r.parseDirective(a[s],"type_directive");break;case 17:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 18:r.parseDirective("}%%","close_directive","journey")}},table:[{3:1,4:e,7:3,12:4,21:n},{1:[3]},t(r,[2,3],{5:6}),{3:7,4:e,7:3,12:4,21:n},{13:8,22:[1,9]},{22:[2,15]},{6:[1,10],7:18,8:11,9:[1,12],10:13,11:[1,14],12:4,17:i,18:a,19:o,21:n},{1:[2,2]},{14:19,15:[1,20],24:s},t([15,24],[2,16]),t(r,[2,8],{1:[2,1]}),t(r,[2,4]),{7:18,10:22,12:4,17:i,18:a,19:o,21:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,11]),t(r,[2,12]),{20:[1,23]},t(r,[2,14]),{11:[1,24]},{16:25,23:[1,26]},{11:[2,18]},t(r,[2,5]),t(r,[2,13]),t(c,[2,9]),{14:27,24:s},{24:[2,17]},{11:[1,28]},t(c,[2,10])],defaultActions:{5:[2,15],7:[2,2],21:[2,18],26:[2,17]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},l={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),21;case 1:return this.begin("type_directive"),22;case 2:return this.popState(),this.begin("arg_directive"),15;case 3:return this.popState(),this.popState(),24;case 4:return 23;case 5:case 6:case 8:case 9:break;case 7:return 11;case 10:return 4;case 11:return 17;case 12:return 18;case 13:return 19;case 14:return 20;case 15:return 15;case 16:return 6;case 17:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:journey\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{open_directive:{rules:[1],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,13,14,15,16,17],inclusive:!0}}};function h(){this.yy={}}return u.lexer=l,h.prototype=u,u.Parser=h,new h}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(9143).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},9609:t=>{"use strict";var e=/^(%20|\s)*(javascript|data)/im,n=/[^\x20-\x7E]/gim,r=/^([^:]+):/gm,i=[".","/"];t.exports={sanitizeUrl:function(t){if(!t)return"about:blank";var a,o,s=t.replace(n,"").trim();return function(t){return i.indexOf(t[0])>-1}(s)?s:(o=s.match(r))?(a=o[0],e.test(a)?"about:blank":s):"about:blank"}}},3841:t=>{t.exports=function(t,e){return t.intersect(e)}},7458:(t,e,n)=>{"use strict";n.d(e,{default:()=>hC});var r=n(1941),i=n.n(r),a={debug:1,info:2,warn:3,error:4,fatal:5},o={debug:function(){},info:function(){},warn:function(){},error:function(){},fatal:function(){}},s=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"fatal";isNaN(t)&&(t=t.toLowerCase(),void 0!==a[t]&&(t=a[t])),o.trace=function(){},o.debug=function(){},o.info=function(){},o.warn=function(){},o.error=function(){},o.fatal=function(){},t<=a.fatal&&(o.fatal=console.error?console.error.bind(console,c("FATAL"),"color: orange"):console.log.bind(console,"[35m",c("FATAL"))),t<=a.error&&(o.error=console.error?console.error.bind(console,c("ERROR"),"color: orange"):console.log.bind(console,"[31m",c("ERROR"))),t<=a.warn&&(o.warn=console.warn?console.warn.bind(console,c("WARN"),"color: orange"):console.log.bind(console,"[33m",c("WARN"))),t<=a.info&&(o.info=console.info?console.info.bind(console,c("INFO"),"color: lightblue"):console.log.bind(console,"[34m",c("INFO"))),t<=a.debug&&(o.debug=console.debug?console.debug.bind(console,c("DEBUG"),"color: lightgreen"):console.log.bind(console,"[32m",c("DEBUG")))},c=function(t){var e=i()().format("ss.SSS");return"%c".concat(e," : ").concat(t," : ")};function u(t,e){let n;if(void 0===e)for(const e of t)null!=e&&(n<e||void 0===n&&e>=e)&&(n=e);else{let r=-1;for(let i of t)null!=(i=e(i,++r,t))&&(n<i||void 0===n&&i>=i)&&(n=i)}return n}function l(t,e){let n;if(void 0===e)for(const e of t)null!=e&&(n>e||void 0===n&&e>=e)&&(n=e);else{let r=-1;for(let i of t)null!=(i=e(i,++r,t))&&(n>i||void 0===n&&i>=i)&&(n=i)}return n}function h(t){return t}var f=1e-6;function d(t){return"translate("+t+",0)"}function p(t){return"translate(0,"+t+")"}function y(t){return e=>+t(e)}function g(t,e){return e=Math.max(0,t.bandwidth()-2*e)/2,t.round()&&(e=Math.round(e)),n=>+t(n)+e}function m(){return!this.__axis}function v(t,e){var n=[],r=null,i=null,a=6,o=6,s=3,c="undefined"!=typeof window&&window.devicePixelRatio>1?0:.5,u=1===t||4===t?-1:1,l=4===t||2===t?"x":"y",v=1===t||3===t?d:p;function b(d){var p=null==r?e.ticks?e.ticks.apply(e,n):e.domain():r,b=null==i?e.tickFormat?e.tickFormat.apply(e,n):h:i,_=Math.max(a,0)+s,x=e.range(),w=+x[0]+c,k=+x[x.length-1]+c,T=(e.bandwidth?g:y)(e.copy(),c),E=d.selection?d.selection():d,C=E.selectAll(".domain").data([null]),S=E.selectAll(".tick").data(p,e).order(),A=S.exit(),M=S.enter().append("g").attr("class","tick"),N=S.select("line"),D=S.select("text");C=C.merge(C.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),S=S.merge(M),N=N.merge(M.append("line").attr("stroke","currentColor").attr(l+"2",u*a)),D=D.merge(M.append("text").attr("fill","currentColor").attr(l,u*_).attr("dy",1===t?"0em":3===t?"0.71em":"0.32em")),d!==E&&(C=C.transition(d),S=S.transition(d),N=N.transition(d),D=D.transition(d),A=A.transition(d).attr("opacity",f).attr("transform",(function(t){return isFinite(t=T(t))?v(t+c):this.getAttribute("transform")})),M.attr("opacity",f).attr("transform",(function(t){var e=this.parentNode.__axis;return v((e&&isFinite(e=e(t))?e:T(t))+c)}))),A.remove(),C.attr("d",4===t||2===t?o?"M"+u*o+","+w+"H"+c+"V"+k+"H"+u*o:"M"+c+","+w+"V"+k:o?"M"+w+","+u*o+"V"+c+"H"+k+"V"+u*o:"M"+w+","+c+"H"+k),S.attr("opacity",1).attr("transform",(function(t){return v(T(t)+c)})),N.attr(l+"2",u*a),D.attr(l,u*_).text(b),E.filter(m).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",2===t?"start":4===t?"end":"middle"),E.each((function(){this.__axis=T}))}return b.scale=function(t){return arguments.length?(e=t,b):e},b.ticks=function(){return n=Array.from(arguments),b},b.tickArguments=function(t){return arguments.length?(n=null==t?[]:Array.from(t),b):n.slice()},b.tickValues=function(t){return arguments.length?(r=null==t?null:Array.from(t),b):r&&r.slice()},b.tickFormat=function(t){return arguments.length?(i=t,b):i},b.tickSize=function(t){return arguments.length?(a=o=+t,b):a},b.tickSizeInner=function(t){return arguments.length?(a=+t,b):a},b.tickSizeOuter=function(t){return arguments.length?(o=+t,b):o},b.tickPadding=function(t){return arguments.length?(s=+t,b):s},b.offset=function(t){return arguments.length?(c=+t,b):c},b}function b(){}function _(t){return null==t?b:function(){return this.querySelector(t)}}function x(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function w(){return[]}function k(t){return null==t?w:function(){return this.querySelectorAll(t)}}function T(t){return function(){return this.matches(t)}}function E(t){return function(e){return e.matches(t)}}var C=Array.prototype.find;function S(){return this.firstElementChild}var A=Array.prototype.filter;function M(){return Array.from(this.children)}function N(t){return new Array(t.length)}function D(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}function O(t){return function(){return t}}function B(t,e,n,r,i,a){for(var o,s=0,c=e.length,u=a.length;s<u;++s)(o=e[s])?(o.__data__=a[s],r[s]=o):n[s]=new D(t,a[s]);for(;s<c;++s)(o=e[s])&&(i[s]=o)}function L(t,e,n,r,i,a,o){var s,c,u,l=new Map,h=e.length,f=a.length,d=new Array(h);for(s=0;s<h;++s)(c=e[s])&&(d[s]=u=o.call(c,c.__data__,s,e)+"",l.has(u)?i[s]=c:l.set(u,c));for(s=0;s<f;++s)u=o.call(t,a[s],s,a)+"",(c=l.get(u))?(r[s]=c,c.__data__=a[s],l.delete(u)):n[s]=new D(t,a[s]);for(s=0;s<h;++s)(c=e[s])&&l.get(d[s])===c&&(i[s]=c)}function I(t){return t.__data__}function R(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function F(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}D.prototype={constructor:D,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,e){return this._parent.insertBefore(t,e)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var P="http://www.w3.org/1999/xhtml";const j={svg:"http://www.w3.org/2000/svg",xhtml:P,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function Y(t){var e=t+="",n=e.indexOf(":");return n>=0&&"xmlns"!==(e=t.slice(0,n))&&(t=t.slice(n+1)),j.hasOwnProperty(e)?{space:j[e],local:t}:t}function z(t){return function(){this.removeAttribute(t)}}function U(t){return function(){this.removeAttributeNS(t.space,t.local)}}function q(t,e){return function(){this.setAttribute(t,e)}}function H(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function $(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}}function W(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}function V(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function G(t){return function(){this.style.removeProperty(t)}}function X(t,e,n){return function(){this.style.setProperty(t,e,n)}}function Z(t,e,n){return function(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}}function Q(t,e){return t.style.getPropertyValue(e)||V(t).getComputedStyle(t,null).getPropertyValue(e)}function K(t){return function(){delete this[t]}}function J(t,e){return function(){this[t]=e}}function tt(t,e){return function(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}}function et(t){return t.trim().split(/^|\s+/)}function nt(t){return t.classList||new rt(t)}function rt(t){this._node=t,this._names=et(t.getAttribute("class")||"")}function it(t,e){for(var n=nt(t),r=-1,i=e.length;++r<i;)n.add(e[r])}function at(t,e){for(var n=nt(t),r=-1,i=e.length;++r<i;)n.remove(e[r])}function ot(t){return function(){it(this,t)}}function st(t){return function(){at(this,t)}}function ct(t,e){return function(){(e.apply(this,arguments)?it:at)(this,t)}}function ut(){this.textContent=""}function lt(t){return function(){this.textContent=t}}function ht(t){return function(){var e=t.apply(this,arguments);this.textContent=null==e?"":e}}function ft(){this.innerHTML=""}function dt(t){return function(){this.innerHTML=t}}function pt(t){return function(){var e=t.apply(this,arguments);this.innerHTML=null==e?"":e}}function yt(){this.nextSibling&&this.parentNode.appendChild(this)}function gt(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function mt(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===P&&e.documentElement.namespaceURI===P?e.createElement(t):e.createElementNS(n,t)}}function vt(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function bt(t){var e=Y(t);return(e.local?vt:mt)(e)}function _t(){return null}function xt(){var t=this.parentNode;t&&t.removeChild(this)}function wt(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function kt(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function Tt(t){return t.trim().split(/^|\s+/).map((function(t){var e="",n=t.indexOf(".");return n>=0&&(e=t.slice(n+1),t=t.slice(0,n)),{type:t,name:e}}))}function Et(t){return function(){var e=this.__on;if(e){for(var n,r=0,i=-1,a=e.length;r<a;++r)n=e[r],t.type&&n.type!==t.type||n.name!==t.name?e[++i]=n:this.removeEventListener(n.type,n.listener,n.options);++i?e.length=i:delete this.__on}}}function Ct(t,e,n){return function(){var r,i=this.__on,a=function(t){return function(e){t.call(this,e,this.__data__)}}(e);if(i)for(var o=0,s=i.length;o<s;++o)if((r=i[o]).type===t.type&&r.name===t.name)return this.removeEventListener(r.type,r.listener,r.options),this.addEventListener(r.type,r.listener=a,r.options=n),void(r.value=e);this.addEventListener(t.type,a,n),r={type:t.type,name:t.name,value:e,listener:a,options:n},i?i.push(r):this.__on=[r]}}function St(t,e,n){var r=V(t),i=r.CustomEvent;"function"==typeof i?i=new i(e,n):(i=r.document.createEvent("Event"),n?(i.initEvent(e,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function At(t,e){return function(){return St(this,t,e)}}function Mt(t,e){return function(){return St(this,t,e.apply(this,arguments))}}rt.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var Nt=[null];function Dt(t,e){this._groups=t,this._parents=e}function Ot(){return new Dt([[document.documentElement]],Nt)}Dt.prototype=Ot.prototype={constructor:Dt,select:function(t){"function"!=typeof t&&(t=_(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o,s=e[i],c=s.length,u=r[i]=new Array(c),l=0;l<c;++l)(a=s[l])&&(o=t.call(a,a.__data__,l,s))&&("__data__"in a&&(o.__data__=a.__data__),u[l]=o);return new Dt(r,this._parents)},selectAll:function(t){t="function"==typeof t?function(t){return function(){return x(t.apply(this,arguments))}}(t):k(t);for(var e=this._groups,n=e.length,r=[],i=[],a=0;a<n;++a)for(var o,s=e[a],c=s.length,u=0;u<c;++u)(o=s[u])&&(r.push(t.call(o,o.__data__,u,s)),i.push(o));return new Dt(r,i)},selectChild:function(t){return this.select(null==t?S:function(t){return function(){return C.call(this.children,t)}}("function"==typeof t?t:E(t)))},selectChildren:function(t){return this.selectAll(null==t?M:function(t){return function(){return A.call(this.children,t)}}("function"==typeof t?t:E(t)))},filter:function(t){"function"!=typeof t&&(t=T(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new Dt(r,this._parents)},data:function(t,e){if(!arguments.length)return Array.from(this,I);var n=e?L:B,r=this._parents,i=this._groups;"function"!=typeof t&&(t=O(t));for(var a=i.length,o=new Array(a),s=new Array(a),c=new Array(a),u=0;u<a;++u){var l=r[u],h=i[u],f=h.length,d=R(t.call(l,l&&l.__data__,u,r)),p=d.length,y=s[u]=new Array(p),g=o[u]=new Array(p),m=c[u]=new Array(f);n(l,h,y,g,m,d,e);for(var v,b,_=0,x=0;_<p;++_)if(v=y[_]){for(_>=x&&(x=_+1);!(b=g[x])&&++x<p;);v._next=b||null}}return(o=new Dt(o,r))._enter=s,o._exit=c,o},enter:function(){return new Dt(this._enter||this._groups.map(N),this._parents)},exit:function(){return new Dt(this._exit||this._groups.map(N),this._parents)},join:function(t,e,n){var r=this.enter(),i=this,a=this.exit();return"function"==typeof t?(r=t(r))&&(r=r.selection()):r=r.append(t+""),null!=e&&(i=e(i))&&(i=i.selection()),null==n?a.remove():n(a),r&&i?r.merge(i).order():i},merge:function(t){for(var e=t.selection?t.selection():t,n=this._groups,r=e._groups,i=n.length,a=r.length,o=Math.min(i,a),s=new Array(i),c=0;c<o;++c)for(var u,l=n[c],h=r[c],f=l.length,d=s[c]=new Array(f),p=0;p<f;++p)(u=l[p]||h[p])&&(d[p]=u);for(;c<i;++c)s[c]=n[c];return new Dt(s,this._parents)},selection:function(){return this},order:function(){for(var t=this._groups,e=-1,n=t.length;++e<n;)for(var r,i=t[e],a=i.length-1,o=i[a];--a>=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function e(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}t||(t=F);for(var n=this._groups,r=n.length,i=new Array(r),a=0;a<r;++a){for(var o,s=n[a],c=s.length,u=i[a]=new Array(c),l=0;l<c;++l)(o=s[l])&&(u[l]=o);u.sort(e)}return new Dt(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){return Array.from(this)},node:function(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r=t[e],i=0,a=r.length;i<a;++i){var o=r[i];if(o)return o}return null},size:function(){let t=0;for(const e of this)++t;return t},empty:function(){return!this.node()},each:function(t){for(var e=this._groups,n=0,r=e.length;n<r;++n)for(var i,a=e[n],o=0,s=a.length;o<s;++o)(i=a[o])&&t.call(i,i.__data__,o,a);return this},attr:function(t,e){var n=Y(t);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((null==e?n.local?U:z:"function"==typeof e?n.local?W:$:n.local?H:q)(n,e))},style:function(t,e,n){return arguments.length>1?this.each((null==e?G:"function"==typeof e?Z:X)(t,e,null==n?"":n)):Q(this.node(),t)},property:function(t,e){return arguments.length>1?this.each((null==e?K:"function"==typeof e?tt:J)(t,e)):this.node()[t]},classed:function(t,e){var n=et(t+"");if(arguments.length<2){for(var r=nt(this.node()),i=-1,a=n.length;++i<a;)if(!r.contains(n[i]))return!1;return!0}return this.each(("function"==typeof e?ct:e?ot:st)(n,e))},text:function(t){return arguments.length?this.each(null==t?ut:("function"==typeof t?ht:lt)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?ft:("function"==typeof t?pt:dt)(t)):this.node().innerHTML},raise:function(){return this.each(yt)},lower:function(){return this.each(gt)},append:function(t){var e="function"==typeof t?t:bt(t);return this.select((function(){return this.appendChild(e.apply(this,arguments))}))},insert:function(t,e){var n="function"==typeof t?t:bt(t),r=null==e?_t:"function"==typeof e?e:_(e);return this.select((function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(xt)},clone:function(t){return this.select(t?kt:wt)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,e,n){var r,i,a=Tt(t+""),o=a.length;if(!(arguments.length<2)){for(s=e?Ct:Et,r=0;r<o;++r)this.each(s(a[r],e,n));return this}var s=this.node().__on;if(s)for(var c,u=0,l=s.length;u<l;++u)for(r=0,c=s[u];r<o;++r)if((i=a[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,e){return this.each(("function"==typeof e?Mt:At)(t,e))},[Symbol.iterator]:function*(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r,i=t[e],a=0,o=i.length;a<o;++a)(r=i[a])&&(yield r)}};const Bt=Ot;var Lt={value:()=>{}};function It(){for(var t,e=0,n=arguments.length,r={};e<n;++e){if(!(t=arguments[e]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new Rt(r)}function Rt(t){this._=t}function Ft(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function Pt(t,e){for(var n,r=0,i=t.length;r<i;++r)if((n=t[r]).name===e)return n.value}function jt(t,e,n){for(var r=0,i=t.length;r<i;++r)if(t[r].name===e){t[r]=Lt,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=n&&t.push({name:e,value:n}),t}Rt.prototype=It.prototype={constructor:Rt,on:function(t,e){var n,r=this._,i=Ft(t+"",r),a=-1,o=i.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++a<o;)if(n=(t=i[a]).type)r[n]=jt(r[n],t.name,e);else if(null==e)for(n in r)r[n]=jt(r[n],t.name,null);return this}for(;++a<o;)if((n=(t=i[a]).type)&&(n=Pt(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new Rt(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,i=new Array(n),a=0;a<n;++a)i[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,i)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,a=r.length;i<a;++i)r[i].value.apply(e,n)}};const Yt=It;var zt,Ut,qt=0,Ht=0,$t=0,Wt=0,Vt=0,Gt=0,Xt="object"==typeof performance&&performance.now?performance:Date,Zt="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function Qt(){return Vt||(Zt(Kt),Vt=Xt.now()+Gt)}function Kt(){Vt=0}function Jt(){this._call=this._time=this._next=null}function te(t,e,n){var r=new Jt;return r.restart(t,e,n),r}function ee(){Vt=(Wt=Xt.now())+Gt,qt=Ht=0;try{!function(){Qt(),++qt;for(var t,e=zt;e;)(t=Vt-e._time)>=0&&e._call.call(void 0,t),e=e._next;--qt}()}finally{qt=0,function(){for(var t,e,n=zt,r=1/0;n;)n._call?(r>n._time&&(r=n._time),t=n,n=n._next):(e=n._next,n._next=null,n=t?t._next=e:zt=e);Ut=t,re(r)}(),Vt=0}}function ne(){var t=Xt.now(),e=t-Wt;e>1e3&&(Gt-=e,Wt=t)}function re(t){qt||(Ht&&(Ht=clearTimeout(Ht)),t-Vt>24?(t<1/0&&(Ht=setTimeout(ee,t-Xt.now()-Gt)),$t&&($t=clearInterval($t))):($t||(Wt=Xt.now(),$t=setInterval(ne,1e3)),qt=1,Zt(ee)))}function ie(t,e,n){var r=new Jt;return e=null==e?0:+e,r.restart((n=>{r.stop(),t(n+e)}),e,n),r}Jt.prototype=te.prototype={constructor:Jt,restart:function(t,e,n){if("function"!=typeof t)throw new TypeError("callback is not a function");n=(null==n?Qt():+n)+(null==e?0:+e),this._next||Ut===this||(Ut?Ut._next=this:zt=this,Ut=this),this._call=t,this._time=n,re()},stop:function(){this._call&&(this._call=null,this._time=1/0,re())}};var ae=Yt("start","end","cancel","interrupt"),oe=[];function se(t,e,n,r,i,a){var o=t.__transition;if(o){if(n in o)return}else t.__transition={};!function(t,e,n){var r,i=t.__transition;function a(c){var u,l,h,f;if(1!==n.state)return s();for(u in i)if((f=i[u]).name===n.name){if(3===f.state)return ie(a);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[u]):+u<e&&(f.state=6,f.timer.stop(),f.on.call("cancel",t,t.__data__,f.index,f.group),delete i[u])}if(ie((function(){3===n.state&&(n.state=4,n.timer.restart(o,n.delay,n.time),o(c))})),n.state=2,n.on.call("start",t,t.__data__,n.index,n.group),2===n.state){for(n.state=3,r=new Array(h=n.tween.length),u=0,l=-1;u<h;++u)(f=n.tween[u].value.call(t,t.__data__,n.index,n.group))&&(r[++l]=f);r.length=l+1}}function o(e){for(var i=e<n.duration?n.ease.call(null,e/n.duration):(n.timer.restart(s),n.state=5,1),a=-1,o=r.length;++a<o;)r[a].call(t,i);5===n.state&&(n.on.call("end",t,t.__data__,n.index,n.group),s())}function s(){for(var r in n.state=6,n.timer.stop(),delete i[e],i)return;delete t.__transition}i[e]=n,n.timer=te((function(t){n.state=1,n.timer.restart(a,n.delay,n.time),n.delay<=t&&a(t-n.delay)}),0,n.time)}(t,n,{name:e,index:r,group:i,on:ae,tween:oe,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:0})}function ce(t,e){var n=le(t,e);if(n.state>0)throw new Error("too late; already scheduled");return n}function ue(t,e){var n=le(t,e);if(n.state>3)throw new Error("too late; already running");return n}function le(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function he(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}var fe,de=180/Math.PI,pe={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function ye(t,e,n,r,i,a){var o,s,c;return(o=Math.sqrt(t*t+e*e))&&(t/=o,e/=o),(c=t*n+e*r)&&(n-=t*c,r-=e*c),(s=Math.sqrt(n*n+r*r))&&(n/=s,r/=s,c/=s),t*r<e*n&&(t=-t,e=-e,c=-c,o=-o),{translateX:i,translateY:a,rotate:Math.atan2(e,t)*de,skewX:Math.atan(c)*de,scaleX:o,scaleY:s}}function ge(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return function(a,o){var s=[],c=[];return a=t(a),o=t(o),function(t,r,i,a,o,s){if(t!==i||r!==a){var c=o.push("translate(",null,e,null,n);s.push({i:c-4,x:he(t,i)},{i:c-2,x:he(r,a)})}else(i||a)&&o.push("translate("+i+e+a+n)}(a.translateX,a.translateY,o.translateX,o.translateY,s,c),function(t,e,n,a){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:he(t,e)})):e&&n.push(i(n)+"rotate("+e+r)}(a.rotate,o.rotate,s,c),function(t,e,n,a){t!==e?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:he(t,e)}):e&&n.push(i(n)+"skewX("+e+r)}(a.skewX,o.skewX,s,c),function(t,e,n,r,a,o){if(t!==n||e!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:he(t,n)},{i:s-2,x:he(e,r)})}else 1===n&&1===r||a.push(i(a)+"scale("+n+","+r+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,c),a=o=null,function(t){for(var e,n=-1,r=c.length;++n<r;)s[(e=c[n]).i]=e.x(t);return s.join("")}}}var me=ge((function(t){const e=new("function"==typeof DOMMatrix?DOMMatrix:WebKitCSSMatrix)(t+"");return e.isIdentity?pe:ye(e.a,e.b,e.c,e.d,e.e,e.f)}),"px, ","px)","deg)"),ve=ge((function(t){return null==t?pe:(fe||(fe=document.createElementNS("http://www.w3.org/2000/svg","g")),fe.setAttribute("transform",t),(t=fe.transform.baseVal.consolidate())?ye((t=t.matrix).a,t.b,t.c,t.d,t.e,t.f):pe)}),", ",")",")");function be(t,e){var n,r;return function(){var i=ue(this,t),a=i.tween;if(a!==n)for(var o=0,s=(r=n=a).length;o<s;++o)if(r[o].name===e){(r=r.slice()).splice(o,1);break}i.tween=r}}function _e(t,e,n){var r,i;if("function"!=typeof n)throw new Error;return function(){var a=ue(this,t),o=a.tween;if(o!==r){i=(r=o).slice();for(var s={name:e,value:n},c=0,u=i.length;c<u;++c)if(i[c].name===e){i[c]=s;break}c===u&&i.push(s)}a.tween=i}}function xe(t,e,n){var r=t._id;return t.each((function(){var t=ue(this,r);(t.value||(t.value={}))[e]=n.apply(this,arguments)})),function(t){return le(t,r).value[e]}}function we(t,e,n){t.prototype=e.prototype=n,n.constructor=t}function ke(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function Te(){}var Ee=.7,Ce=1/Ee,Se="\\s*([+-]?\\d+)\\s*",Ae="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",Me="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",Ne=/^#([0-9a-f]{3,8})$/,De=new RegExp("^rgb\\("+[Se,Se,Se]+"\\)$"),Oe=new RegExp("^rgb\\("+[Me,Me,Me]+"\\)$"),Be=new RegExp("^rgba\\("+[Se,Se,Se,Ae]+"\\)$"),Le=new RegExp("^rgba\\("+[Me,Me,Me,Ae]+"\\)$"),Ie=new RegExp("^hsl\\("+[Ae,Me,Me]+"\\)$"),Re=new RegExp("^hsla\\("+[Ae,Me,Me,Ae]+"\\)$"),Fe={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function Pe(){return this.rgb().formatHex()}function je(){return this.rgb().formatRgb()}function Ye(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=Ne.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?ze(e):3===n?new $e(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?Ue(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?Ue(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=De.exec(t))?new $e(e[1],e[2],e[3],1):(e=Oe.exec(t))?new $e(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=Be.exec(t))?Ue(e[1],e[2],e[3],e[4]):(e=Le.exec(t))?Ue(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=Ie.exec(t))?Xe(e[1],e[2]/100,e[3]/100,1):(e=Re.exec(t))?Xe(e[1],e[2]/100,e[3]/100,e[4]):Fe.hasOwnProperty(t)?ze(Fe[t]):"transparent"===t?new $e(NaN,NaN,NaN,0):null}function ze(t){return new $e(t>>16&255,t>>8&255,255&t,1)}function Ue(t,e,n,r){return r<=0&&(t=e=n=NaN),new $e(t,e,n,r)}function qe(t){return t instanceof Te||(t=Ye(t)),t?new $e((t=t.rgb()).r,t.g,t.b,t.opacity):new $e}function He(t,e,n,r){return 1===arguments.length?qe(t):new $e(t,e,n,null==r?1:r)}function $e(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function We(){return"#"+Ge(this.r)+Ge(this.g)+Ge(this.b)}function Ve(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function Ge(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function Xe(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new Qe(t,e,n,r)}function Ze(t){if(t instanceof Qe)return new Qe(t.h,t.s,t.l,t.opacity);if(t instanceof Te||(t=Ye(t)),!t)return new Qe;if(t instanceof Qe)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new Qe(o,s,c,t.opacity)}function Qe(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function Ke(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}function Je(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}we(Te,Ye,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:Pe,formatHex:Pe,formatHsl:function(){return Ze(this).formatHsl()},formatRgb:je,toString:je}),we($e,He,ke(Te,{brighter:function(t){return t=null==t?Ce:Math.pow(Ce,t),new $e(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Ee:Math.pow(Ee,t),new $e(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:We,formatHex:We,formatRgb:Ve,toString:Ve})),we(Qe,(function(t,e,n,r){return 1===arguments.length?Ze(t):new Qe(t,e,n,null==r?1:r)}),ke(Te,{brighter:function(t){return t=null==t?Ce:Math.pow(Ce,t),new Qe(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Ee:Math.pow(Ee,t),new Qe(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new $e(Ke(t>=240?t-240:t+120,i,r),Ke(t,i,r),Ke(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const tn=t=>()=>t;function en(t,e){var n=e-t;return n?function(t,e){return function(n){return t+n*e}}(t,n):tn(isNaN(t)?e:t)}const nn=function t(e){var n=function(t){return 1==(t=+t)?en:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):tn(isNaN(e)?n:e)}}(e);function r(t,e){var r=n((t=He(t)).r,(e=He(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=en(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function rn(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=He(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}rn((function(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return Je((n-r/e)*e,o,i,a,s)}})),rn((function(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return Je((n-r/e)*e,i,a,o,s)}}));var an=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,on=new RegExp(an.source,"g");function sn(t,e){var n,r,i,a=an.lastIndex=on.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=an.exec(t))&&(r=on.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:he(n,r)})),a=on.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})}function cn(t,e){var n;return("number"==typeof e?he:e instanceof Ye?nn:(n=Ye(e))?(e=n,nn):sn)(t,e)}function un(t){return function(){this.removeAttribute(t)}}function ln(t){return function(){this.removeAttributeNS(t.space,t.local)}}function hn(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttribute(t);return o===a?null:o===r?i:i=e(r=o,n)}}function fn(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttributeNS(t.space,t.local);return o===a?null:o===r?i:i=e(r=o,n)}}function dn(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttribute(t))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttribute(t)}}function pn(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttributeNS(t.space,t.local))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttributeNS(t.space,t.local)}}function yn(t,e){return function(n){this.setAttribute(t,e.call(this,n))}}function gn(t,e){return function(n){this.setAttributeNS(t.space,t.local,e.call(this,n))}}function mn(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&gn(t,i)),n}return i._value=e,i}function vn(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&yn(t,i)),n}return i._value=e,i}function bn(t,e){return function(){ce(this,t).delay=+e.apply(this,arguments)}}function _n(t,e){return e=+e,function(){ce(this,t).delay=e}}function xn(t,e){return function(){ue(this,t).duration=+e.apply(this,arguments)}}function wn(t,e){return e=+e,function(){ue(this,t).duration=e}}function kn(t,e){if("function"!=typeof e)throw new Error;return function(){ue(this,t).ease=e}}function Tn(t,e,n){var r,i,a=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var e=t.indexOf(".");return e>=0&&(t=t.slice(0,e)),!t||"start"===t}))}(e)?ce:ue;return function(){var o=a(this,t),s=o.on;s!==r&&(i=(r=s).copy()).on(e,n),o.on=i}}var En=Bt.prototype.constructor;function Cn(t){return function(){this.style.removeProperty(t)}}function Sn(t,e,n){return function(r){this.style.setProperty(t,e.call(this,r),n)}}function An(t,e,n){var r,i;function a(){var a=e.apply(this,arguments);return a!==i&&(r=(i=a)&&Sn(t,a,n)),r}return a._value=e,a}function Mn(t){return function(e){this.textContent=t.call(this,e)}}function Nn(t){var e,n;function r(){var r=t.apply(this,arguments);return r!==n&&(e=(n=r)&&Mn(r)),e}return r._value=t,r}var Dn=0;function On(t,e,n,r){this._groups=t,this._parents=e,this._name=n,this._id=r}function Bn(){return++Dn}var Ln=Bt.prototype;On.prototype=function(t){return Bt().transition(t)}.prototype={constructor:On,select:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=_(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;o<i;++o)for(var s,c,u=r[o],l=u.length,h=a[o]=new Array(l),f=0;f<l;++f)(s=u[f])&&(c=t.call(s,s.__data__,f,u))&&("__data__"in s&&(c.__data__=s.__data__),h[f]=c,se(h[f],e,n,f,h,le(s,n)));return new On(a,this._parents,e,n)},selectAll:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=k(t));for(var r=this._groups,i=r.length,a=[],o=[],s=0;s<i;++s)for(var c,u=r[s],l=u.length,h=0;h<l;++h)if(c=u[h]){for(var f,d=t.call(c,c.__data__,h,u),p=le(c,n),y=0,g=d.length;y<g;++y)(f=d[y])&&se(f,e,n,y,d,p);a.push(d),o.push(c)}return new On(a,o,e,n)},selectChild:Ln.selectChild,selectChildren:Ln.selectChildren,filter:function(t){"function"!=typeof t&&(t=T(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new On(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new On(o,this._parents,this._name,this._id)},selection:function(){return new En(this._groups,this._parents)},transition:function(){for(var t=this._name,e=this._id,n=Bn(),r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)if(o=s[u]){var l=le(o,e);se(o,t,n,u,s,{time:l.time+l.delay+l.duration,delay:0,duration:l.duration,ease:l.ease})}return new On(r,this._parents,t,n)},call:Ln.call,nodes:Ln.nodes,node:Ln.node,size:Ln.size,empty:Ln.empty,each:Ln.each,on:function(t,e){var n=this._id;return arguments.length<2?le(this.node(),n).on.on(t):this.each(Tn(n,t,e))},attr:function(t,e){var n=Y(t),r="transform"===n?ve:cn;return this.attrTween(t,"function"==typeof e?(n.local?pn:dn)(n,r,xe(this,"attr."+t,e)):null==e?(n.local?ln:un)(n):(n.local?fn:hn)(n,r,e))},attrTween:function(t,e){var n="attr."+t;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(null==e)return this.tween(n,null);if("function"!=typeof e)throw new Error;var r=Y(t);return this.tween(n,(r.local?mn:vn)(r,e))},style:function(t,e,n){var r="transform"==(t+="")?me:cn;return null==e?this.styleTween(t,function(t,e){var n,r,i;return function(){var a=Q(this,t),o=(this.style.removeProperty(t),Q(this,t));return a===o?null:a===n&&o===r?i:i=e(n=a,r=o)}}(t,r)).on("end.style."+t,Cn(t)):"function"==typeof e?this.styleTween(t,function(t,e,n){var r,i,a;return function(){var o=Q(this,t),s=n(this),c=s+"";return null==s&&(this.style.removeProperty(t),c=s=Q(this,t)),o===c?null:o===r&&c===i?a:(i=c,a=e(r=o,s))}}(t,r,xe(this,"style."+t,e))).each(function(t,e){var n,r,i,a,o="style."+e,s="end."+o;return function(){var c=ue(this,t),u=c.on,l=null==c.value[o]?a||(a=Cn(e)):void 0;u===n&&i===l||(r=(n=u).copy()).on(s,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,e,n){var r,i,a=n+"";return function(){var o=Q(this,t);return o===a?null:o===r?i:i=e(r=o,n)}}(t,r,e),n).on("end.style."+t,null)},styleTween:function(t,e,n){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==e)return this.tween(r,null);if("function"!=typeof e)throw new Error;return this.tween(r,An(t,e,null==n?"":n))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var e=t(this);this.textContent=null==e?"":e}}(xe(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(null==t)return this.tween(e,null);if("function"!=typeof t)throw new Error;return this.tween(e,Nn(t))},remove:function(){return this.on("end.remove",function(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}(this._id))},tween:function(t,e){var n=this._id;if(t+="",arguments.length<2){for(var r,i=le(this.node(),n).tween,a=0,o=i.length;a<o;++a)if((r=i[a]).name===t)return r.value;return null}return this.each((null==e?be:_e)(n,t,e))},delay:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?bn:_n)(e,t)):le(this.node(),e).delay},duration:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?xn:wn)(e,t)):le(this.node(),e).duration},ease:function(t){var e=this._id;return arguments.length?this.each(kn(e,t)):le(this.node(),e).ease},easeVarying:function(t){if("function"!=typeof t)throw new Error;return this.each(function(t,e){return function(){var n=e.apply(this,arguments);if("function"!=typeof n)throw new Error;ue(this,t).ease=n}}(this._id,t))},end:function(){var t,e,n=this,r=n._id,i=n.size();return new Promise((function(a,o){var s={value:o},c={value:function(){0==--i&&a()}};n.each((function(){var n=ue(this,r),i=n.on;i!==t&&((e=(t=i).copy())._.cancel.push(s),e._.interrupt.push(s),e._.end.push(c)),n.on=e})),0===i&&a()}))},[Symbol.iterator]:Ln[Symbol.iterator]};var In={time:null,delay:0,duration:250,ease:function(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}};function Rn(t,e){for(var n;!(n=t.__transition)||!(n=n[e]);)if(!(t=t.parentNode))throw new Error(`transition ${e} not found`);return n}Bt.prototype.interrupt=function(t){return this.each((function(){!function(t,e){var n,r,i,a=t.__transition,o=!0;if(a){for(i in e=null==e?null:e+"",a)(n=a[i]).name===e?(r=n.state>2&&n.state<5,n.state=6,n.timer.stop(),n.on.call(r?"interrupt":"cancel",t,t.__data__,n.index,n.group),delete a[i]):o=!1;o&&delete t.__transition}}(this,t)}))},Bt.prototype.transition=function(t){var e,n;t instanceof On?(e=t._id,t=t._name):(e=Bn(),(n=In).time=Qt(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)(o=s[u])&&se(o,t,e,u,s,n||Rn(o,e));return new On(r,this._parents,t,e)};const{abs:Fn,max:Pn,min:jn}=Math;function Yn(t){return{type:t}}function zn(t,e,n){t.prototype=e.prototype=n,n.constructor=t}function Un(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function qn(){}["w","e"].map(Yn),["n","s"].map(Yn),["n","w","e","s","nw","ne","sw","se"].map(Yn);var Hn=.7,$n=1/Hn,Wn="\\s*([+-]?\\d+)\\s*",Vn="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",Gn="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",Xn=/^#([0-9a-f]{3,8})$/,Zn=new RegExp("^rgb\\("+[Wn,Wn,Wn]+"\\)$"),Qn=new RegExp("^rgb\\("+[Gn,Gn,Gn]+"\\)$"),Kn=new RegExp("^rgba\\("+[Wn,Wn,Wn,Vn]+"\\)$"),Jn=new RegExp("^rgba\\("+[Gn,Gn,Gn,Vn]+"\\)$"),tr=new RegExp("^hsl\\("+[Vn,Gn,Gn]+"\\)$"),er=new RegExp("^hsla\\("+[Vn,Gn,Gn,Vn]+"\\)$"),nr={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function rr(){return this.rgb().formatHex()}function ir(){return this.rgb().formatRgb()}function ar(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=Xn.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?or(e):3===n?new lr(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?sr(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?sr(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=Zn.exec(t))?new lr(e[1],e[2],e[3],1):(e=Qn.exec(t))?new lr(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=Kn.exec(t))?sr(e[1],e[2],e[3],e[4]):(e=Jn.exec(t))?sr(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=tr.exec(t))?pr(e[1],e[2]/100,e[3]/100,1):(e=er.exec(t))?pr(e[1],e[2]/100,e[3]/100,e[4]):nr.hasOwnProperty(t)?or(nr[t]):"transparent"===t?new lr(NaN,NaN,NaN,0):null}function or(t){return new lr(t>>16&255,t>>8&255,255&t,1)}function sr(t,e,n,r){return r<=0&&(t=e=n=NaN),new lr(t,e,n,r)}function cr(t){return t instanceof qn||(t=ar(t)),t?new lr((t=t.rgb()).r,t.g,t.b,t.opacity):new lr}function ur(t,e,n,r){return 1===arguments.length?cr(t):new lr(t,e,n,null==r?1:r)}function lr(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function hr(){return"#"+dr(this.r)+dr(this.g)+dr(this.b)}function fr(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function dr(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function pr(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new gr(t,e,n,r)}function yr(t){if(t instanceof gr)return new gr(t.h,t.s,t.l,t.opacity);if(t instanceof qn||(t=ar(t)),!t)return new gr;if(t instanceof gr)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new gr(o,s,c,t.opacity)}function gr(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function mr(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}zn(qn,ar,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:rr,formatHex:rr,formatHsl:function(){return yr(this).formatHsl()},formatRgb:ir,toString:ir}),zn(lr,ur,Un(qn,{brighter:function(t){return t=null==t?$n:Math.pow($n,t),new lr(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Hn:Math.pow(Hn,t),new lr(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:hr,formatHex:hr,formatRgb:fr,toString:fr})),zn(gr,(function(t,e,n,r){return 1===arguments.length?yr(t):new gr(t,e,n,null==r?1:r)}),Un(qn,{brighter:function(t){return t=null==t?$n:Math.pow($n,t),new gr(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Hn:Math.pow(Hn,t),new gr(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new lr(mr(t>=240?t-240:t+120,i,r),mr(t,i,r),mr(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const vr=Math.PI/180,br=180/Math.PI,_r=.96422,xr=.82521,wr=4/29,kr=6/29,Tr=3*kr*kr;function Er(t){if(t instanceof Cr)return new Cr(t.l,t.a,t.b,t.opacity);if(t instanceof Br)return Lr(t);t instanceof lr||(t=cr(t));var e,n,r=Nr(t.r),i=Nr(t.g),a=Nr(t.b),o=Sr((.2225045*r+.7168786*i+.0606169*a)/1);return r===i&&i===a?e=n=o:(e=Sr((.4360747*r+.3850649*i+.1430804*a)/_r),n=Sr((.0139322*r+.0971045*i+.7141733*a)/xr)),new Cr(116*o-16,500*(e-o),200*(o-n),t.opacity)}function Cr(t,e,n,r){this.l=+t,this.a=+e,this.b=+n,this.opacity=+r}function Sr(t){return t>.008856451679035631?Math.pow(t,1/3):t/Tr+wr}function Ar(t){return t>kr?t*t*t:Tr*(t-wr)}function Mr(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Nr(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Dr(t){if(t instanceof Br)return new Br(t.h,t.c,t.l,t.opacity);if(t instanceof Cr||(t=Er(t)),0===t.a&&0===t.b)return new Br(NaN,0<t.l&&t.l<100?0:NaN,t.l,t.opacity);var e=Math.atan2(t.b,t.a)*br;return new Br(e<0?e+360:e,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function Or(t,e,n,r){return 1===arguments.length?Dr(t):new Br(t,e,n,null==r?1:r)}function Br(t,e,n,r){this.h=+t,this.c=+e,this.l=+n,this.opacity=+r}function Lr(t){if(isNaN(t.h))return new Cr(t.l,0,0,t.opacity);var e=t.h*vr;return new Cr(t.l,Math.cos(e)*t.c,Math.sin(e)*t.c,t.opacity)}zn(Cr,(function(t,e,n,r){return 1===arguments.length?Er(t):new Cr(t,e,n,null==r?1:r)}),Un(qn,{brighter:function(t){return new Cr(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new Cr(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,n=isNaN(this.b)?t:t-this.b/200;return new lr(Mr(3.1338561*(e=_r*Ar(e))-1.6168667*(t=1*Ar(t))-.4906146*(n=xr*Ar(n))),Mr(-.9787684*e+1.9161415*t+.033454*n),Mr(.0719453*e-.2289914*t+1.4052427*n),this.opacity)}})),zn(Br,Or,Un(qn,{brighter:function(t){return new Br(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new Br(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return Lr(this).rgb()}}));const Ir=t=>()=>t;function Rr(t,e){return function(n){return t+n*e}}function Fr(t,e){var n=e-t;return n?Rr(t,n):Ir(isNaN(t)?e:t)}function Pr(t){return function(e,n){var r=t((e=Or(e)).h,(n=Or(n)).h),i=Fr(e.c,n.c),a=Fr(e.l,n.l),o=Fr(e.opacity,n.opacity);return function(t){return e.h=r(t),e.c=i(t),e.l=a(t),e.opacity=o(t),e+""}}}const jr=Pr((function(t,e){var n=e-t;return n?Rr(t,n>180||n<-180?n-360*Math.round(n/360):n):Ir(isNaN(t)?e:t)}));Pr(Fr);var Yr=Math.sqrt(50),zr=Math.sqrt(10),Ur=Math.sqrt(2);function qr(t,e,n){var r=(e-t)/Math.max(0,n),i=Math.floor(Math.log(r)/Math.LN10),a=r/Math.pow(10,i);return i>=0?(a>=Yr?10:a>=zr?5:a>=Ur?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(a>=Yr?10:a>=zr?5:a>=Ur?2:1)}function Hr(t,e,n){var r=Math.abs(e-t)/Math.max(0,n),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),a=r/i;return a>=Yr?i*=10:a>=zr?i*=5:a>=Ur&&(i*=2),e<t?-i:i}function $r(t,e){return null==t||null==e?NaN:t<e?-1:t>e?1:t>=e?0:NaN}function Wr(t){let e=t,n=t,r=t;function i(t,e,i=0,a=t.length){if(i<a){if(0!==n(e,e))return a;do{const n=i+a>>>1;r(t[n],e)<0?i=n+1:a=n}while(i<a)}return i}return 2!==t.length&&(e=(e,n)=>t(e)-n,n=$r,r=(e,n)=>$r(t(e),n)),{left:i,center:function(t,n,r=0,a=t.length){const o=i(t,n,r,a-1);return o>r&&e(t[o-1],n)>-e(t[o],n)?o-1:o},right:function(t,e,i=0,a=t.length){if(i<a){if(0!==n(e,e))return a;do{const n=i+a>>>1;r(t[n],e)<=0?i=n+1:a=n}while(i<a)}return i}}}const Vr=Wr($r),Gr=Vr.right,Xr=(Vr.left,Wr((function(t){return null===t?NaN:+t})).center,Gr);function Zr(t,e,n){t.prototype=e.prototype=n,n.constructor=t}function Qr(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function Kr(){}var Jr=.7,ti=1.4285714285714286,ei="\\s*([+-]?\\d+)\\s*",ni="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",ri="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",ii=/^#([0-9a-f]{3,8})$/,ai=new RegExp("^rgb\\("+[ei,ei,ei]+"\\)$"),oi=new RegExp("^rgb\\("+[ri,ri,ri]+"\\)$"),si=new RegExp("^rgba\\("+[ei,ei,ei,ni]+"\\)$"),ci=new RegExp("^rgba\\("+[ri,ri,ri,ni]+"\\)$"),ui=new RegExp("^hsl\\("+[ni,ri,ri]+"\\)$"),li=new RegExp("^hsla\\("+[ni,ri,ri,ni]+"\\)$"),hi={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function fi(){return this.rgb().formatHex()}function di(){return this.rgb().formatRgb()}function pi(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=ii.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?yi(e):3===n?new bi(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?gi(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?gi(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=ai.exec(t))?new bi(e[1],e[2],e[3],1):(e=oi.exec(t))?new bi(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=si.exec(t))?gi(e[1],e[2],e[3],e[4]):(e=ci.exec(t))?gi(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=ui.exec(t))?ki(e[1],e[2]/100,e[3]/100,1):(e=li.exec(t))?ki(e[1],e[2]/100,e[3]/100,e[4]):hi.hasOwnProperty(t)?yi(hi[t]):"transparent"===t?new bi(NaN,NaN,NaN,0):null}function yi(t){return new bi(t>>16&255,t>>8&255,255&t,1)}function gi(t,e,n,r){return r<=0&&(t=e=n=NaN),new bi(t,e,n,r)}function mi(t){return t instanceof Kr||(t=pi(t)),t?new bi((t=t.rgb()).r,t.g,t.b,t.opacity):new bi}function vi(t,e,n,r){return 1===arguments.length?mi(t):new bi(t,e,n,null==r?1:r)}function bi(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function _i(){return"#"+wi(this.r)+wi(this.g)+wi(this.b)}function xi(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function wi(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function ki(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new Ei(t,e,n,r)}function Ti(t){if(t instanceof Ei)return new Ei(t.h,t.s,t.l,t.opacity);if(t instanceof Kr||(t=pi(t)),!t)return new Ei;if(t instanceof Ei)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new Ei(o,s,c,t.opacity)}function Ei(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function Ci(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}function Si(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}Zr(Kr,pi,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:fi,formatHex:fi,formatHsl:function(){return Ti(this).formatHsl()},formatRgb:di,toString:di}),Zr(bi,vi,Qr(Kr,{brighter:function(t){return t=null==t?ti:Math.pow(ti,t),new bi(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Jr:Math.pow(Jr,t),new bi(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:_i,formatHex:_i,formatRgb:xi,toString:xi})),Zr(Ei,(function(t,e,n,r){return 1===arguments.length?Ti(t):new Ei(t,e,n,null==r?1:r)}),Qr(Kr,{brighter:function(t){return t=null==t?ti:Math.pow(ti,t),new Ei(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Jr:Math.pow(Jr,t),new Ei(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new bi(Ci(t>=240?t-240:t+120,i,r),Ci(t,i,r),Ci(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const Ai=t=>()=>t;function Mi(t,e){var n=e-t;return n?function(t,e){return function(n){return t+n*e}}(t,n):Ai(isNaN(t)?e:t)}const Ni=function t(e){var n=function(t){return 1==(t=+t)?Mi:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):Ai(isNaN(e)?n:e)}}(e);function r(t,e){var r=n((t=vi(t)).r,(e=vi(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=Mi(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function Di(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=vi(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}function Oi(t,e){var n,r=e?e.length:0,i=t?Math.min(r,t.length):0,a=new Array(i),o=new Array(r);for(n=0;n<i;++n)a[n]=Yi(t[n],e[n]);for(;n<r;++n)o[n]=e[n];return function(t){for(n=0;n<i;++n)o[n]=a[n](t);return o}}function Bi(t,e){var n=new Date;return t=+t,e=+e,function(r){return n.setTime(t*(1-r)+e*r),n}}function Li(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}function Ii(t,e){var n,r={},i={};for(n in null!==t&&"object"==typeof t||(t={}),null!==e&&"object"==typeof e||(e={}),e)n in t?r[n]=Yi(t[n],e[n]):i[n]=e[n];return function(t){for(n in r)i[n]=r[n](t);return i}}Di((function(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return Si((n-r/e)*e,o,i,a,s)}})),Di((function(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return Si((n-r/e)*e,i,a,o,s)}}));var Ri=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Fi=new RegExp(Ri.source,"g");function Pi(t,e){var n,r,i,a=Ri.lastIndex=Fi.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=Ri.exec(t))&&(r=Fi.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:Li(n,r)})),a=Fi.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})}function ji(t,e){e||(e=[]);var n,r=t?Math.min(e.length,t.length):0,i=e.slice();return function(a){for(n=0;n<r;++n)i[n]=t[n]*(1-a)+e[n]*a;return i}}function Yi(t,e){var n,r,i=typeof e;return null==e||"boolean"===i?Ai(e):("number"===i?Li:"string"===i?(n=pi(e))?(e=n,Ni):Pi:e instanceof pi?Ni:e instanceof Date?Bi:(r=e,!ArrayBuffer.isView(r)||r instanceof DataView?Array.isArray(e)?Oi:"function"!=typeof e.valueOf&&"function"!=typeof e.toString||isNaN(e)?Ii:Li:ji))(t,e)}function zi(t,e){return t=+t,e=+e,function(n){return Math.round(t*(1-n)+e*n)}}function Ui(t){return+t}var qi=[0,1];function Hi(t){return t}function $i(t,e){return(e-=t=+t)?function(n){return(n-t)/e}:(n=isNaN(e)?NaN:.5,function(){return n});var n}function Wi(t,e,n){var r=t[0],i=t[1],a=e[0],o=e[1];return i<r?(r=$i(i,r),a=n(o,a)):(r=$i(r,i),a=n(a,o)),function(t){return a(r(t))}}function Vi(t,e,n){var r=Math.min(t.length,e.length)-1,i=new Array(r),a=new Array(r),o=-1;for(t[r]<t[0]&&(t=t.slice().reverse(),e=e.slice().reverse());++o<r;)i[o]=$i(t[o],t[o+1]),a[o]=n(e[o],e[o+1]);return function(e){var n=Xr(t,e,1,r)-1;return a[n](i[n](e))}}function Gi(t,e){return e.domain(t.domain()).range(t.range()).interpolate(t.interpolate()).clamp(t.clamp()).unknown(t.unknown())}function Xi(){return function(){var t,e,n,r,i,a,o=qi,s=qi,c=Yi,u=Hi;function l(){var t,e,n,c=Math.min(o.length,s.length);return u!==Hi&&(t=o[0],e=o[c-1],t>e&&(n=t,t=e,e=n),u=function(n){return Math.max(t,Math.min(e,n))}),r=c>2?Vi:Wi,i=a=null,h}function h(e){return null==e||isNaN(e=+e)?n:(i||(i=r(o.map(t),s,c)))(t(u(e)))}return h.invert=function(n){return u(e((a||(a=r(s,o.map(t),Li)))(n)))},h.domain=function(t){return arguments.length?(o=Array.from(t,Ui),l()):o.slice()},h.range=function(t){return arguments.length?(s=Array.from(t),l()):s.slice()},h.rangeRound=function(t){return s=Array.from(t),c=zi,l()},h.clamp=function(t){return arguments.length?(u=!!t||Hi,l()):u!==Hi},h.interpolate=function(t){return arguments.length?(c=t,l()):c},h.unknown=function(t){return arguments.length?(n=t,h):n},function(n,r){return t=n,e=r,l()}}()(Hi,Hi)}function Zi(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t)}return this}var Qi,Ki=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Ji(t){if(!(e=Ki.exec(t)))throw new Error("invalid format: "+t);var e;return new ta({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}function ta(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function ea(t,e){if((n=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var n,r=t.slice(0,n);return[r.length>1?r[0]+r.slice(2):r,+t.slice(n+1)]}function na(t){return(t=ea(Math.abs(t)))?t[1]:NaN}function ra(t,e){var n=ea(t,e);if(!n)return t+"";var r=n[0],i=n[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}Ji.prototype=ta.prototype,ta.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};const ia={"%":(t,e)=>(100*t).toFixed(e),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,e)=>t.toExponential(e),f:(t,e)=>t.toFixed(e),g:(t,e)=>t.toPrecision(e),o:t=>Math.round(t).toString(8),p:(t,e)=>ra(100*t,e),r:ra,s:function(t,e){var n=ea(t,e);if(!n)return t+"";var r=n[0],i=n[1],a=i-(Qi=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,o=r.length;return a===o?r:a>o?r+new Array(a-o+1).join("0"):a>0?r.slice(0,a)+"."+r.slice(a):"0."+new Array(1-a).join("0")+ea(t,Math.max(0,e+a-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function aa(t){return t}var oa,sa,ca,ua=Array.prototype.map,la=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function ha(t){var e=t.domain;return t.ticks=function(t){var n=e();return function(t,e,n){var r,i,a,o,s=-1;if(n=+n,(t=+t)==(e=+e)&&n>0)return[t];if((r=e<t)&&(i=t,t=e,e=i),0===(o=qr(t,e,n))||!isFinite(o))return[];if(o>0){let n=Math.round(t/o),r=Math.round(e/o);for(n*o<t&&++n,r*o>e&&--r,a=new Array(i=r-n+1);++s<i;)a[s]=(n+s)*o}else{o=-o;let n=Math.round(t*o),r=Math.round(e*o);for(n/o<t&&++n,r/o>e&&--r,a=new Array(i=r-n+1);++s<i;)a[s]=(n+s)/o}return r&&a.reverse(),a}(n[0],n[n.length-1],null==t?10:t)},t.tickFormat=function(t,n){var r=e();return function(t,e,n,r){var i,a=Hr(t,e,n);switch((r=Ji(null==r?",f":r)).type){case"s":var o=Math.max(Math.abs(t),Math.abs(e));return null!=r.precision||isNaN(i=function(t,e){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(na(e)/3)))-na(Math.abs(t)))}(a,o))||(r.precision=i),ca(r,o);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=function(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,na(e)-na(t))+1}(a,Math.max(Math.abs(t),Math.abs(e))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=function(t){return Math.max(0,-na(Math.abs(t)))}(a))||(r.precision=i-2*("%"===r.type))}return sa(r)}(r[0],r[r.length-1],null==t?10:t,n)},t.nice=function(n){null==n&&(n=10);var r,i,a=e(),o=0,s=a.length-1,c=a[o],u=a[s],l=10;for(u<c&&(i=c,c=u,u=i,i=o,o=s,s=i);l-- >0;){if((i=qr(c,u,n))===r)return a[o]=c,a[s]=u,e(a);if(i>0)c=Math.floor(c/i)*i,u=Math.ceil(u/i)*i;else{if(!(i<0))break;c=Math.ceil(c*i)/i,u=Math.floor(u*i)/i}r=i}return t},t}function fa(){var t=Xi();return t.copy=function(){return Gi(t,fa())},Zi.apply(t,arguments),ha(t)}oa=function(t){var e,n,r=void 0===t.grouping||void 0===t.thousands?aa:(e=ua.call(t.grouping,Number),n=t.thousands+"",function(t,r){for(var i=t.length,a=[],o=0,s=e[0],c=0;i>0&&s>0&&(c+s+1>r&&(s=Math.max(1,r-c)),a.push(t.substring(i-=s,i+s)),!((c+=s+1)>r));)s=e[o=(o+1)%e.length];return a.reverse().join(n)}),i=void 0===t.currency?"":t.currency[0]+"",a=void 0===t.currency?"":t.currency[1]+"",o=void 0===t.decimal?".":t.decimal+"",s=void 0===t.numerals?aa:function(t){return function(e){return e.replace(/[0-9]/g,(function(e){return t[+e]}))}}(ua.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",u=void 0===t.minus?"−":t.minus+"",l=void 0===t.nan?"NaN":t.nan+"";function h(t){var e=(t=Ji(t)).fill,n=t.align,h=t.sign,f=t.symbol,d=t.zero,p=t.width,y=t.comma,g=t.precision,m=t.trim,v=t.type;"n"===v?(y=!0,v="g"):ia[v]||(void 0===g&&(g=12),m=!0,v="g"),(d||"0"===e&&"="===n)&&(d=!0,e="0",n="=");var b="$"===f?i:"#"===f&&/[boxX]/.test(v)?"0"+v.toLowerCase():"",_="$"===f?a:/[%p]/.test(v)?c:"",x=ia[v],w=/[defgprs%]/.test(v);function k(t){var i,a,c,f=b,k=_;if("c"===v)k=x(t)+k,t="";else{var T=(t=+t)<0||1/t<0;if(t=isNaN(t)?l:x(Math.abs(t),g),m&&(t=function(t){t:for(var e,n=t.length,r=1,i=-1;r<n;++r)switch(t[r]){case".":i=e=r;break;case"0":0===i&&(i=r),e=r;break;default:if(!+t[r])break t;i>0&&(i=0)}return i>0?t.slice(0,i)+t.slice(e+1):t}(t)),T&&0==+t&&"+"!==h&&(T=!1),f=(T?"("===h?h:u:"-"===h||"("===h?"":h)+f,k=("s"===v?la[8+Qi/3]:"")+k+(T&&"("===h?")":""),w)for(i=-1,a=t.length;++i<a;)if(48>(c=t.charCodeAt(i))||c>57){k=(46===c?o+t.slice(i+1):t.slice(i))+k,t=t.slice(0,i);break}}y&&!d&&(t=r(t,1/0));var E=f.length+t.length+k.length,C=E<p?new Array(p-E+1).join(e):"";switch(y&&d&&(t=r(C+t,C.length?p-k.length:1/0),C=""),n){case"<":t=f+t+k+C;break;case"=":t=f+C+t+k;break;case"^":t=C.slice(0,E=C.length>>1)+f+t+k+C.slice(E);break;default:t=C+f+t+k}return s(t)}return g=void 0===g?6:/[gprs]/.test(v)?Math.max(1,Math.min(21,g)):Math.max(0,Math.min(20,g)),k.toString=function(){return t+""},k}return{format:h,formatPrefix:function(t,e){var n=h(((t=Ji(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(na(e)/3))),i=Math.pow(10,-r),a=la[8+r/3];return function(t){return n(i*t)+a}}}}({thousands:",",grouping:[3],currency:["$",""]}),sa=oa.format,ca=oa.formatPrefix;class da extends Map{constructor(t,e=ya){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:e}}),null!=t)for(const[e,n]of t)this.set(e,n)}get(t){return super.get(pa(this,t))}has(t){return super.has(pa(this,t))}set(t,e){return super.set(function({_intern:t,_key:e},n){const r=e(n);return t.has(r)?t.get(r):(t.set(r,n),n)}(this,t),e)}delete(t){return super.delete(function({_intern:t,_key:e},n){const r=e(n);return t.has(r)&&(n=t.get(r),t.delete(r)),n}(this,t))}}function pa({_intern:t,_key:e},n){const r=e(n);return t.has(r)?t.get(r):n}function ya(t){return null!==t&&"object"==typeof t?t.valueOf():t}Set;const ga=Symbol("implicit");function ma(){var t=new da,e=[],n=[],r=ga;function i(i){let a=t.get(i);if(void 0===a){if(r!==ga)return r;t.set(i,a=e.push(i)-1)}return n[a%n.length]}return i.domain=function(n){if(!arguments.length)return e.slice();e=[],t=new da;for(const r of n)t.has(r)||t.set(r,e.push(r)-1);return i},i.range=function(t){return arguments.length?(n=Array.from(t),i):n.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return ma(e,n).unknown(r)},Zi.apply(i,arguments),i}const va=1e3,ba=6e4,_a=36e5,xa=864e5,wa=6048e5,ka=31536e6;var Ta=new Date,Ea=new Date;function Ca(t,e,n,r){function i(e){return t(e=0===arguments.length?new Date:new Date(+e)),e}return i.floor=function(e){return t(e=new Date(+e)),e},i.ceil=function(n){return t(n=new Date(n-1)),e(n,1),t(n),n},i.round=function(t){var e=i(t),n=i.ceil(t);return t-e<n-t?e:n},i.offset=function(t,n){return e(t=new Date(+t),null==n?1:Math.floor(n)),t},i.range=function(n,r,a){var o,s=[];if(n=i.ceil(n),a=null==a?1:Math.floor(a),!(n<r&&a>0))return s;do{s.push(o=new Date(+n)),e(n,a),t(n)}while(o<n&&n<r);return s},i.filter=function(n){return Ca((function(e){if(e>=e)for(;t(e),!n(e);)e.setTime(e-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;e(t,-1),!n(t););else for(;--r>=0;)for(;e(t,1),!n(t););}))},n&&(i.count=function(e,r){return Ta.setTime(+e),Ea.setTime(+r),t(Ta),t(Ea),Math.floor(n(Ta,Ea))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(e){return r(e)%t==0}:function(e){return i.count(0,e)%t==0}):i:null}),i}var Sa=Ca((function(){}),(function(t,e){t.setTime(+t+e)}),(function(t,e){return e-t}));Sa.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?Ca((function(e){e.setTime(Math.floor(e/t)*t)}),(function(e,n){e.setTime(+e+n*t)}),(function(e,n){return(n-e)/t})):Sa:null};const Aa=Sa;Sa.range;var Ma=Ca((function(t){t.setTime(t-t.getMilliseconds())}),(function(t,e){t.setTime(+t+e*va)}),(function(t,e){return(e-t)/va}),(function(t){return t.getUTCSeconds()}));const Na=Ma;Ma.range;var Da=Ca((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*va)}),(function(t,e){t.setTime(+t+e*ba)}),(function(t,e){return(e-t)/ba}),(function(t){return t.getMinutes()}));const Oa=Da;Da.range;var Ba=Ca((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*va-t.getMinutes()*ba)}),(function(t,e){t.setTime(+t+e*_a)}),(function(t,e){return(e-t)/_a}),(function(t){return t.getHours()}));const La=Ba;Ba.range;var Ia=Ca((t=>t.setHours(0,0,0,0)),((t,e)=>t.setDate(t.getDate()+e)),((t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*ba)/xa),(t=>t.getDate()-1));const Ra=Ia;function Fa(t){return Ca((function(e){e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+7*e)}),(function(t,e){return(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*ba)/wa}))}Ia.range;var Pa=Fa(0),ja=Fa(1),Ya=Fa(2),za=Fa(3),Ua=Fa(4),qa=Fa(5),Ha=Fa(6),$a=(Pa.range,ja.range,Ya.range,za.range,Ua.range,qa.range,Ha.range,Ca((function(t){t.setDate(1),t.setHours(0,0,0,0)}),(function(t,e){t.setMonth(t.getMonth()+e)}),(function(t,e){return e.getMonth()-t.getMonth()+12*(e.getFullYear()-t.getFullYear())}),(function(t){return t.getMonth()})));const Wa=$a;$a.range;var Va=Ca((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,e){t.setFullYear(t.getFullYear()+e)}),(function(t,e){return e.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));Va.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Ca((function(e){e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)}),(function(e,n){e.setFullYear(e.getFullYear()+n*t)})):null};const Ga=Va;Va.range;var Xa=Ca((function(t){t.setUTCSeconds(0,0)}),(function(t,e){t.setTime(+t+e*ba)}),(function(t,e){return(e-t)/ba}),(function(t){return t.getUTCMinutes()}));const Za=Xa;Xa.range;var Qa=Ca((function(t){t.setUTCMinutes(0,0,0)}),(function(t,e){t.setTime(+t+e*_a)}),(function(t,e){return(e-t)/_a}),(function(t){return t.getUTCHours()}));const Ka=Qa;Qa.range;var Ja=Ca((function(t){t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+e)}),(function(t,e){return(e-t)/xa}),(function(t){return t.getUTCDate()-1}));const to=Ja;function eo(t){return Ca((function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+7*e)}),(function(t,e){return(e-t)/wa}))}Ja.range;var no=eo(0),ro=eo(1),io=eo(2),ao=eo(3),oo=eo(4),so=eo(5),co=eo(6),uo=(no.range,ro.range,io.range,ao.range,oo.range,so.range,co.range,Ca((function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCMonth(t.getUTCMonth()+e)}),(function(t,e){return e.getUTCMonth()-t.getUTCMonth()+12*(e.getUTCFullYear()-t.getUTCFullYear())}),(function(t){return t.getUTCMonth()})));const lo=uo;uo.range;var ho=Ca((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCFullYear(t.getUTCFullYear()+e)}),(function(t,e){return e.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));ho.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Ca((function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)}),(function(e,n){e.setUTCFullYear(e.getUTCFullYear()+n*t)})):null};const fo=ho;function po(t,e,n,r,i,a){const o=[[Na,1,va],[Na,5,5e3],[Na,15,15e3],[Na,30,3e4],[a,1,ba],[a,5,3e5],[a,15,9e5],[a,30,18e5],[i,1,_a],[i,3,108e5],[i,6,216e5],[i,12,432e5],[r,1,xa],[r,2,1728e5],[n,1,wa],[e,1,2592e6],[e,3,7776e6],[t,1,ka]];function s(e,n,r){const i=Math.abs(n-e)/r,a=Wr((([,,t])=>t)).right(o,i);if(a===o.length)return t.every(Hr(e/ka,n/ka,r));if(0===a)return Aa.every(Math.max(Hr(e,n,r),1));const[s,c]=o[i/o[a-1][2]<o[a][2]/i?a-1:a];return s.every(c)}return[function(t,e,n){const r=e<t;r&&([t,e]=[e,t]);const i=n&&"function"==typeof n.range?n:s(t,e,n),a=i?i.range(t,+e+1):[];return r?a.reverse():a},s]}ho.range;const[yo,go]=po(fo,lo,no,to,Ka,Za),[mo,vo]=po(Ga,Wa,Pa,Ra,La,Oa);function bo(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function _o(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function xo(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}var wo,ko,To={"-":"",_:" ",0:"0"},Eo=/^\s*\d+/,Co=/^%/,So=/[\\^$*+?|[\]().{}]/g;function Ao(t,e,n){var r=t<0?"-":"",i=(r?-t:t)+"",a=i.length;return r+(a<n?new Array(n-a+1).join(e)+i:i)}function Mo(t){return t.replace(So,"\\$&")}function No(t){return new RegExp("^(?:"+t.map(Mo).join("|")+")","i")}function Do(t){return new Map(t.map(((t,e)=>[t.toLowerCase(),e])))}function Oo(t,e,n){var r=Eo.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r[0].length):-1}function Bo(t,e,n){var r=Eo.exec(e.slice(n,n+1));return r?(t.u=+r[0],n+r[0].length):-1}function Lo(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.U=+r[0],n+r[0].length):-1}function Io(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.V=+r[0],n+r[0].length):-1}function Ro(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.W=+r[0],n+r[0].length):-1}function Fo(t,e,n){var r=Eo.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r[0].length):-1}function Po(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),n+r[0].length):-1}function jo(t,e,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function Yo(t,e,n){var r=Eo.exec(e.slice(n,n+1));return r?(t.q=3*r[0]-3,n+r[0].length):-1}function zo(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+r[0].length):-1}function Uo(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r[0].length):-1}function qo(t,e,n){var r=Eo.exec(e.slice(n,n+3));return r?(t.m=0,t.d=+r[0],n+r[0].length):-1}function Ho(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r[0].length):-1}function $o(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r[0].length):-1}function Wo(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r[0].length):-1}function Vo(t,e,n){var r=Eo.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r[0].length):-1}function Go(t,e,n){var r=Eo.exec(e.slice(n,n+6));return r?(t.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function Xo(t,e,n){var r=Co.exec(e.slice(n,n+1));return r?n+r[0].length:-1}function Zo(t,e,n){var r=Eo.exec(e.slice(n));return r?(t.Q=+r[0],n+r[0].length):-1}function Qo(t,e,n){var r=Eo.exec(e.slice(n));return r?(t.s=+r[0],n+r[0].length):-1}function Ko(t,e){return Ao(t.getDate(),e,2)}function Jo(t,e){return Ao(t.getHours(),e,2)}function ts(t,e){return Ao(t.getHours()%12||12,e,2)}function es(t,e){return Ao(1+Ra.count(Ga(t),t),e,3)}function ns(t,e){return Ao(t.getMilliseconds(),e,3)}function rs(t,e){return ns(t,e)+"000"}function is(t,e){return Ao(t.getMonth()+1,e,2)}function as(t,e){return Ao(t.getMinutes(),e,2)}function os(t,e){return Ao(t.getSeconds(),e,2)}function ss(t){var e=t.getDay();return 0===e?7:e}function cs(t,e){return Ao(Pa.count(Ga(t)-1,t),e,2)}function us(t){var e=t.getDay();return e>=4||0===e?Ua(t):Ua.ceil(t)}function ls(t,e){return t=us(t),Ao(Ua.count(Ga(t),t)+(4===Ga(t).getDay()),e,2)}function hs(t){return t.getDay()}function fs(t,e){return Ao(ja.count(Ga(t)-1,t),e,2)}function ds(t,e){return Ao(t.getFullYear()%100,e,2)}function ps(t,e){return Ao((t=us(t)).getFullYear()%100,e,2)}function ys(t,e){return Ao(t.getFullYear()%1e4,e,4)}function gs(t,e){var n=t.getDay();return Ao((t=n>=4||0===n?Ua(t):Ua.ceil(t)).getFullYear()%1e4,e,4)}function ms(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+Ao(e/60|0,"0",2)+Ao(e%60,"0",2)}function vs(t,e){return Ao(t.getUTCDate(),e,2)}function bs(t,e){return Ao(t.getUTCHours(),e,2)}function _s(t,e){return Ao(t.getUTCHours()%12||12,e,2)}function xs(t,e){return Ao(1+to.count(fo(t),t),e,3)}function ws(t,e){return Ao(t.getUTCMilliseconds(),e,3)}function ks(t,e){return ws(t,e)+"000"}function Ts(t,e){return Ao(t.getUTCMonth()+1,e,2)}function Es(t,e){return Ao(t.getUTCMinutes(),e,2)}function Cs(t,e){return Ao(t.getUTCSeconds(),e,2)}function Ss(t){var e=t.getUTCDay();return 0===e?7:e}function As(t,e){return Ao(no.count(fo(t)-1,t),e,2)}function Ms(t){var e=t.getUTCDay();return e>=4||0===e?oo(t):oo.ceil(t)}function Ns(t,e){return t=Ms(t),Ao(oo.count(fo(t),t)+(4===fo(t).getUTCDay()),e,2)}function Ds(t){return t.getUTCDay()}function Os(t,e){return Ao(ro.count(fo(t)-1,t),e,2)}function Bs(t,e){return Ao(t.getUTCFullYear()%100,e,2)}function Ls(t,e){return Ao((t=Ms(t)).getUTCFullYear()%100,e,2)}function Is(t,e){return Ao(t.getUTCFullYear()%1e4,e,4)}function Rs(t,e){var n=t.getUTCDay();return Ao((t=n>=4||0===n?oo(t):oo.ceil(t)).getUTCFullYear()%1e4,e,4)}function Fs(){return"+0000"}function Ps(){return"%"}function js(t){return+t}function Ys(t){return Math.floor(+t/1e3)}function zs(t){return new Date(t)}function Us(t){return t instanceof Date?+t:+new Date(+t)}function qs(t,e,n,r,i,a,o,s,c,u){var l=Xi(),h=l.invert,f=l.domain,d=u(".%L"),p=u(":%S"),y=u("%I:%M"),g=u("%I %p"),m=u("%a %d"),v=u("%b %d"),b=u("%B"),_=u("%Y");function x(t){return(c(t)<t?d:s(t)<t?p:o(t)<t?y:a(t)<t?g:r(t)<t?i(t)<t?m:v:n(t)<t?b:_)(t)}return l.invert=function(t){return new Date(h(t))},l.domain=function(t){return arguments.length?f(Array.from(t,Us)):f().map(zs)},l.ticks=function(e){var n=f();return t(n[0],n[n.length-1],null==e?10:e)},l.tickFormat=function(t,e){return null==e?x:u(e)},l.nice=function(t){var n=f();return t&&"function"==typeof t.range||(t=e(n[0],n[n.length-1],null==t?10:t)),t?f(function(t,e){var n,r=0,i=(t=t.slice()).length-1,a=t[r],o=t[i];return o<a&&(n=r,r=i,i=n,n=a,a=o,o=n),t[r]=e.floor(a),t[i]=e.ceil(o),t}(n,t)):l},l.copy=function(){return Gi(l,qs(t,e,n,r,i,a,o,s,c,u))},l}function Hs(){}function $s(t){return null==t?Hs:function(){return this.querySelector(t)}}function Ws(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function Vs(){return[]}function Gs(t){return null==t?Vs:function(){return this.querySelectorAll(t)}}function Xs(t){return function(){return this.matches(t)}}function Zs(t){return function(e){return e.matches(t)}}wo=function(t){var e=t.dateTime,n=t.date,r=t.time,i=t.periods,a=t.days,o=t.shortDays,s=t.months,c=t.shortMonths,u=No(i),l=Do(i),h=No(a),f=Do(a),d=No(o),p=Do(o),y=No(s),g=Do(s),m=No(c),v=Do(c),b={a:function(t){return o[t.getDay()]},A:function(t){return a[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return s[t.getMonth()]},c:null,d:Ko,e:Ko,f:rs,g:ps,G:gs,H:Jo,I:ts,j:es,L:ns,m:is,M:as,p:function(t){return i[+(t.getHours()>=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:js,s:Ys,S:os,u:ss,U:cs,V:ls,w:hs,W:fs,x:null,X:null,y:ds,Y:ys,Z:ms,"%":Ps},_={a:function(t){return o[t.getUTCDay()]},A:function(t){return a[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return s[t.getUTCMonth()]},c:null,d:vs,e:vs,f:ks,g:Ls,G:Rs,H:bs,I:_s,j:xs,L:ws,m:Ts,M:Es,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:js,s:Ys,S:Cs,u:Ss,U:As,V:Ns,w:Ds,W:Os,x:null,X:null,y:Bs,Y:Is,Z:Fs,"%":Ps},x={a:function(t,e,n){var r=d.exec(e.slice(n));return r?(t.w=p.get(r[0].toLowerCase()),n+r[0].length):-1},A:function(t,e,n){var r=h.exec(e.slice(n));return r?(t.w=f.get(r[0].toLowerCase()),n+r[0].length):-1},b:function(t,e,n){var r=m.exec(e.slice(n));return r?(t.m=v.get(r[0].toLowerCase()),n+r[0].length):-1},B:function(t,e,n){var r=y.exec(e.slice(n));return r?(t.m=g.get(r[0].toLowerCase()),n+r[0].length):-1},c:function(t,n,r){return T(t,e,n,r)},d:Uo,e:Uo,f:Go,g:Po,G:Fo,H:Ho,I:Ho,j:qo,L:Vo,m:zo,M:$o,p:function(t,e,n){var r=u.exec(e.slice(n));return r?(t.p=l.get(r[0].toLowerCase()),n+r[0].length):-1},q:Yo,Q:Zo,s:Qo,S:Wo,u:Bo,U:Lo,V:Io,w:Oo,W:Ro,x:function(t,e,r){return T(t,n,e,r)},X:function(t,e,n){return T(t,r,e,n)},y:Po,Y:Fo,Z:jo,"%":Xo};function w(t,e){return function(n){var r,i,a,o=[],s=-1,c=0,u=t.length;for(n instanceof Date||(n=new Date(+n));++s<u;)37===t.charCodeAt(s)&&(o.push(t.slice(c,s)),null!=(i=To[r=t.charAt(++s)])?r=t.charAt(++s):i="e"===r?" ":"0",(a=e[r])&&(r=a(n,i)),o.push(r),c=s+1);return o.push(t.slice(c,s)),o.join("")}}function k(t,e){return function(n){var r,i,a=xo(1900,void 0,1);if(T(a,t,n+="",0)!=n.length)return null;if("Q"in a)return new Date(a.Q);if("s"in a)return new Date(1e3*a.s+("L"in a?a.L:0));if(e&&!("Z"in a)&&(a.Z=0),"p"in a&&(a.H=a.H%12+12*a.p),void 0===a.m&&(a.m="q"in a?a.q:0),"V"in a){if(a.V<1||a.V>53)return null;"w"in a||(a.w=1),"Z"in a?(i=(r=_o(xo(a.y,0,1))).getUTCDay(),r=i>4||0===i?ro.ceil(r):ro(r),r=to.offset(r,7*(a.V-1)),a.y=r.getUTCFullYear(),a.m=r.getUTCMonth(),a.d=r.getUTCDate()+(a.w+6)%7):(i=(r=bo(xo(a.y,0,1))).getDay(),r=i>4||0===i?ja.ceil(r):ja(r),r=Ra.offset(r,7*(a.V-1)),a.y=r.getFullYear(),a.m=r.getMonth(),a.d=r.getDate()+(a.w+6)%7)}else("W"in a||"U"in a)&&("w"in a||(a.w="u"in a?a.u%7:"W"in a?1:0),i="Z"in a?_o(xo(a.y,0,1)).getUTCDay():bo(xo(a.y,0,1)).getDay(),a.m=0,a.d="W"in a?(a.w+6)%7+7*a.W-(i+5)%7:a.w+7*a.U-(i+6)%7);return"Z"in a?(a.H+=a.Z/100|0,a.M+=a.Z%100,_o(a)):bo(a)}}function T(t,e,n,r){for(var i,a,o=0,s=e.length,c=n.length;o<s;){if(r>=c)return-1;if(37===(i=e.charCodeAt(o++))){if(i=e.charAt(o++),!(a=x[i in To?e.charAt(o++):i])||(r=a(t,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}return b.x=w(n,b),b.X=w(r,b),b.c=w(e,b),_.x=w(n,_),_.X=w(r,_),_.c=w(e,_),{format:function(t){var e=w(t+="",b);return e.toString=function(){return t},e},parse:function(t){var e=k(t+="",!1);return e.toString=function(){return t},e},utcFormat:function(t){var e=w(t+="",_);return e.toString=function(){return t},e},utcParse:function(t){var e=k(t+="",!0);return e.toString=function(){return t},e}}}({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}),ko=wo.format,wo.parse,wo.utcFormat,wo.utcParse;var Qs=Array.prototype.find;function Ks(){return this.firstElementChild}var Js=Array.prototype.filter;function tc(){return Array.from(this.children)}function ec(t){return new Array(t.length)}function nc(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}function rc(t){return function(){return t}}function ic(t,e,n,r,i,a){for(var o,s=0,c=e.length,u=a.length;s<u;++s)(o=e[s])?(o.__data__=a[s],r[s]=o):n[s]=new nc(t,a[s]);for(;s<c;++s)(o=e[s])&&(i[s]=o)}function ac(t,e,n,r,i,a,o){var s,c,u,l=new Map,h=e.length,f=a.length,d=new Array(h);for(s=0;s<h;++s)(c=e[s])&&(d[s]=u=o.call(c,c.__data__,s,e)+"",l.has(u)?i[s]=c:l.set(u,c));for(s=0;s<f;++s)u=o.call(t,a[s],s,a)+"",(c=l.get(u))?(r[s]=c,c.__data__=a[s],l.delete(u)):n[s]=new nc(t,a[s]);for(s=0;s<h;++s)(c=e[s])&&l.get(d[s])===c&&(i[s]=c)}function oc(t){return t.__data__}function sc(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function cc(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}nc.prototype={constructor:nc,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,e){return this._parent.insertBefore(t,e)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var uc="http://www.w3.org/1999/xhtml";const lc={svg:"http://www.w3.org/2000/svg",xhtml:uc,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function hc(t){var e=t+="",n=e.indexOf(":");return n>=0&&"xmlns"!==(e=t.slice(0,n))&&(t=t.slice(n+1)),lc.hasOwnProperty(e)?{space:lc[e],local:t}:t}function fc(t){return function(){this.removeAttribute(t)}}function dc(t){return function(){this.removeAttributeNS(t.space,t.local)}}function pc(t,e){return function(){this.setAttribute(t,e)}}function yc(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function gc(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}}function mc(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}function vc(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function bc(t){return function(){this.style.removeProperty(t)}}function _c(t,e,n){return function(){this.style.setProperty(t,e,n)}}function xc(t,e,n){return function(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}}function wc(t,e){return t.style.getPropertyValue(e)||vc(t).getComputedStyle(t,null).getPropertyValue(e)}function kc(t){return function(){delete this[t]}}function Tc(t,e){return function(){this[t]=e}}function Ec(t,e){return function(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}}function Cc(t){return t.trim().split(/^|\s+/)}function Sc(t){return t.classList||new Ac(t)}function Ac(t){this._node=t,this._names=Cc(t.getAttribute("class")||"")}function Mc(t,e){for(var n=Sc(t),r=-1,i=e.length;++r<i;)n.add(e[r])}function Nc(t,e){for(var n=Sc(t),r=-1,i=e.length;++r<i;)n.remove(e[r])}function Dc(t){return function(){Mc(this,t)}}function Oc(t){return function(){Nc(this,t)}}function Bc(t,e){return function(){(e.apply(this,arguments)?Mc:Nc)(this,t)}}function Lc(){this.textContent=""}function Ic(t){return function(){this.textContent=t}}function Rc(t){return function(){var e=t.apply(this,arguments);this.textContent=null==e?"":e}}function Fc(){this.innerHTML=""}function Pc(t){return function(){this.innerHTML=t}}function jc(t){return function(){var e=t.apply(this,arguments);this.innerHTML=null==e?"":e}}function Yc(){this.nextSibling&&this.parentNode.appendChild(this)}function zc(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function Uc(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===uc&&e.documentElement.namespaceURI===uc?e.createElement(t):e.createElementNS(n,t)}}function qc(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Hc(t){var e=hc(t);return(e.local?qc:Uc)(e)}function $c(){return null}function Wc(){var t=this.parentNode;t&&t.removeChild(this)}function Vc(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function Gc(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function Xc(t){return t.trim().split(/^|\s+/).map((function(t){var e="",n=t.indexOf(".");return n>=0&&(e=t.slice(n+1),t=t.slice(0,n)),{type:t,name:e}}))}function Zc(t){return function(){var e=this.__on;if(e){for(var n,r=0,i=-1,a=e.length;r<a;++r)n=e[r],t.type&&n.type!==t.type||n.name!==t.name?e[++i]=n:this.removeEventListener(n.type,n.listener,n.options);++i?e.length=i:delete this.__on}}}function Qc(t,e,n){return function(){var r,i=this.__on,a=function(t){return function(e){t.call(this,e,this.__data__)}}(e);if(i)for(var o=0,s=i.length;o<s;++o)if((r=i[o]).type===t.type&&r.name===t.name)return this.removeEventListener(r.type,r.listener,r.options),this.addEventListener(r.type,r.listener=a,r.options=n),void(r.value=e);this.addEventListener(t.type,a,n),r={type:t.type,name:t.name,value:e,listener:a,options:n},i?i.push(r):this.__on=[r]}}function Kc(t,e,n){var r=vc(t),i=r.CustomEvent;"function"==typeof i?i=new i(e,n):(i=r.document.createEvent("Event"),n?(i.initEvent(e,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function Jc(t,e){return function(){return Kc(this,t,e)}}function tu(t,e){return function(){return Kc(this,t,e.apply(this,arguments))}}Ac.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var eu=[null];function nu(t,e){this._groups=t,this._parents=e}function ru(){return new nu([[document.documentElement]],eu)}nu.prototype=ru.prototype={constructor:nu,select:function(t){"function"!=typeof t&&(t=$s(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o,s=e[i],c=s.length,u=r[i]=new Array(c),l=0;l<c;++l)(a=s[l])&&(o=t.call(a,a.__data__,l,s))&&("__data__"in a&&(o.__data__=a.__data__),u[l]=o);return new nu(r,this._parents)},selectAll:function(t){t="function"==typeof t?function(t){return function(){return Ws(t.apply(this,arguments))}}(t):Gs(t);for(var e=this._groups,n=e.length,r=[],i=[],a=0;a<n;++a)for(var o,s=e[a],c=s.length,u=0;u<c;++u)(o=s[u])&&(r.push(t.call(o,o.__data__,u,s)),i.push(o));return new nu(r,i)},selectChild:function(t){return this.select(null==t?Ks:function(t){return function(){return Qs.call(this.children,t)}}("function"==typeof t?t:Zs(t)))},selectChildren:function(t){return this.selectAll(null==t?tc:function(t){return function(){return Js.call(this.children,t)}}("function"==typeof t?t:Zs(t)))},filter:function(t){"function"!=typeof t&&(t=Xs(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new nu(r,this._parents)},data:function(t,e){if(!arguments.length)return Array.from(this,oc);var n=e?ac:ic,r=this._parents,i=this._groups;"function"!=typeof t&&(t=rc(t));for(var a=i.length,o=new Array(a),s=new Array(a),c=new Array(a),u=0;u<a;++u){var l=r[u],h=i[u],f=h.length,d=sc(t.call(l,l&&l.__data__,u,r)),p=d.length,y=s[u]=new Array(p),g=o[u]=new Array(p),m=c[u]=new Array(f);n(l,h,y,g,m,d,e);for(var v,b,_=0,x=0;_<p;++_)if(v=y[_]){for(_>=x&&(x=_+1);!(b=g[x])&&++x<p;);v._next=b||null}}return(o=new nu(o,r))._enter=s,o._exit=c,o},enter:function(){return new nu(this._enter||this._groups.map(ec),this._parents)},exit:function(){return new nu(this._exit||this._groups.map(ec),this._parents)},join:function(t,e,n){var r=this.enter(),i=this,a=this.exit();return"function"==typeof t?(r=t(r))&&(r=r.selection()):r=r.append(t+""),null!=e&&(i=e(i))&&(i=i.selection()),null==n?a.remove():n(a),r&&i?r.merge(i).order():i},merge:function(t){for(var e=t.selection?t.selection():t,n=this._groups,r=e._groups,i=n.length,a=r.length,o=Math.min(i,a),s=new Array(i),c=0;c<o;++c)for(var u,l=n[c],h=r[c],f=l.length,d=s[c]=new Array(f),p=0;p<f;++p)(u=l[p]||h[p])&&(d[p]=u);for(;c<i;++c)s[c]=n[c];return new nu(s,this._parents)},selection:function(){return this},order:function(){for(var t=this._groups,e=-1,n=t.length;++e<n;)for(var r,i=t[e],a=i.length-1,o=i[a];--a>=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function e(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}t||(t=cc);for(var n=this._groups,r=n.length,i=new Array(r),a=0;a<r;++a){for(var o,s=n[a],c=s.length,u=i[a]=new Array(c),l=0;l<c;++l)(o=s[l])&&(u[l]=o);u.sort(e)}return new nu(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){return Array.from(this)},node:function(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r=t[e],i=0,a=r.length;i<a;++i){var o=r[i];if(o)return o}return null},size:function(){let t=0;for(const e of this)++t;return t},empty:function(){return!this.node()},each:function(t){for(var e=this._groups,n=0,r=e.length;n<r;++n)for(var i,a=e[n],o=0,s=a.length;o<s;++o)(i=a[o])&&t.call(i,i.__data__,o,a);return this},attr:function(t,e){var n=hc(t);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((null==e?n.local?dc:fc:"function"==typeof e?n.local?mc:gc:n.local?yc:pc)(n,e))},style:function(t,e,n){return arguments.length>1?this.each((null==e?bc:"function"==typeof e?xc:_c)(t,e,null==n?"":n)):wc(this.node(),t)},property:function(t,e){return arguments.length>1?this.each((null==e?kc:"function"==typeof e?Ec:Tc)(t,e)):this.node()[t]},classed:function(t,e){var n=Cc(t+"");if(arguments.length<2){for(var r=Sc(this.node()),i=-1,a=n.length;++i<a;)if(!r.contains(n[i]))return!1;return!0}return this.each(("function"==typeof e?Bc:e?Dc:Oc)(n,e))},text:function(t){return arguments.length?this.each(null==t?Lc:("function"==typeof t?Rc:Ic)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?Fc:("function"==typeof t?jc:Pc)(t)):this.node().innerHTML},raise:function(){return this.each(Yc)},lower:function(){return this.each(zc)},append:function(t){var e="function"==typeof t?t:Hc(t);return this.select((function(){return this.appendChild(e.apply(this,arguments))}))},insert:function(t,e){var n="function"==typeof t?t:Hc(t),r=null==e?$c:"function"==typeof e?e:$s(e);return this.select((function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(Wc)},clone:function(t){return this.select(t?Gc:Vc)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,e,n){var r,i,a=Xc(t+""),o=a.length;if(!(arguments.length<2)){for(s=e?Qc:Zc,r=0;r<o;++r)this.each(s(a[r],e,n));return this}var s=this.node().__on;if(s)for(var c,u=0,l=s.length;u<l;++u)for(r=0,c=s[u];r<o;++r)if((i=a[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,e){return this.each(("function"==typeof e?tu:Jc)(t,e))},[Symbol.iterator]:function*(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r,i=t[e],a=0,o=i.length;a<o;++a)(r=i[a])&&(yield r)}};const iu=ru;function au(t){return"string"==typeof t?new nu([[document.querySelector(t)]],[document.documentElement]):new nu([[t]],eu)}function ou(t){return"string"==typeof t?new nu([document.querySelectorAll(t)],[document.documentElement]):new nu([Ws(t)],eu)}const su=Math.PI,cu=2*su,uu=1e-6,lu=cu-uu;function hu(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function fu(){return new hu}hu.prototype=fu.prototype={constructor:hu,moveTo:function(t,e){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)},closePath:function(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},lineTo:function(t,e){this._+="L"+(this._x1=+t)+","+(this._y1=+e)},quadraticCurveTo:function(t,e,n,r){this._+="Q"+ +t+","+ +e+","+(this._x1=+n)+","+(this._y1=+r)},bezierCurveTo:function(t,e,n,r,i,a){this._+="C"+ +t+","+ +e+","+ +n+","+ +r+","+(this._x1=+i)+","+(this._y1=+a)},arcTo:function(t,e,n,r,i){t=+t,e=+e,n=+n,r=+r,i=+i;var a=this._x1,o=this._y1,s=n-t,c=r-e,u=a-t,l=o-e,h=u*u+l*l;if(i<0)throw new Error("negative radius: "+i);if(null===this._x1)this._+="M"+(this._x1=t)+","+(this._y1=e);else if(h>uu)if(Math.abs(l*s-c*u)>uu&&i){var f=n-a,d=r-o,p=s*s+c*c,y=f*f+d*d,g=Math.sqrt(p),m=Math.sqrt(h),v=i*Math.tan((su-Math.acos((p+h-y)/(2*g*m)))/2),b=v/m,_=v/g;Math.abs(b-1)>uu&&(this._+="L"+(t+b*u)+","+(e+b*l)),this._+="A"+i+","+i+",0,0,"+ +(l*f>u*d)+","+(this._x1=t+_*s)+","+(this._y1=e+_*c)}else this._+="L"+(this._x1=t)+","+(this._y1=e)},arc:function(t,e,n,r,i,a){t=+t,e=+e,a=!!a;var o=(n=+n)*Math.cos(r),s=n*Math.sin(r),c=t+o,u=e+s,l=1^a,h=a?r-i:i-r;if(n<0)throw new Error("negative radius: "+n);null===this._x1?this._+="M"+c+","+u:(Math.abs(this._x1-c)>uu||Math.abs(this._y1-u)>uu)&&(this._+="L"+c+","+u),n&&(h<0&&(h=h%cu+cu),h>lu?this._+="A"+n+","+n+",0,1,"+l+","+(t-o)+","+(e-s)+"A"+n+","+n+",0,1,"+l+","+(this._x1=c)+","+(this._y1=u):h>uu&&(this._+="A"+n+","+n+",0,"+ +(h>=su)+","+l+","+(this._x1=t+n*Math.cos(i))+","+(this._y1=e+n*Math.sin(i))))},rect:function(t,e,n,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +n+"v"+ +r+"h"+-n+"Z"},toString:function(){return this._}};const du=fu;function pu(t){return function(){return t}}var yu=Math.abs,gu=Math.atan2,mu=Math.cos,vu=Math.max,bu=Math.min,_u=Math.sin,xu=Math.sqrt,wu=1e-12,ku=Math.PI,Tu=ku/2,Eu=2*ku;function Cu(t){return t>1?0:t<-1?ku:Math.acos(t)}function Su(t){return t>=1?Tu:t<=-1?-Tu:Math.asin(t)}function Au(t){return t.innerRadius}function Mu(t){return t.outerRadius}function Nu(t){return t.startAngle}function Du(t){return t.endAngle}function Ou(t){return t&&t.padAngle}function Bu(t,e,n,r,i,a,o,s){var c=n-t,u=r-e,l=o-i,h=s-a,f=h*c-l*u;if(!(f*f<wu))return[t+(f=(l*(e-a)-h*(t-i))/f)*c,e+f*u]}function Lu(t,e,n,r,i,a,o){var s=t-n,c=e-r,u=(o?a:-a)/xu(s*s+c*c),l=u*c,h=-u*s,f=t+l,d=e+h,p=n+l,y=r+h,g=(f+p)/2,m=(d+y)/2,v=p-f,b=y-d,_=v*v+b*b,x=i-a,w=f*y-p*d,k=(b<0?-1:1)*xu(vu(0,x*x*_-w*w)),T=(w*b-v*k)/_,E=(-w*v-b*k)/_,C=(w*b+v*k)/_,S=(-w*v+b*k)/_,A=T-g,M=E-m,N=C-g,D=S-m;return A*A+M*M>N*N+D*D&&(T=C,E=S),{cx:T,cy:E,x01:-l,y01:-h,x11:T*(i/x-1),y11:E*(i/x-1)}}function Iu(){var t=Au,e=Mu,n=pu(0),r=null,i=Nu,a=Du,o=Ou,s=null;function c(){var c,u,l=+t.apply(this,arguments),h=+e.apply(this,arguments),f=i.apply(this,arguments)-Tu,d=a.apply(this,arguments)-Tu,p=yu(d-f),y=d>f;if(s||(s=c=du()),h<l&&(u=h,h=l,l=u),h>wu)if(p>Eu-wu)s.moveTo(h*mu(f),h*_u(f)),s.arc(0,0,h,f,d,!y),l>wu&&(s.moveTo(l*mu(d),l*_u(d)),s.arc(0,0,l,d,f,y));else{var g,m,v=f,b=d,_=f,x=d,w=p,k=p,T=o.apply(this,arguments)/2,E=T>wu&&(r?+r.apply(this,arguments):xu(l*l+h*h)),C=bu(yu(h-l)/2,+n.apply(this,arguments)),S=C,A=C;if(E>wu){var M=Su(E/l*_u(T)),N=Su(E/h*_u(T));(w-=2*M)>wu?(_+=M*=y?1:-1,x-=M):(w=0,_=x=(f+d)/2),(k-=2*N)>wu?(v+=N*=y?1:-1,b-=N):(k=0,v=b=(f+d)/2)}var D=h*mu(v),O=h*_u(v),B=l*mu(x),L=l*_u(x);if(C>wu){var I,R=h*mu(b),F=h*_u(b),P=l*mu(_),j=l*_u(_);if(p<ku&&(I=Bu(D,O,P,j,R,F,B,L))){var Y=D-I[0],z=O-I[1],U=R-I[0],q=F-I[1],H=1/_u(Cu((Y*U+z*q)/(xu(Y*Y+z*z)*xu(U*U+q*q)))/2),$=xu(I[0]*I[0]+I[1]*I[1]);S=bu(C,(l-$)/(H-1)),A=bu(C,(h-$)/(H+1))}}k>wu?A>wu?(g=Lu(P,j,D,O,h,A,y),m=Lu(R,F,B,L,h,A,y),s.moveTo(g.cx+g.x01,g.cy+g.y01),A<C?s.arc(g.cx,g.cy,A,gu(g.y01,g.x01),gu(m.y01,m.x01),!y):(s.arc(g.cx,g.cy,A,gu(g.y01,g.x01),gu(g.y11,g.x11),!y),s.arc(0,0,h,gu(g.cy+g.y11,g.cx+g.x11),gu(m.cy+m.y11,m.cx+m.x11),!y),s.arc(m.cx,m.cy,A,gu(m.y11,m.x11),gu(m.y01,m.x01),!y))):(s.moveTo(D,O),s.arc(0,0,h,v,b,!y)):s.moveTo(D,O),l>wu&&w>wu?S>wu?(g=Lu(B,L,R,F,l,-S,y),m=Lu(D,O,P,j,l,-S,y),s.lineTo(g.cx+g.x01,g.cy+g.y01),S<C?s.arc(g.cx,g.cy,S,gu(g.y01,g.x01),gu(m.y01,m.x01),!y):(s.arc(g.cx,g.cy,S,gu(g.y01,g.x01),gu(g.y11,g.x11),!y),s.arc(0,0,l,gu(g.cy+g.y11,g.cx+g.x11),gu(m.cy+m.y11,m.cx+m.x11),y),s.arc(m.cx,m.cy,S,gu(m.y11,m.x11),gu(m.y01,m.x01),!y))):s.arc(0,0,l,x,_,y):s.lineTo(B,L)}else s.moveTo(0,0);if(s.closePath(),c)return s=null,c+""||null}return c.centroid=function(){var n=(+t.apply(this,arguments)+ +e.apply(this,arguments))/2,r=(+i.apply(this,arguments)+ +a.apply(this,arguments))/2-ku/2;return[mu(r)*n,_u(r)*n]},c.innerRadius=function(e){return arguments.length?(t="function"==typeof e?e:pu(+e),c):t},c.outerRadius=function(t){return arguments.length?(e="function"==typeof t?t:pu(+t),c):e},c.cornerRadius=function(t){return arguments.length?(n="function"==typeof t?t:pu(+t),c):n},c.padRadius=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:pu(+t),c):r},c.startAngle=function(t){return arguments.length?(i="function"==typeof t?t:pu(+t),c):i},c.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:pu(+t),c):a},c.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:pu(+t),c):o},c.context=function(t){return arguments.length?(s=null==t?null:t,c):s},c}function Ru(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function Fu(t){this._context=t}function Pu(t){return new Fu(t)}function ju(t){return t[0]}function Yu(t){return t[1]}function zu(t,e){var n=pu(!0),r=null,i=Pu,a=null;function o(o){var s,c,u,l=(o=Ru(o)).length,h=!1;for(null==r&&(a=i(u=du())),s=0;s<=l;++s)!(s<l&&n(c=o[s],s,o))===h&&((h=!h)?a.lineStart():a.lineEnd()),h&&a.point(+t(c,s,o),+e(c,s,o));if(u)return a=null,u+""||null}return t="function"==typeof t?t:void 0===t?ju:pu(t),e="function"==typeof e?e:void 0===e?Yu:pu(e),o.x=function(e){return arguments.length?(t="function"==typeof e?e:pu(+e),o):t},o.y=function(t){return arguments.length?(e="function"==typeof t?t:pu(+t),o):e},o.defined=function(t){return arguments.length?(n="function"==typeof t?t:pu(!!t),o):n},o.curve=function(t){return arguments.length?(i=t,null!=r&&(a=i(r)),o):i},o.context=function(t){return arguments.length?(null==t?r=a=null:a=i(r=t),o):r},o}function Uu(t,e){return e<t?-1:e>t?1:e>=t?0:NaN}function qu(t){return t}function Hu(){}function $u(t,e,n){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+n)/6)}function Wu(t){this._context=t}function Vu(t){return new Wu(t)}function Gu(t){this._context=t}function Xu(t){this._context=t}function Zu(t){this._context=t}function Qu(t){return t<0?-1:1}function Ku(t,e,n){var r=t._x1-t._x0,i=e-t._x1,a=(t._y1-t._y0)/(r||i<0&&-0),o=(n-t._y1)/(i||r<0&&-0),s=(a*i+o*r)/(r+i);return(Qu(a)+Qu(o))*Math.min(Math.abs(a),Math.abs(o),.5*Math.abs(s))||0}function Ju(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}function tl(t,e,n){var r=t._x0,i=t._y0,a=t._x1,o=t._y1,s=(a-r)/3;t._context.bezierCurveTo(r+s,i+s*e,a-s,o-s*n,a,o)}function el(t){this._context=t}function nl(t){this._context=new rl(t)}function rl(t){this._context=t}function il(t){this._context=t}function al(t){var e,n,r=t.length-1,i=new Array(r),a=new Array(r),o=new Array(r);for(i[0]=0,a[0]=2,o[0]=t[0]+2*t[1],e=1;e<r-1;++e)i[e]=1,a[e]=4,o[e]=4*t[e]+2*t[e+1];for(i[r-1]=2,a[r-1]=7,o[r-1]=8*t[r-1]+t[r],e=1;e<r;++e)n=i[e]/a[e-1],a[e]-=n,o[e]-=n*o[e-1];for(i[r-1]=o[r-1]/a[r-1],e=r-2;e>=0;--e)i[e]=(o[e]-i[e+1])/a[e];for(a[r-1]=(t[r]+i[r-1])/2,e=0;e<r-1;++e)a[e]=2*t[e+1]-i[e+1];return[i,a]}function ol(t,e){this._context=t,this._t=e}Array.prototype.slice,Fu.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:this._context.lineTo(t,e)}}},Wu.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:$u(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:$u(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},Gu.prototype={areaStart:Hu,areaEnd:Hu,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:$u(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},Xu.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(n,r):this._context.moveTo(n,r);break;case 3:this._point=4;default:$u(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},Zu.prototype={areaStart:Hu,areaEnd:Hu,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))}},el.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:tl(this,this._t0,Ju(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){var n=NaN;if(e=+e,(t=+t)!==this._x1||e!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,tl(this,Ju(this,n=Ku(this,t,e)),n);break;default:tl(this,this._t0,n=Ku(this,t,e))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=n}}},(nl.prototype=Object.create(el.prototype)).point=function(t,e){el.prototype.point.call(this,e,t)},rl.prototype={moveTo:function(t,e){this._context.moveTo(e,t)},closePath:function(){this._context.closePath()},lineTo:function(t,e){this._context.lineTo(e,t)},bezierCurveTo:function(t,e,n,r,i,a){this._context.bezierCurveTo(e,t,r,n,a,i)}},il.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,e=this._y,n=t.length;if(n)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),2===n)this._context.lineTo(t[1],e[1]);else for(var r=al(t),i=al(e),a=0,o=1;o<n;++a,++o)this._context.bezierCurveTo(r[0][a],i[0][a],r[1][a],i[1][a],t[o],e[o]);(this._line||0!==this._line&&1===n)&&this._context.closePath(),this._line=1-this._line,this._x=this._y=null},point:function(t,e){this._x.push(+t),this._y.push(+e)}},ol.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=this._y=NaN,this._point=0},lineEnd:function(){0<this._t&&this._t<1&&2===this._point&&this._context.lineTo(this._x,this._y),(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line>=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var n=this._x*(1-this._t)+t*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,e)}}this._x=t,this._y=e}};var sl=new Date,cl=new Date;function ul(t,e,n,r){function i(e){return t(e=0===arguments.length?new Date:new Date(+e)),e}return i.floor=function(e){return t(e=new Date(+e)),e},i.ceil=function(n){return t(n=new Date(n-1)),e(n,1),t(n),n},i.round=function(t){var e=i(t),n=i.ceil(t);return t-e<n-t?e:n},i.offset=function(t,n){return e(t=new Date(+t),null==n?1:Math.floor(n)),t},i.range=function(n,r,a){var o,s=[];if(n=i.ceil(n),a=null==a?1:Math.floor(a),!(n<r&&a>0))return s;do{s.push(o=new Date(+n)),e(n,a),t(n)}while(o<n&&n<r);return s},i.filter=function(n){return ul((function(e){if(e>=e)for(;t(e),!n(e);)e.setTime(e-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;e(t,-1),!n(t););else for(;--r>=0;)for(;e(t,1),!n(t););}))},n&&(i.count=function(e,r){return sl.setTime(+e),cl.setTime(+r),t(sl),t(cl),Math.floor(n(sl,cl))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(e){return r(e)%t==0}:function(e){return i.count(0,e)%t==0}):i:null}),i}const ll=864e5,hl=6048e5;function fl(t){return ul((function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+7*e)}),(function(t,e){return(e-t)/hl}))}var dl=fl(0),pl=fl(1),yl=fl(2),gl=fl(3),ml=fl(4),vl=fl(5),bl=fl(6),_l=(dl.range,pl.range,yl.range,gl.range,ml.range,vl.range,bl.range,ul((function(t){t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+e)}),(function(t,e){return(e-t)/ll}),(function(t){return t.getUTCDate()-1})));const xl=_l;function wl(t){return ul((function(e){e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+7*e)}),(function(t,e){return(e-t-6e4*(e.getTimezoneOffset()-t.getTimezoneOffset()))/hl}))}_l.range;var kl=wl(0),Tl=wl(1),El=wl(2),Cl=wl(3),Sl=wl(4),Al=wl(5),Ml=wl(6),Nl=(kl.range,Tl.range,El.range,Cl.range,Sl.range,Al.range,Ml.range,ul((t=>t.setHours(0,0,0,0)),((t,e)=>t.setDate(t.getDate()+e)),((t,e)=>(e-t-6e4*(e.getTimezoneOffset()-t.getTimezoneOffset()))/ll),(t=>t.getDate()-1)));const Dl=Nl;Nl.range;var Ol=ul((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,e){t.setFullYear(t.getFullYear()+e)}),(function(t,e){return e.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));Ol.every=function(t){return isFinite(t=Math.floor(t))&&t>0?ul((function(e){e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)}),(function(e,n){e.setFullYear(e.getFullYear()+n*t)})):null};const Bl=Ol;Ol.range;var Ll=ul((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCFullYear(t.getUTCFullYear()+e)}),(function(t,e){return e.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));Ll.every=function(t){return isFinite(t=Math.floor(t))&&t>0?ul((function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)}),(function(e,n){e.setUTCFullYear(e.getUTCFullYear()+n*t)})):null};const Il=Ll;function Rl(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function Fl(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Pl(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}Ll.range;var jl,Yl,zl={"-":"",_:" ",0:"0"},Ul=/^\s*\d+/,ql=/^%/,Hl=/[\\^$*+?|[\]().{}]/g;function $l(t,e,n){var r=t<0?"-":"",i=(r?-t:t)+"",a=i.length;return r+(a<n?new Array(n-a+1).join(e)+i:i)}function Wl(t){return t.replace(Hl,"\\$&")}function Vl(t){return new RegExp("^(?:"+t.map(Wl).join("|")+")","i")}function Gl(t){return new Map(t.map(((t,e)=>[t.toLowerCase(),e])))}function Xl(t,e,n){var r=Ul.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r[0].length):-1}function Zl(t,e,n){var r=Ul.exec(e.slice(n,n+1));return r?(t.u=+r[0],n+r[0].length):-1}function Ql(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.U=+r[0],n+r[0].length):-1}function Kl(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.V=+r[0],n+r[0].length):-1}function Jl(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.W=+r[0],n+r[0].length):-1}function th(t,e,n){var r=Ul.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r[0].length):-1}function eh(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),n+r[0].length):-1}function nh(t,e,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function rh(t,e,n){var r=Ul.exec(e.slice(n,n+1));return r?(t.q=3*r[0]-3,n+r[0].length):-1}function ih(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+r[0].length):-1}function ah(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r[0].length):-1}function oh(t,e,n){var r=Ul.exec(e.slice(n,n+3));return r?(t.m=0,t.d=+r[0],n+r[0].length):-1}function sh(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r[0].length):-1}function ch(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r[0].length):-1}function uh(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r[0].length):-1}function lh(t,e,n){var r=Ul.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r[0].length):-1}function hh(t,e,n){var r=Ul.exec(e.slice(n,n+6));return r?(t.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function fh(t,e,n){var r=ql.exec(e.slice(n,n+1));return r?n+r[0].length:-1}function dh(t,e,n){var r=Ul.exec(e.slice(n));return r?(t.Q=+r[0],n+r[0].length):-1}function ph(t,e,n){var r=Ul.exec(e.slice(n));return r?(t.s=+r[0],n+r[0].length):-1}function yh(t,e){return $l(t.getDate(),e,2)}function gh(t,e){return $l(t.getHours(),e,2)}function mh(t,e){return $l(t.getHours()%12||12,e,2)}function vh(t,e){return $l(1+Dl.count(Bl(t),t),e,3)}function bh(t,e){return $l(t.getMilliseconds(),e,3)}function _h(t,e){return bh(t,e)+"000"}function xh(t,e){return $l(t.getMonth()+1,e,2)}function wh(t,e){return $l(t.getMinutes(),e,2)}function kh(t,e){return $l(t.getSeconds(),e,2)}function Th(t){var e=t.getDay();return 0===e?7:e}function Eh(t,e){return $l(kl.count(Bl(t)-1,t),e,2)}function Ch(t){var e=t.getDay();return e>=4||0===e?Sl(t):Sl.ceil(t)}function Sh(t,e){return t=Ch(t),$l(Sl.count(Bl(t),t)+(4===Bl(t).getDay()),e,2)}function Ah(t){return t.getDay()}function Mh(t,e){return $l(Tl.count(Bl(t)-1,t),e,2)}function Nh(t,e){return $l(t.getFullYear()%100,e,2)}function Dh(t,e){return $l((t=Ch(t)).getFullYear()%100,e,2)}function Oh(t,e){return $l(t.getFullYear()%1e4,e,4)}function Bh(t,e){var n=t.getDay();return $l((t=n>=4||0===n?Sl(t):Sl.ceil(t)).getFullYear()%1e4,e,4)}function Lh(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+$l(e/60|0,"0",2)+$l(e%60,"0",2)}function Ih(t,e){return $l(t.getUTCDate(),e,2)}function Rh(t,e){return $l(t.getUTCHours(),e,2)}function Fh(t,e){return $l(t.getUTCHours()%12||12,e,2)}function Ph(t,e){return $l(1+xl.count(Il(t),t),e,3)}function jh(t,e){return $l(t.getUTCMilliseconds(),e,3)}function Yh(t,e){return jh(t,e)+"000"}function zh(t,e){return $l(t.getUTCMonth()+1,e,2)}function Uh(t,e){return $l(t.getUTCMinutes(),e,2)}function qh(t,e){return $l(t.getUTCSeconds(),e,2)}function Hh(t){var e=t.getUTCDay();return 0===e?7:e}function $h(t,e){return $l(dl.count(Il(t)-1,t),e,2)}function Wh(t){var e=t.getUTCDay();return e>=4||0===e?ml(t):ml.ceil(t)}function Vh(t,e){return t=Wh(t),$l(ml.count(Il(t),t)+(4===Il(t).getUTCDay()),e,2)}function Gh(t){return t.getUTCDay()}function Xh(t,e){return $l(pl.count(Il(t)-1,t),e,2)}function Zh(t,e){return $l(t.getUTCFullYear()%100,e,2)}function Qh(t,e){return $l((t=Wh(t)).getUTCFullYear()%100,e,2)}function Kh(t,e){return $l(t.getUTCFullYear()%1e4,e,4)}function Jh(t,e){var n=t.getUTCDay();return $l((t=n>=4||0===n?ml(t):ml.ceil(t)).getUTCFullYear()%1e4,e,4)}function tf(){return"+0000"}function ef(){return"%"}function nf(t){return+t}function rf(t){return Math.floor(+t/1e3)}jl=function(t){var e=t.dateTime,n=t.date,r=t.time,i=t.periods,a=t.days,o=t.shortDays,s=t.months,c=t.shortMonths,u=Vl(i),l=Gl(i),h=Vl(a),f=Gl(a),d=Vl(o),p=Gl(o),y=Vl(s),g=Gl(s),m=Vl(c),v=Gl(c),b={a:function(t){return o[t.getDay()]},A:function(t){return a[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return s[t.getMonth()]},c:null,d:yh,e:yh,f:_h,g:Dh,G:Bh,H:gh,I:mh,j:vh,L:bh,m:xh,M:wh,p:function(t){return i[+(t.getHours()>=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:nf,s:rf,S:kh,u:Th,U:Eh,V:Sh,w:Ah,W:Mh,x:null,X:null,y:Nh,Y:Oh,Z:Lh,"%":ef},_={a:function(t){return o[t.getUTCDay()]},A:function(t){return a[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return s[t.getUTCMonth()]},c:null,d:Ih,e:Ih,f:Yh,g:Qh,G:Jh,H:Rh,I:Fh,j:Ph,L:jh,m:zh,M:Uh,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:nf,s:rf,S:qh,u:Hh,U:$h,V:Vh,w:Gh,W:Xh,x:null,X:null,y:Zh,Y:Kh,Z:tf,"%":ef},x={a:function(t,e,n){var r=d.exec(e.slice(n));return r?(t.w=p.get(r[0].toLowerCase()),n+r[0].length):-1},A:function(t,e,n){var r=h.exec(e.slice(n));return r?(t.w=f.get(r[0].toLowerCase()),n+r[0].length):-1},b:function(t,e,n){var r=m.exec(e.slice(n));return r?(t.m=v.get(r[0].toLowerCase()),n+r[0].length):-1},B:function(t,e,n){var r=y.exec(e.slice(n));return r?(t.m=g.get(r[0].toLowerCase()),n+r[0].length):-1},c:function(t,n,r){return T(t,e,n,r)},d:ah,e:ah,f:hh,g:eh,G:th,H:sh,I:sh,j:oh,L:lh,m:ih,M:ch,p:function(t,e,n){var r=u.exec(e.slice(n));return r?(t.p=l.get(r[0].toLowerCase()),n+r[0].length):-1},q:rh,Q:dh,s:ph,S:uh,u:Zl,U:Ql,V:Kl,w:Xl,W:Jl,x:function(t,e,r){return T(t,n,e,r)},X:function(t,e,n){return T(t,r,e,n)},y:eh,Y:th,Z:nh,"%":fh};function w(t,e){return function(n){var r,i,a,o=[],s=-1,c=0,u=t.length;for(n instanceof Date||(n=new Date(+n));++s<u;)37===t.charCodeAt(s)&&(o.push(t.slice(c,s)),null!=(i=zl[r=t.charAt(++s)])?r=t.charAt(++s):i="e"===r?" ":"0",(a=e[r])&&(r=a(n,i)),o.push(r),c=s+1);return o.push(t.slice(c,s)),o.join("")}}function k(t,e){return function(n){var r,i,a=Pl(1900,void 0,1);if(T(a,t,n+="",0)!=n.length)return null;if("Q"in a)return new Date(a.Q);if("s"in a)return new Date(1e3*a.s+("L"in a?a.L:0));if(e&&!("Z"in a)&&(a.Z=0),"p"in a&&(a.H=a.H%12+12*a.p),void 0===a.m&&(a.m="q"in a?a.q:0),"V"in a){if(a.V<1||a.V>53)return null;"w"in a||(a.w=1),"Z"in a?(i=(r=Fl(Pl(a.y,0,1))).getUTCDay(),r=i>4||0===i?pl.ceil(r):pl(r),r=xl.offset(r,7*(a.V-1)),a.y=r.getUTCFullYear(),a.m=r.getUTCMonth(),a.d=r.getUTCDate()+(a.w+6)%7):(i=(r=Rl(Pl(a.y,0,1))).getDay(),r=i>4||0===i?Tl.ceil(r):Tl(r),r=Dl.offset(r,7*(a.V-1)),a.y=r.getFullYear(),a.m=r.getMonth(),a.d=r.getDate()+(a.w+6)%7)}else("W"in a||"U"in a)&&("w"in a||(a.w="u"in a?a.u%7:"W"in a?1:0),i="Z"in a?Fl(Pl(a.y,0,1)).getUTCDay():Rl(Pl(a.y,0,1)).getDay(),a.m=0,a.d="W"in a?(a.w+6)%7+7*a.W-(i+5)%7:a.w+7*a.U-(i+6)%7);return"Z"in a?(a.H+=a.Z/100|0,a.M+=a.Z%100,Fl(a)):Rl(a)}}function T(t,e,n,r){for(var i,a,o=0,s=e.length,c=n.length;o<s;){if(r>=c)return-1;if(37===(i=e.charCodeAt(o++))){if(i=e.charAt(o++),!(a=x[i in zl?e.charAt(o++):i])||(r=a(t,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}return b.x=w(n,b),b.X=w(r,b),b.c=w(e,b),_.x=w(n,_),_.X=w(r,_),_.c=w(e,_),{format:function(t){var e=w(t+="",b);return e.toString=function(){return t},e},parse:function(t){var e=k(t+="",!1);return e.toString=function(){return t},e},utcFormat:function(t){var e=w(t+="",_);return e.toString=function(){return t},e},utcParse:function(t){var e=k(t+="",!0);return e.toString=function(){return t},e}}}({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}),Yl=jl.format,jl.parse,jl.utcFormat,jl.utcParse;var af={value:()=>{}};function of(){for(var t,e=0,n=arguments.length,r={};e<n;++e){if(!(t=arguments[e]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new sf(r)}function sf(t){this._=t}function cf(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function uf(t,e){for(var n,r=0,i=t.length;r<i;++r)if((n=t[r]).name===e)return n.value}function lf(t,e,n){for(var r=0,i=t.length;r<i;++r)if(t[r].name===e){t[r]=af,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=n&&t.push({name:e,value:n}),t}sf.prototype=of.prototype={constructor:sf,on:function(t,e){var n,r=this._,i=cf(t+"",r),a=-1,o=i.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++a<o;)if(n=(t=i[a]).type)r[n]=lf(r[n],t.name,e);else if(null==e)for(n in r)r[n]=lf(r[n],t.name,null);return this}for(;++a<o;)if((n=(t=i[a]).type)&&(n=uf(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new sf(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,i=new Array(n),a=0;a<n;++a)i[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,i)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,a=r.length;i<a;++i)r[i].value.apply(e,n)}};const hf=of;var ff,df,pf=0,yf=0,gf=0,mf=0,vf=0,bf=0,_f="object"==typeof performance&&performance.now?performance:Date,xf="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function wf(){return vf||(xf(kf),vf=_f.now()+bf)}function kf(){vf=0}function Tf(){this._call=this._time=this._next=null}function Ef(t,e,n){var r=new Tf;return r.restart(t,e,n),r}function Cf(){vf=(mf=_f.now())+bf,pf=yf=0;try{!function(){wf(),++pf;for(var t,e=ff;e;)(t=vf-e._time)>=0&&e._call.call(void 0,t),e=e._next;--pf}()}finally{pf=0,function(){for(var t,e,n=ff,r=1/0;n;)n._call?(r>n._time&&(r=n._time),t=n,n=n._next):(e=n._next,n._next=null,n=t?t._next=e:ff=e);df=t,Af(r)}(),vf=0}}function Sf(){var t=_f.now(),e=t-mf;e>1e3&&(bf-=e,mf=t)}function Af(t){pf||(yf&&(yf=clearTimeout(yf)),t-vf>24?(t<1/0&&(yf=setTimeout(Cf,t-_f.now()-bf)),gf&&(gf=clearInterval(gf))):(gf||(mf=_f.now(),gf=setInterval(Sf,1e3)),pf=1,xf(Cf)))}function Mf(t,e,n){var r=new Tf;return e=null==e?0:+e,r.restart((n=>{r.stop(),t(n+e)}),e,n),r}Tf.prototype=Ef.prototype={constructor:Tf,restart:function(t,e,n){if("function"!=typeof t)throw new TypeError("callback is not a function");n=(null==n?wf():+n)+(null==e?0:+e),this._next||df===this||(df?df._next=this:ff=this,df=this),this._call=t,this._time=n,Af()},stop:function(){this._call&&(this._call=null,this._time=1/0,Af())}};var Nf=hf("start","end","cancel","interrupt"),Df=[];function Of(t,e,n,r,i,a){var o=t.__transition;if(o){if(n in o)return}else t.__transition={};!function(t,e,n){var r,i=t.__transition;function a(c){var u,l,h,f;if(1!==n.state)return s();for(u in i)if((f=i[u]).name===n.name){if(3===f.state)return Mf(a);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[u]):+u<e&&(f.state=6,f.timer.stop(),f.on.call("cancel",t,t.__data__,f.index,f.group),delete i[u])}if(Mf((function(){3===n.state&&(n.state=4,n.timer.restart(o,n.delay,n.time),o(c))})),n.state=2,n.on.call("start",t,t.__data__,n.index,n.group),2===n.state){for(n.state=3,r=new Array(h=n.tween.length),u=0,l=-1;u<h;++u)(f=n.tween[u].value.call(t,t.__data__,n.index,n.group))&&(r[++l]=f);r.length=l+1}}function o(e){for(var i=e<n.duration?n.ease.call(null,e/n.duration):(n.timer.restart(s),n.state=5,1),a=-1,o=r.length;++a<o;)r[a].call(t,i);5===n.state&&(n.on.call("end",t,t.__data__,n.index,n.group),s())}function s(){for(var r in n.state=6,n.timer.stop(),delete i[e],i)return;delete t.__transition}i[e]=n,n.timer=Ef((function(t){n.state=1,n.timer.restart(a,n.delay,n.time),n.delay<=t&&a(t-n.delay)}),0,n.time)}(t,n,{name:e,index:r,group:i,on:Nf,tween:Df,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:0})}function Bf(t,e){var n=If(t,e);if(n.state>0)throw new Error("too late; already scheduled");return n}function Lf(t,e){var n=If(t,e);if(n.state>3)throw new Error("too late; already running");return n}function If(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function Rf(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}var Ff,Pf=180/Math.PI,jf={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Yf(t,e,n,r,i,a){var o,s,c;return(o=Math.sqrt(t*t+e*e))&&(t/=o,e/=o),(c=t*n+e*r)&&(n-=t*c,r-=e*c),(s=Math.sqrt(n*n+r*r))&&(n/=s,r/=s,c/=s),t*r<e*n&&(t=-t,e=-e,c=-c,o=-o),{translateX:i,translateY:a,rotate:Math.atan2(e,t)*Pf,skewX:Math.atan(c)*Pf,scaleX:o,scaleY:s}}function zf(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return function(a,o){var s=[],c=[];return a=t(a),o=t(o),function(t,r,i,a,o,s){if(t!==i||r!==a){var c=o.push("translate(",null,e,null,n);s.push({i:c-4,x:Rf(t,i)},{i:c-2,x:Rf(r,a)})}else(i||a)&&o.push("translate("+i+e+a+n)}(a.translateX,a.translateY,o.translateX,o.translateY,s,c),function(t,e,n,a){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:Rf(t,e)})):e&&n.push(i(n)+"rotate("+e+r)}(a.rotate,o.rotate,s,c),function(t,e,n,a){t!==e?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:Rf(t,e)}):e&&n.push(i(n)+"skewX("+e+r)}(a.skewX,o.skewX,s,c),function(t,e,n,r,a,o){if(t!==n||e!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:Rf(t,n)},{i:s-2,x:Rf(e,r)})}else 1===n&&1===r||a.push(i(a)+"scale("+n+","+r+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,c),a=o=null,function(t){for(var e,n=-1,r=c.length;++n<r;)s[(e=c[n]).i]=e.x(t);return s.join("")}}}var Uf=zf((function(t){const e=new("function"==typeof DOMMatrix?DOMMatrix:WebKitCSSMatrix)(t+"");return e.isIdentity?jf:Yf(e.a,e.b,e.c,e.d,e.e,e.f)}),"px, ","px)","deg)"),qf=zf((function(t){return null==t?jf:(Ff||(Ff=document.createElementNS("http://www.w3.org/2000/svg","g")),Ff.setAttribute("transform",t),(t=Ff.transform.baseVal.consolidate())?Yf((t=t.matrix).a,t.b,t.c,t.d,t.e,t.f):jf)}),", ",")",")");function Hf(t,e){var n,r;return function(){var i=Lf(this,t),a=i.tween;if(a!==n)for(var o=0,s=(r=n=a).length;o<s;++o)if(r[o].name===e){(r=r.slice()).splice(o,1);break}i.tween=r}}function $f(t,e,n){var r,i;if("function"!=typeof n)throw new Error;return function(){var a=Lf(this,t),o=a.tween;if(o!==r){i=(r=o).slice();for(var s={name:e,value:n},c=0,u=i.length;c<u;++c)if(i[c].name===e){i[c]=s;break}c===u&&i.push(s)}a.tween=i}}function Wf(t,e,n){var r=t._id;return t.each((function(){var t=Lf(this,r);(t.value||(t.value={}))[e]=n.apply(this,arguments)})),function(t){return If(t,r).value[e]}}function Vf(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}const Gf=function t(e){var n=function(t){return 1==(t=+t)?Fr:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):Ir(isNaN(e)?n:e)}}(e);function r(t,e){var r=n((t=ur(t)).r,(e=ur(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=Fr(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function Xf(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=ur(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}Xf((function(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return Vf((n-r/e)*e,o,i,a,s)}})),Xf((function(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return Vf((n-r/e)*e,i,a,o,s)}}));var Zf=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Qf=new RegExp(Zf.source,"g");function Kf(t,e){var n,r,i,a=Zf.lastIndex=Qf.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=Zf.exec(t))&&(r=Qf.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:Rf(n,r)})),a=Qf.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})}function Jf(t,e){var n;return("number"==typeof e?Rf:e instanceof ar?Gf:(n=ar(e))?(e=n,Gf):Kf)(t,e)}function td(t){return function(){this.removeAttribute(t)}}function ed(t){return function(){this.removeAttributeNS(t.space,t.local)}}function nd(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttribute(t);return o===a?null:o===r?i:i=e(r=o,n)}}function rd(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttributeNS(t.space,t.local);return o===a?null:o===r?i:i=e(r=o,n)}}function id(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttribute(t))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttribute(t)}}function ad(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttributeNS(t.space,t.local))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttributeNS(t.space,t.local)}}function od(t,e){return function(n){this.setAttribute(t,e.call(this,n))}}function sd(t,e){return function(n){this.setAttributeNS(t.space,t.local,e.call(this,n))}}function cd(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&sd(t,i)),n}return i._value=e,i}function ud(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&od(t,i)),n}return i._value=e,i}function ld(t,e){return function(){Bf(this,t).delay=+e.apply(this,arguments)}}function hd(t,e){return e=+e,function(){Bf(this,t).delay=e}}function fd(t,e){return function(){Lf(this,t).duration=+e.apply(this,arguments)}}function dd(t,e){return e=+e,function(){Lf(this,t).duration=e}}function pd(t,e){if("function"!=typeof e)throw new Error;return function(){Lf(this,t).ease=e}}function yd(t,e,n){var r,i,a=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var e=t.indexOf(".");return e>=0&&(t=t.slice(0,e)),!t||"start"===t}))}(e)?Bf:Lf;return function(){var o=a(this,t),s=o.on;s!==r&&(i=(r=s).copy()).on(e,n),o.on=i}}var gd=iu.prototype.constructor;function md(t){return function(){this.style.removeProperty(t)}}function vd(t,e,n){return function(r){this.style.setProperty(t,e.call(this,r),n)}}function bd(t,e,n){var r,i;function a(){var a=e.apply(this,arguments);return a!==i&&(r=(i=a)&&vd(t,a,n)),r}return a._value=e,a}function _d(t){return function(e){this.textContent=t.call(this,e)}}function xd(t){var e,n;function r(){var r=t.apply(this,arguments);return r!==n&&(e=(n=r)&&_d(r)),e}return r._value=t,r}var wd=0;function kd(t,e,n,r){this._groups=t,this._parents=e,this._name=n,this._id=r}function Td(){return++wd}var Ed=iu.prototype;kd.prototype=function(t){return iu().transition(t)}.prototype={constructor:kd,select:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=$s(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;o<i;++o)for(var s,c,u=r[o],l=u.length,h=a[o]=new Array(l),f=0;f<l;++f)(s=u[f])&&(c=t.call(s,s.__data__,f,u))&&("__data__"in s&&(c.__data__=s.__data__),h[f]=c,Of(h[f],e,n,f,h,If(s,n)));return new kd(a,this._parents,e,n)},selectAll:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=Gs(t));for(var r=this._groups,i=r.length,a=[],o=[],s=0;s<i;++s)for(var c,u=r[s],l=u.length,h=0;h<l;++h)if(c=u[h]){for(var f,d=t.call(c,c.__data__,h,u),p=If(c,n),y=0,g=d.length;y<g;++y)(f=d[y])&&Of(f,e,n,y,d,p);a.push(d),o.push(c)}return new kd(a,o,e,n)},selectChild:Ed.selectChild,selectChildren:Ed.selectChildren,filter:function(t){"function"!=typeof t&&(t=Xs(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new kd(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new kd(o,this._parents,this._name,this._id)},selection:function(){return new gd(this._groups,this._parents)},transition:function(){for(var t=this._name,e=this._id,n=Td(),r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)if(o=s[u]){var l=If(o,e);Of(o,t,n,u,s,{time:l.time+l.delay+l.duration,delay:0,duration:l.duration,ease:l.ease})}return new kd(r,this._parents,t,n)},call:Ed.call,nodes:Ed.nodes,node:Ed.node,size:Ed.size,empty:Ed.empty,each:Ed.each,on:function(t,e){var n=this._id;return arguments.length<2?If(this.node(),n).on.on(t):this.each(yd(n,t,e))},attr:function(t,e){var n=hc(t),r="transform"===n?qf:Jf;return this.attrTween(t,"function"==typeof e?(n.local?ad:id)(n,r,Wf(this,"attr."+t,e)):null==e?(n.local?ed:td)(n):(n.local?rd:nd)(n,r,e))},attrTween:function(t,e){var n="attr."+t;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(null==e)return this.tween(n,null);if("function"!=typeof e)throw new Error;var r=hc(t);return this.tween(n,(r.local?cd:ud)(r,e))},style:function(t,e,n){var r="transform"==(t+="")?Uf:Jf;return null==e?this.styleTween(t,function(t,e){var n,r,i;return function(){var a=wc(this,t),o=(this.style.removeProperty(t),wc(this,t));return a===o?null:a===n&&o===r?i:i=e(n=a,r=o)}}(t,r)).on("end.style."+t,md(t)):"function"==typeof e?this.styleTween(t,function(t,e,n){var r,i,a;return function(){var o=wc(this,t),s=n(this),c=s+"";return null==s&&(this.style.removeProperty(t),c=s=wc(this,t)),o===c?null:o===r&&c===i?a:(i=c,a=e(r=o,s))}}(t,r,Wf(this,"style."+t,e))).each(function(t,e){var n,r,i,a,o="style."+e,s="end."+o;return function(){var c=Lf(this,t),u=c.on,l=null==c.value[o]?a||(a=md(e)):void 0;u===n&&i===l||(r=(n=u).copy()).on(s,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,e,n){var r,i,a=n+"";return function(){var o=wc(this,t);return o===a?null:o===r?i:i=e(r=o,n)}}(t,r,e),n).on("end.style."+t,null)},styleTween:function(t,e,n){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==e)return this.tween(r,null);if("function"!=typeof e)throw new Error;return this.tween(r,bd(t,e,null==n?"":n))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var e=t(this);this.textContent=null==e?"":e}}(Wf(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(null==t)return this.tween(e,null);if("function"!=typeof t)throw new Error;return this.tween(e,xd(t))},remove:function(){return this.on("end.remove",function(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}(this._id))},tween:function(t,e){var n=this._id;if(t+="",arguments.length<2){for(var r,i=If(this.node(),n).tween,a=0,o=i.length;a<o;++a)if((r=i[a]).name===t)return r.value;return null}return this.each((null==e?Hf:$f)(n,t,e))},delay:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?ld:hd)(e,t)):If(this.node(),e).delay},duration:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?fd:dd)(e,t)):If(this.node(),e).duration},ease:function(t){var e=this._id;return arguments.length?this.each(pd(e,t)):If(this.node(),e).ease},easeVarying:function(t){if("function"!=typeof t)throw new Error;return this.each(function(t,e){return function(){var n=e.apply(this,arguments);if("function"!=typeof n)throw new Error;Lf(this,t).ease=n}}(this._id,t))},end:function(){var t,e,n=this,r=n._id,i=n.size();return new Promise((function(a,o){var s={value:o},c={value:function(){0==--i&&a()}};n.each((function(){var n=Lf(this,r),i=n.on;i!==t&&((e=(t=i).copy())._.cancel.push(s),e._.interrupt.push(s),e._.end.push(c)),n.on=e})),0===i&&a()}))},[Symbol.iterator]:Ed[Symbol.iterator]};var Cd={time:null,delay:0,duration:250,ease:function(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}};function Sd(t,e){for(var n;!(n=t.__transition)||!(n=n[e]);)if(!(t=t.parentNode))throw new Error(`transition ${e} not found`);return n}function Ad(){}function Md(t){return null==t?Ad:function(){return this.querySelector(t)}}function Nd(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function Dd(){return[]}function Od(t){return null==t?Dd:function(){return this.querySelectorAll(t)}}function Bd(t){return function(){return this.matches(t)}}function Ld(t){return function(e){return e.matches(t)}}iu.prototype.interrupt=function(t){return this.each((function(){!function(t,e){var n,r,i,a=t.__transition,o=!0;if(a){for(i in e=null==e?null:e+"",a)(n=a[i]).name===e?(r=n.state>2&&n.state<5,n.state=6,n.timer.stop(),n.on.call(r?"interrupt":"cancel",t,t.__data__,n.index,n.group),delete a[i]):o=!1;o&&delete t.__transition}}(this,t)}))},iu.prototype.transition=function(t){var e,n;t instanceof kd?(e=t._id,t=t._name):(e=Td(),(n=Cd).time=wf(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)(o=s[u])&&Of(o,t,e,u,s,n||Sd(o,e));return new kd(r,this._parents,t,e)};var Id=Array.prototype.find;function Rd(){return this.firstElementChild}var Fd=Array.prototype.filter;function Pd(){return Array.from(this.children)}function jd(t){return new Array(t.length)}function Yd(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}function zd(t){return function(){return t}}function Ud(t,e,n,r,i,a){for(var o,s=0,c=e.length,u=a.length;s<u;++s)(o=e[s])?(o.__data__=a[s],r[s]=o):n[s]=new Yd(t,a[s]);for(;s<c;++s)(o=e[s])&&(i[s]=o)}function qd(t,e,n,r,i,a,o){var s,c,u,l=new Map,h=e.length,f=a.length,d=new Array(h);for(s=0;s<h;++s)(c=e[s])&&(d[s]=u=o.call(c,c.__data__,s,e)+"",l.has(u)?i[s]=c:l.set(u,c));for(s=0;s<f;++s)u=o.call(t,a[s],s,a)+"",(c=l.get(u))?(r[s]=c,c.__data__=a[s],l.delete(u)):n[s]=new Yd(t,a[s]);for(s=0;s<h;++s)(c=e[s])&&l.get(d[s])===c&&(i[s]=c)}function Hd(t){return t.__data__}function $d(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function Wd(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}Yd.prototype={constructor:Yd,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,e){return this._parent.insertBefore(t,e)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var Vd="http://www.w3.org/1999/xhtml";const Gd={svg:"http://www.w3.org/2000/svg",xhtml:Vd,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function Xd(t){var e=t+="",n=e.indexOf(":");return n>=0&&"xmlns"!==(e=t.slice(0,n))&&(t=t.slice(n+1)),Gd.hasOwnProperty(e)?{space:Gd[e],local:t}:t}function Zd(t){return function(){this.removeAttribute(t)}}function Qd(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Kd(t,e){return function(){this.setAttribute(t,e)}}function Jd(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function tp(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}}function ep(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}function np(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function rp(t){return function(){this.style.removeProperty(t)}}function ip(t,e,n){return function(){this.style.setProperty(t,e,n)}}function ap(t,e,n){return function(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}}function op(t,e){return t.style.getPropertyValue(e)||np(t).getComputedStyle(t,null).getPropertyValue(e)}function sp(t){return function(){delete this[t]}}function cp(t,e){return function(){this[t]=e}}function up(t,e){return function(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}}function lp(t){return t.trim().split(/^|\s+/)}function hp(t){return t.classList||new fp(t)}function fp(t){this._node=t,this._names=lp(t.getAttribute("class")||"")}function dp(t,e){for(var n=hp(t),r=-1,i=e.length;++r<i;)n.add(e[r])}function pp(t,e){for(var n=hp(t),r=-1,i=e.length;++r<i;)n.remove(e[r])}function yp(t){return function(){dp(this,t)}}function gp(t){return function(){pp(this,t)}}function mp(t,e){return function(){(e.apply(this,arguments)?dp:pp)(this,t)}}function vp(){this.textContent=""}function bp(t){return function(){this.textContent=t}}function _p(t){return function(){var e=t.apply(this,arguments);this.textContent=null==e?"":e}}function xp(){this.innerHTML=""}function wp(t){return function(){this.innerHTML=t}}function kp(t){return function(){var e=t.apply(this,arguments);this.innerHTML=null==e?"":e}}function Tp(){this.nextSibling&&this.parentNode.appendChild(this)}function Ep(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function Cp(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===Vd&&e.documentElement.namespaceURI===Vd?e.createElement(t):e.createElementNS(n,t)}}function Sp(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Ap(t){var e=Xd(t);return(e.local?Sp:Cp)(e)}function Mp(){return null}function Np(){var t=this.parentNode;t&&t.removeChild(this)}function Dp(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function Op(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function Bp(t){return t.trim().split(/^|\s+/).map((function(t){var e="",n=t.indexOf(".");return n>=0&&(e=t.slice(n+1),t=t.slice(0,n)),{type:t,name:e}}))}function Lp(t){return function(){var e=this.__on;if(e){for(var n,r=0,i=-1,a=e.length;r<a;++r)n=e[r],t.type&&n.type!==t.type||n.name!==t.name?e[++i]=n:this.removeEventListener(n.type,n.listener,n.options);++i?e.length=i:delete this.__on}}}function Ip(t,e,n){return function(){var r,i=this.__on,a=function(t){return function(e){t.call(this,e,this.__data__)}}(e);if(i)for(var o=0,s=i.length;o<s;++o)if((r=i[o]).type===t.type&&r.name===t.name)return this.removeEventListener(r.type,r.listener,r.options),this.addEventListener(r.type,r.listener=a,r.options=n),void(r.value=e);this.addEventListener(t.type,a,n),r={type:t.type,name:t.name,value:e,listener:a,options:n},i?i.push(r):this.__on=[r]}}function Rp(t,e,n){var r=np(t),i=r.CustomEvent;"function"==typeof i?i=new i(e,n):(i=r.document.createEvent("Event"),n?(i.initEvent(e,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function Fp(t,e){return function(){return Rp(this,t,e)}}function Pp(t,e){return function(){return Rp(this,t,e.apply(this,arguments))}}fp.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var jp=[null];function Yp(t,e){this._groups=t,this._parents=e}function zp(){return new Yp([[document.documentElement]],jp)}Yp.prototype=zp.prototype={constructor:Yp,select:function(t){"function"!=typeof t&&(t=Md(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o,s=e[i],c=s.length,u=r[i]=new Array(c),l=0;l<c;++l)(a=s[l])&&(o=t.call(a,a.__data__,l,s))&&("__data__"in a&&(o.__data__=a.__data__),u[l]=o);return new Yp(r,this._parents)},selectAll:function(t){t="function"==typeof t?function(t){return function(){return Nd(t.apply(this,arguments))}}(t):Od(t);for(var e=this._groups,n=e.length,r=[],i=[],a=0;a<n;++a)for(var o,s=e[a],c=s.length,u=0;u<c;++u)(o=s[u])&&(r.push(t.call(o,o.__data__,u,s)),i.push(o));return new Yp(r,i)},selectChild:function(t){return this.select(null==t?Rd:function(t){return function(){return Id.call(this.children,t)}}("function"==typeof t?t:Ld(t)))},selectChildren:function(t){return this.selectAll(null==t?Pd:function(t){return function(){return Fd.call(this.children,t)}}("function"==typeof t?t:Ld(t)))},filter:function(t){"function"!=typeof t&&(t=Bd(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new Yp(r,this._parents)},data:function(t,e){if(!arguments.length)return Array.from(this,Hd);var n=e?qd:Ud,r=this._parents,i=this._groups;"function"!=typeof t&&(t=zd(t));for(var a=i.length,o=new Array(a),s=new Array(a),c=new Array(a),u=0;u<a;++u){var l=r[u],h=i[u],f=h.length,d=$d(t.call(l,l&&l.__data__,u,r)),p=d.length,y=s[u]=new Array(p),g=o[u]=new Array(p),m=c[u]=new Array(f);n(l,h,y,g,m,d,e);for(var v,b,_=0,x=0;_<p;++_)if(v=y[_]){for(_>=x&&(x=_+1);!(b=g[x])&&++x<p;);v._next=b||null}}return(o=new Yp(o,r))._enter=s,o._exit=c,o},enter:function(){return new Yp(this._enter||this._groups.map(jd),this._parents)},exit:function(){return new Yp(this._exit||this._groups.map(jd),this._parents)},join:function(t,e,n){var r=this.enter(),i=this,a=this.exit();return"function"==typeof t?(r=t(r))&&(r=r.selection()):r=r.append(t+""),null!=e&&(i=e(i))&&(i=i.selection()),null==n?a.remove():n(a),r&&i?r.merge(i).order():i},merge:function(t){for(var e=t.selection?t.selection():t,n=this._groups,r=e._groups,i=n.length,a=r.length,o=Math.min(i,a),s=new Array(i),c=0;c<o;++c)for(var u,l=n[c],h=r[c],f=l.length,d=s[c]=new Array(f),p=0;p<f;++p)(u=l[p]||h[p])&&(d[p]=u);for(;c<i;++c)s[c]=n[c];return new Yp(s,this._parents)},selection:function(){return this},order:function(){for(var t=this._groups,e=-1,n=t.length;++e<n;)for(var r,i=t[e],a=i.length-1,o=i[a];--a>=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function e(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}t||(t=Wd);for(var n=this._groups,r=n.length,i=new Array(r),a=0;a<r;++a){for(var o,s=n[a],c=s.length,u=i[a]=new Array(c),l=0;l<c;++l)(o=s[l])&&(u[l]=o);u.sort(e)}return new Yp(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){return Array.from(this)},node:function(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r=t[e],i=0,a=r.length;i<a;++i){var o=r[i];if(o)return o}return null},size:function(){let t=0;for(const e of this)++t;return t},empty:function(){return!this.node()},each:function(t){for(var e=this._groups,n=0,r=e.length;n<r;++n)for(var i,a=e[n],o=0,s=a.length;o<s;++o)(i=a[o])&&t.call(i,i.__data__,o,a);return this},attr:function(t,e){var n=Xd(t);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((null==e?n.local?Qd:Zd:"function"==typeof e?n.local?ep:tp:n.local?Jd:Kd)(n,e))},style:function(t,e,n){return arguments.length>1?this.each((null==e?rp:"function"==typeof e?ap:ip)(t,e,null==n?"":n)):op(this.node(),t)},property:function(t,e){return arguments.length>1?this.each((null==e?sp:"function"==typeof e?up:cp)(t,e)):this.node()[t]},classed:function(t,e){var n=lp(t+"");if(arguments.length<2){for(var r=hp(this.node()),i=-1,a=n.length;++i<a;)if(!r.contains(n[i]))return!1;return!0}return this.each(("function"==typeof e?mp:e?yp:gp)(n,e))},text:function(t){return arguments.length?this.each(null==t?vp:("function"==typeof t?_p:bp)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?xp:("function"==typeof t?kp:wp)(t)):this.node().innerHTML},raise:function(){return this.each(Tp)},lower:function(){return this.each(Ep)},append:function(t){var e="function"==typeof t?t:Ap(t);return this.select((function(){return this.appendChild(e.apply(this,arguments))}))},insert:function(t,e){var n="function"==typeof t?t:Ap(t),r=null==e?Mp:"function"==typeof e?e:Md(e);return this.select((function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(Np)},clone:function(t){return this.select(t?Op:Dp)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,e,n){var r,i,a=Bp(t+""),o=a.length;if(!(arguments.length<2)){for(s=e?Ip:Lp,r=0;r<o;++r)this.each(s(a[r],e,n));return this}var s=this.node().__on;if(s)for(var c,u=0,l=s.length;u<l;++u)for(r=0,c=s[u];r<o;++r)if((i=a[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,e){return this.each(("function"==typeof e?Pp:Fp)(t,e))},[Symbol.iterator]:function*(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r,i=t[e],a=0,o=i.length;a<o;++a)(r=i[a])&&(yield r)}};const Up=zp;var qp={value:()=>{}};function Hp(){for(var t,e=0,n=arguments.length,r={};e<n;++e){if(!(t=arguments[e]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new $p(r)}function $p(t){this._=t}function Wp(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function Vp(t,e){for(var n,r=0,i=t.length;r<i;++r)if((n=t[r]).name===e)return n.value}function Gp(t,e,n){for(var r=0,i=t.length;r<i;++r)if(t[r].name===e){t[r]=qp,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=n&&t.push({name:e,value:n}),t}$p.prototype=Hp.prototype={constructor:$p,on:function(t,e){var n,r=this._,i=Wp(t+"",r),a=-1,o=i.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++a<o;)if(n=(t=i[a]).type)r[n]=Gp(r[n],t.name,e);else if(null==e)for(n in r)r[n]=Gp(r[n],t.name,null);return this}for(;++a<o;)if((n=(t=i[a]).type)&&(n=Vp(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new $p(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,i=new Array(n),a=0;a<n;++a)i[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,i)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,a=r.length;i<a;++i)r[i].value.apply(e,n)}};const Xp=Hp;var Zp,Qp,Kp=0,Jp=0,ty=0,ey=0,ny=0,ry=0,iy="object"==typeof performance&&performance.now?performance:Date,ay="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function oy(){return ny||(ay(sy),ny=iy.now()+ry)}function sy(){ny=0}function cy(){this._call=this._time=this._next=null}function uy(t,e,n){var r=new cy;return r.restart(t,e,n),r}function ly(){ny=(ey=iy.now())+ry,Kp=Jp=0;try{!function(){oy(),++Kp;for(var t,e=Zp;e;)(t=ny-e._time)>=0&&e._call.call(void 0,t),e=e._next;--Kp}()}finally{Kp=0,function(){for(var t,e,n=Zp,r=1/0;n;)n._call?(r>n._time&&(r=n._time),t=n,n=n._next):(e=n._next,n._next=null,n=t?t._next=e:Zp=e);Qp=t,fy(r)}(),ny=0}}function hy(){var t=iy.now(),e=t-ey;e>1e3&&(ry-=e,ey=t)}function fy(t){Kp||(Jp&&(Jp=clearTimeout(Jp)),t-ny>24?(t<1/0&&(Jp=setTimeout(ly,t-iy.now()-ry)),ty&&(ty=clearInterval(ty))):(ty||(ey=iy.now(),ty=setInterval(hy,1e3)),Kp=1,ay(ly)))}function dy(t,e,n){var r=new cy;return e=null==e?0:+e,r.restart((n=>{r.stop(),t(n+e)}),e,n),r}cy.prototype=uy.prototype={constructor:cy,restart:function(t,e,n){if("function"!=typeof t)throw new TypeError("callback is not a function");n=(null==n?oy():+n)+(null==e?0:+e),this._next||Qp===this||(Qp?Qp._next=this:Zp=this,Qp=this),this._call=t,this._time=n,fy()},stop:function(){this._call&&(this._call=null,this._time=1/0,fy())}};var py=Xp("start","end","cancel","interrupt"),yy=[];function gy(t,e,n,r,i,a){var o=t.__transition;if(o){if(n in o)return}else t.__transition={};!function(t,e,n){var r,i=t.__transition;function a(c){var u,l,h,f;if(1!==n.state)return s();for(u in i)if((f=i[u]).name===n.name){if(3===f.state)return dy(a);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[u]):+u<e&&(f.state=6,f.timer.stop(),f.on.call("cancel",t,t.__data__,f.index,f.group),delete i[u])}if(dy((function(){3===n.state&&(n.state=4,n.timer.restart(o,n.delay,n.time),o(c))})),n.state=2,n.on.call("start",t,t.__data__,n.index,n.group),2===n.state){for(n.state=3,r=new Array(h=n.tween.length),u=0,l=-1;u<h;++u)(f=n.tween[u].value.call(t,t.__data__,n.index,n.group))&&(r[++l]=f);r.length=l+1}}function o(e){for(var i=e<n.duration?n.ease.call(null,e/n.duration):(n.timer.restart(s),n.state=5,1),a=-1,o=r.length;++a<o;)r[a].call(t,i);5===n.state&&(n.on.call("end",t,t.__data__,n.index,n.group),s())}function s(){for(var r in n.state=6,n.timer.stop(),delete i[e],i)return;delete t.__transition}i[e]=n,n.timer=uy((function(t){n.state=1,n.timer.restart(a,n.delay,n.time),n.delay<=t&&a(t-n.delay)}),0,n.time)}(t,n,{name:e,index:r,group:i,on:py,tween:yy,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:0})}function my(t,e){var n=by(t,e);if(n.state>0)throw new Error("too late; already scheduled");return n}function vy(t,e){var n=by(t,e);if(n.state>3)throw new Error("too late; already running");return n}function by(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function _y(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}var xy,wy=180/Math.PI,ky={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Ty(t,e,n,r,i,a){var o,s,c;return(o=Math.sqrt(t*t+e*e))&&(t/=o,e/=o),(c=t*n+e*r)&&(n-=t*c,r-=e*c),(s=Math.sqrt(n*n+r*r))&&(n/=s,r/=s,c/=s),t*r<e*n&&(t=-t,e=-e,c=-c,o=-o),{translateX:i,translateY:a,rotate:Math.atan2(e,t)*wy,skewX:Math.atan(c)*wy,scaleX:o,scaleY:s}}function Ey(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return function(a,o){var s=[],c=[];return a=t(a),o=t(o),function(t,r,i,a,o,s){if(t!==i||r!==a){var c=o.push("translate(",null,e,null,n);s.push({i:c-4,x:_y(t,i)},{i:c-2,x:_y(r,a)})}else(i||a)&&o.push("translate("+i+e+a+n)}(a.translateX,a.translateY,o.translateX,o.translateY,s,c),function(t,e,n,a){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:_y(t,e)})):e&&n.push(i(n)+"rotate("+e+r)}(a.rotate,o.rotate,s,c),function(t,e,n,a){t!==e?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:_y(t,e)}):e&&n.push(i(n)+"skewX("+e+r)}(a.skewX,o.skewX,s,c),function(t,e,n,r,a,o){if(t!==n||e!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:_y(t,n)},{i:s-2,x:_y(e,r)})}else 1===n&&1===r||a.push(i(a)+"scale("+n+","+r+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,c),a=o=null,function(t){for(var e,n=-1,r=c.length;++n<r;)s[(e=c[n]).i]=e.x(t);return s.join("")}}}var Cy=Ey((function(t){const e=new("function"==typeof DOMMatrix?DOMMatrix:WebKitCSSMatrix)(t+"");return e.isIdentity?ky:Ty(e.a,e.b,e.c,e.d,e.e,e.f)}),"px, ","px)","deg)"),Sy=Ey((function(t){return null==t?ky:(xy||(xy=document.createElementNS("http://www.w3.org/2000/svg","g")),xy.setAttribute("transform",t),(t=xy.transform.baseVal.consolidate())?Ty((t=t.matrix).a,t.b,t.c,t.d,t.e,t.f):ky)}),", ",")",")");function Ay(t,e){var n,r;return function(){var i=vy(this,t),a=i.tween;if(a!==n)for(var o=0,s=(r=n=a).length;o<s;++o)if(r[o].name===e){(r=r.slice()).splice(o,1);break}i.tween=r}}function My(t,e,n){var r,i;if("function"!=typeof n)throw new Error;return function(){var a=vy(this,t),o=a.tween;if(o!==r){i=(r=o).slice();for(var s={name:e,value:n},c=0,u=i.length;c<u;++c)if(i[c].name===e){i[c]=s;break}c===u&&i.push(s)}a.tween=i}}function Ny(t,e,n){var r=t._id;return t.each((function(){var t=vy(this,r);(t.value||(t.value={}))[e]=n.apply(this,arguments)})),function(t){return by(t,r).value[e]}}function Dy(t,e,n){t.prototype=e.prototype=n,n.constructor=t}function Oy(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function By(){}var Ly=.7,Iy=1.4285714285714286,Ry="\\s*([+-]?\\d+)\\s*",Fy="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",Py="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",jy=/^#([0-9a-f]{3,8})$/,Yy=new RegExp("^rgb\\("+[Ry,Ry,Ry]+"\\)$"),zy=new RegExp("^rgb\\("+[Py,Py,Py]+"\\)$"),Uy=new RegExp("^rgba\\("+[Ry,Ry,Ry,Fy]+"\\)$"),qy=new RegExp("^rgba\\("+[Py,Py,Py,Fy]+"\\)$"),Hy=new RegExp("^hsl\\("+[Fy,Py,Py]+"\\)$"),$y=new RegExp("^hsla\\("+[Fy,Py,Py,Fy]+"\\)$"),Wy={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function Vy(){return this.rgb().formatHex()}function Gy(){return this.rgb().formatRgb()}function Xy(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=jy.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?Zy(e):3===n?new tg(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?Qy(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?Qy(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=Yy.exec(t))?new tg(e[1],e[2],e[3],1):(e=zy.exec(t))?new tg(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=Uy.exec(t))?Qy(e[1],e[2],e[3],e[4]):(e=qy.exec(t))?Qy(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=Hy.exec(t))?ig(e[1],e[2]/100,e[3]/100,1):(e=$y.exec(t))?ig(e[1],e[2]/100,e[3]/100,e[4]):Wy.hasOwnProperty(t)?Zy(Wy[t]):"transparent"===t?new tg(NaN,NaN,NaN,0):null}function Zy(t){return new tg(t>>16&255,t>>8&255,255&t,1)}function Qy(t,e,n,r){return r<=0&&(t=e=n=NaN),new tg(t,e,n,r)}function Ky(t){return t instanceof By||(t=Xy(t)),t?new tg((t=t.rgb()).r,t.g,t.b,t.opacity):new tg}function Jy(t,e,n,r){return 1===arguments.length?Ky(t):new tg(t,e,n,null==r?1:r)}function tg(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function eg(){return"#"+rg(this.r)+rg(this.g)+rg(this.b)}function ng(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function rg(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function ig(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new og(t,e,n,r)}function ag(t){if(t instanceof og)return new og(t.h,t.s,t.l,t.opacity);if(t instanceof By||(t=Xy(t)),!t)return new og;if(t instanceof og)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new og(o,s,c,t.opacity)}function og(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function sg(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}function cg(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}Dy(By,Xy,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:Vy,formatHex:Vy,formatHsl:function(){return ag(this).formatHsl()},formatRgb:Gy,toString:Gy}),Dy(tg,Jy,Oy(By,{brighter:function(t){return t=null==t?Iy:Math.pow(Iy,t),new tg(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Ly:Math.pow(Ly,t),new tg(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:eg,formatHex:eg,formatRgb:ng,toString:ng})),Dy(og,(function(t,e,n,r){return 1===arguments.length?ag(t):new og(t,e,n,null==r?1:r)}),Oy(By,{brighter:function(t){return t=null==t?Iy:Math.pow(Iy,t),new og(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Ly:Math.pow(Ly,t),new og(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new tg(sg(t>=240?t-240:t+120,i,r),sg(t,i,r),sg(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const ug=t=>()=>t;function lg(t,e){var n=e-t;return n?function(t,e){return function(n){return t+n*e}}(t,n):ug(isNaN(t)?e:t)}const hg=function t(e){var n=function(t){return 1==(t=+t)?lg:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):ug(isNaN(e)?n:e)}}(e);function r(t,e){var r=n((t=Jy(t)).r,(e=Jy(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=lg(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function fg(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=Jy(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}fg((function(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return cg((n-r/e)*e,o,i,a,s)}})),fg((function(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return cg((n-r/e)*e,i,a,o,s)}}));var dg=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,pg=new RegExp(dg.source,"g");function yg(t,e){var n,r,i,a=dg.lastIndex=pg.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=dg.exec(t))&&(r=pg.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:_y(n,r)})),a=pg.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})}function gg(t,e){var n;return("number"==typeof e?_y:e instanceof Xy?hg:(n=Xy(e))?(e=n,hg):yg)(t,e)}function mg(t){return function(){this.removeAttribute(t)}}function vg(t){return function(){this.removeAttributeNS(t.space,t.local)}}function bg(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttribute(t);return o===a?null:o===r?i:i=e(r=o,n)}}function _g(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttributeNS(t.space,t.local);return o===a?null:o===r?i:i=e(r=o,n)}}function xg(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttribute(t))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttribute(t)}}function wg(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttributeNS(t.space,t.local))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttributeNS(t.space,t.local)}}function kg(t,e){return function(n){this.setAttribute(t,e.call(this,n))}}function Tg(t,e){return function(n){this.setAttributeNS(t.space,t.local,e.call(this,n))}}function Eg(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&Tg(t,i)),n}return i._value=e,i}function Cg(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&kg(t,i)),n}return i._value=e,i}function Sg(t,e){return function(){my(this,t).delay=+e.apply(this,arguments)}}function Ag(t,e){return e=+e,function(){my(this,t).delay=e}}function Mg(t,e){return function(){vy(this,t).duration=+e.apply(this,arguments)}}function Ng(t,e){return e=+e,function(){vy(this,t).duration=e}}function Dg(t,e){if("function"!=typeof e)throw new Error;return function(){vy(this,t).ease=e}}function Og(t,e,n){var r,i,a=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var e=t.indexOf(".");return e>=0&&(t=t.slice(0,e)),!t||"start"===t}))}(e)?my:vy;return function(){var o=a(this,t),s=o.on;s!==r&&(i=(r=s).copy()).on(e,n),o.on=i}}var Bg=Up.prototype.constructor;function Lg(t){return function(){this.style.removeProperty(t)}}function Ig(t,e,n){return function(r){this.style.setProperty(t,e.call(this,r),n)}}function Rg(t,e,n){var r,i;function a(){var a=e.apply(this,arguments);return a!==i&&(r=(i=a)&&Ig(t,a,n)),r}return a._value=e,a}function Fg(t){return function(e){this.textContent=t.call(this,e)}}function Pg(t){var e,n;function r(){var r=t.apply(this,arguments);return r!==n&&(e=(n=r)&&Fg(r)),e}return r._value=t,r}var jg=0;function Yg(t,e,n,r){this._groups=t,this._parents=e,this._name=n,this._id=r}function zg(){return++jg}var Ug=Up.prototype;Yg.prototype=function(t){return Up().transition(t)}.prototype={constructor:Yg,select:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=Md(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;o<i;++o)for(var s,c,u=r[o],l=u.length,h=a[o]=new Array(l),f=0;f<l;++f)(s=u[f])&&(c=t.call(s,s.__data__,f,u))&&("__data__"in s&&(c.__data__=s.__data__),h[f]=c,gy(h[f],e,n,f,h,by(s,n)));return new Yg(a,this._parents,e,n)},selectAll:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=Od(t));for(var r=this._groups,i=r.length,a=[],o=[],s=0;s<i;++s)for(var c,u=r[s],l=u.length,h=0;h<l;++h)if(c=u[h]){for(var f,d=t.call(c,c.__data__,h,u),p=by(c,n),y=0,g=d.length;y<g;++y)(f=d[y])&&gy(f,e,n,y,d,p);a.push(d),o.push(c)}return new Yg(a,o,e,n)},selectChild:Ug.selectChild,selectChildren:Ug.selectChildren,filter:function(t){"function"!=typeof t&&(t=Bd(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new Yg(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new Yg(o,this._parents,this._name,this._id)},selection:function(){return new Bg(this._groups,this._parents)},transition:function(){for(var t=this._name,e=this._id,n=zg(),r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)if(o=s[u]){var l=by(o,e);gy(o,t,n,u,s,{time:l.time+l.delay+l.duration,delay:0,duration:l.duration,ease:l.ease})}return new Yg(r,this._parents,t,n)},call:Ug.call,nodes:Ug.nodes,node:Ug.node,size:Ug.size,empty:Ug.empty,each:Ug.each,on:function(t,e){var n=this._id;return arguments.length<2?by(this.node(),n).on.on(t):this.each(Og(n,t,e))},attr:function(t,e){var n=Xd(t),r="transform"===n?Sy:gg;return this.attrTween(t,"function"==typeof e?(n.local?wg:xg)(n,r,Ny(this,"attr."+t,e)):null==e?(n.local?vg:mg)(n):(n.local?_g:bg)(n,r,e))},attrTween:function(t,e){var n="attr."+t;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(null==e)return this.tween(n,null);if("function"!=typeof e)throw new Error;var r=Xd(t);return this.tween(n,(r.local?Eg:Cg)(r,e))},style:function(t,e,n){var r="transform"==(t+="")?Cy:gg;return null==e?this.styleTween(t,function(t,e){var n,r,i;return function(){var a=op(this,t),o=(this.style.removeProperty(t),op(this,t));return a===o?null:a===n&&o===r?i:i=e(n=a,r=o)}}(t,r)).on("end.style."+t,Lg(t)):"function"==typeof e?this.styleTween(t,function(t,e,n){var r,i,a;return function(){var o=op(this,t),s=n(this),c=s+"";return null==s&&(this.style.removeProperty(t),c=s=op(this,t)),o===c?null:o===r&&c===i?a:(i=c,a=e(r=o,s))}}(t,r,Ny(this,"style."+t,e))).each(function(t,e){var n,r,i,a,o="style."+e,s="end."+o;return function(){var c=vy(this,t),u=c.on,l=null==c.value[o]?a||(a=Lg(e)):void 0;u===n&&i===l||(r=(n=u).copy()).on(s,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,e,n){var r,i,a=n+"";return function(){var o=op(this,t);return o===a?null:o===r?i:i=e(r=o,n)}}(t,r,e),n).on("end.style."+t,null)},styleTween:function(t,e,n){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==e)return this.tween(r,null);if("function"!=typeof e)throw new Error;return this.tween(r,Rg(t,e,null==n?"":n))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var e=t(this);this.textContent=null==e?"":e}}(Ny(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(null==t)return this.tween(e,null);if("function"!=typeof t)throw new Error;return this.tween(e,Pg(t))},remove:function(){return this.on("end.remove",function(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}(this._id))},tween:function(t,e){var n=this._id;if(t+="",arguments.length<2){for(var r,i=by(this.node(),n).tween,a=0,o=i.length;a<o;++a)if((r=i[a]).name===t)return r.value;return null}return this.each((null==e?Ay:My)(n,t,e))},delay:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Sg:Ag)(e,t)):by(this.node(),e).delay},duration:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Mg:Ng)(e,t)):by(this.node(),e).duration},ease:function(t){var e=this._id;return arguments.length?this.each(Dg(e,t)):by(this.node(),e).ease},easeVarying:function(t){if("function"!=typeof t)throw new Error;return this.each(function(t,e){return function(){var n=e.apply(this,arguments);if("function"!=typeof n)throw new Error;vy(this,t).ease=n}}(this._id,t))},end:function(){var t,e,n=this,r=n._id,i=n.size();return new Promise((function(a,o){var s={value:o},c={value:function(){0==--i&&a()}};n.each((function(){var n=vy(this,r),i=n.on;i!==t&&((e=(t=i).copy())._.cancel.push(s),e._.interrupt.push(s),e._.end.push(c)),n.on=e})),0===i&&a()}))},[Symbol.iterator]:Ug[Symbol.iterator]};var qg={time:null,delay:0,duration:250,ease:function(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}};function Hg(t,e){for(var n;!(n=t.__transition)||!(n=n[e]);)if(!(t=t.parentNode))throw new Error(`transition ${e} not found`);return n}function $g(t,e,n){this.k=t,this.x=e,this.y=n}Up.prototype.interrupt=function(t){return this.each((function(){!function(t,e){var n,r,i,a=t.__transition,o=!0;if(a){for(i in e=null==e?null:e+"",a)(n=a[i]).name===e?(r=n.state>2&&n.state<5,n.state=6,n.timer.stop(),n.on.call(r?"interrupt":"cancel",t,t.__data__,n.index,n.group),delete a[i]):o=!1;o&&delete t.__transition}}(this,t)}))},Up.prototype.transition=function(t){var e,n;t instanceof Yg?(e=t._id,t=t._name):(e=zg(),(n=qg).time=oy(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)(o=s[u])&&gy(o,t,e,u,s,n||Hg(o,e));return new Yg(r,this._parents,t,e)},$g.prototype={constructor:$g,scale:function(t){return 1===t?this:new $g(this.k*t,this.x,this.y)},translate:function(t,e){return 0===t&0===e?this:new $g(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}},new $g(1,0,0),$g.prototype;var Wg="comm",Vg="rule",Gg="decl",Xg=Math.abs,Zg=String.fromCharCode;function Qg(t){return t.trim()}function Kg(t,e,n){return t.replace(e,n)}function Jg(t,e){return t.indexOf(e)}function tm(t,e){return 0|t.charCodeAt(e)}function em(t,e,n){return t.slice(e,n)}function nm(t){return t.length}function rm(t){return t.length}function im(t,e){return e.push(t),t}function am(t,e){for(var n="",r=rm(t),i=0;i<r;i++)n+=e(t[i],i,t,e)||"";return n}function om(t,e,n,r){switch(t.type){case"@import":case Gg:return t.return=t.return||t.value;case Wg:return"";case"@keyframes":return t.return=t.value+"{"+am(t.children,r)+"}";case Vg:t.value=t.props.join(",")}return nm(n=am(t.children,r))?t.return=t.value+"{"+n+"}":""}Object.assign;var sm=1,cm=1,um=0,lm=0,hm=0,fm="";function dm(t,e,n,r,i,a,o){return{value:t,root:e,parent:n,type:r,props:i,children:a,line:sm,column:cm,length:o,return:""}}function pm(){return hm=lm>0?tm(fm,--lm):0,cm--,10===hm&&(cm=1,sm--),hm}function ym(){return hm=lm<um?tm(fm,lm++):0,cm++,10===hm&&(cm=1,sm++),hm}function gm(){return tm(fm,lm)}function mm(){return lm}function vm(t,e){return em(fm,t,e)}function bm(t){switch(t){case 0:case 9:case 10:case 13:case 32:return 5;case 33:case 43:case 44:case 47:case 62:case 64:case 126:case 59:case 123:case 125:return 4;case 58:return 3;case 34:case 39:case 40:case 91:return 2;case 41:case 93:return 1}return 0}function _m(t){return Qg(vm(lm-1,km(91===t?t+2:40===t?t+1:t)))}function xm(t){for(;(hm=gm())&&hm<33;)ym();return bm(t)>2||bm(hm)>3?"":" "}function wm(t,e){for(;--e&&ym()&&!(hm<48||hm>102||hm>57&&hm<65||hm>70&&hm<97););return vm(t,mm()+(e<6&&32==gm()&&32==ym()))}function km(t){for(;ym();)switch(hm){case t:return lm;case 34:case 39:34!==t&&39!==t&&km(hm);break;case 40:41===t&&km(t);break;case 92:ym()}return lm}function Tm(t,e){for(;ym()&&t+hm!==57&&(t+hm!==84||47!==gm()););return"/*"+vm(e,lm-1)+"*"+Zg(47===t?t:ym())}function Em(t){for(;!bm(gm());)ym();return vm(t,lm)}function Cm(t){return function(t){return fm="",t}(Sm("",null,null,null,[""],t=function(t){return sm=cm=1,um=nm(fm=t),lm=0,[]}(t),0,[0],t))}function Sm(t,e,n,r,i,a,o,s,c){for(var u=0,l=0,h=o,f=0,d=0,p=0,y=1,g=1,m=1,v=0,b="",_=i,x=a,w=r,k=b;g;)switch(p=v,v=ym()){case 40:if(108!=p&&58==k.charCodeAt(h-1)){-1!=Jg(k+=Kg(_m(v),"&","&\f"),"&\f")&&(m=-1);break}case 34:case 39:case 91:k+=_m(v);break;case 9:case 10:case 13:case 32:k+=xm(p);break;case 92:k+=wm(mm()-1,7);continue;case 47:switch(gm()){case 42:case 47:im(Mm(Tm(ym(),mm()),e,n),c);break;default:k+="/"}break;case 123*y:s[u++]=nm(k)*m;case 125*y:case 59:case 0:switch(v){case 0:case 125:g=0;case 59+l:d>0&&nm(k)-h&&im(d>32?Nm(k+";",r,n,h-1):Nm(Kg(k," ","")+";",r,n,h-2),c);break;case 59:k+=";";default:if(im(w=Am(k,e,n,u,l,i,s,b,_=[],x=[],h),a),123===v)if(0===l)Sm(k,e,w,w,_,a,h,s,x);else switch(f){case 100:case 109:case 115:Sm(t,w,w,r&&im(Am(t,w,w,0,0,i,s,b,i,_=[],h),x),i,x,h,s,r?_:x);break;default:Sm(k,w,w,w,[""],x,0,s,x)}}u=l=d=0,y=m=1,b=k="",h=o;break;case 58:h=1+nm(k),d=p;default:if(y<1)if(123==v)--y;else if(125==v&&0==y++&&125==pm())continue;switch(k+=Zg(v),v*y){case 38:m=l>0?1:(k+="\f",-1);break;case 44:s[u++]=(nm(k)-1)*m,m=1;break;case 64:45===gm()&&(k+=_m(ym())),f=gm(),l=h=nm(b=k+=Em(mm())),v++;break;case 45:45===p&&2==nm(k)&&(y=0)}}return a}function Am(t,e,n,r,i,a,o,s,c,u,l){for(var h=i-1,f=0===i?a:[""],d=rm(f),p=0,y=0,g=0;p<r;++p)for(var m=0,v=em(t,h+1,h=Xg(y=o[p])),b=t;m<d;++m)(b=Qg(y>0?f[m]+" "+v:Kg(v,/&\f/g,f[m])))&&(c[g++]=b);return dm(t,e,n,0===i?Vg:s,c,u,l)}function Mm(t,e,n){return dm(t,e,n,Wg,Zg(hm),em(t,2,-2),0)}function Nm(t,e,n,r){return dm(t,e,n,Gg,em(t,0,r),em(t,r+1,-1),r)}const Dm="8.13.10";var Om=n(9609),Bm=n(7856),Lm=n.n(Bm),Im=function(t){var e=t.replace(/\\u[\dA-F]{4}/gi,(function(t){return String.fromCharCode(parseInt(t.replace(/\\u/g,""),16))}));return e=(e=(e=e.replace(/\\x([0-9a-f]{2})/gi,(function(t,e){return String.fromCharCode(parseInt(e,16))}))).replace(/\\[\d\d\d]{3}/gi,(function(t){return String.fromCharCode(parseInt(t.replace(/\\/g,""),8))}))).replace(/\\[\d\d\d]{2}/gi,(function(t){return String.fromCharCode(parseInt(t.replace(/\\/g,""),8))}))},Rm=function(t){for(var e="",n=0;n>=0;){if(!((n=t.indexOf("<script"))>=0)){e+=t,n=-1;break}e+=t.substr(0,n),(n=(t=t.substr(n+1)).indexOf("<\/script>"))>=0&&(n+=9,t=t.substr(n))}var r=Im(e);return(r=(r=(r=r.replace(/script>/gi,"#")).replace(/javascript:/gi,"#")).replace(/onerror=/gi,"onerror:")).replace(/<iframe/gi,"")},Fm=function(t,e){if(!t)return t;var n=Lm().sanitize(function(t,e){var n=t,r=!0;if(!e.flowchart||!1!==e.flowchart.htmlLabels&&"false"!==e.flowchart.htmlLabels||(r=!1),r){var i=e.securityLevel;"antiscript"===i||"strict"===i?n=Rm(n):"loose"!==i&&(n=(n=(n=Ym(n)).replace(/</g,"<").replace(/>/g,">")).replace(/=/g,"="),n=jm(n))}return n}(t,e));return n},Pm=/<br\s*\/?>/gi,jm=function(t){return t.replace(/#br#/g,"<br/>")},Ym=function(t){return t.replace(Pm,"#br#")},zm=function(t){return"false"!==t&&!1!==t};const Um={getRows:function(t){if(!t)return 1;var e=Ym(t);return(e=e.replace(/\\n/g,"#br#")).split("#br#")},sanitizeText:Fm,sanitizeTextOrArray:function(t,e){return"string"==typeof t?Fm(t,e):t.flat().map((function(t){return Fm(t,e)}))},hasBreaks:function(t){return Pm.test(t)},splitBreaks:function(t){return t.split(Pm)},lineBreakRegex:Pm,removeScript:Rm,getUrl:function(t){var e="";return t&&(e=(e=(e=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),e},evaluate:zm,removeEscapes:Im};var qm=n(8613),Hm=function(t,e){return e?(0,qm.adjust)(t,{s:-40,l:10}):(0,qm.adjust)(t,{s:-40,l:-10})};function $m(t){return $m="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},$m(t)}function Wm(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var Vm=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.darkMode=!1,this.primaryColor="#fff4dd",this.noteBkgColor="#fff5ad",this.noteTextColor="#333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px"}var e,n;return e=t,n=[{key:"updateColors",value:function(){this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#ddd":"#333"),this.secondaryColor=this.secondaryColor||(0,qm.adjust)(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||(0,qm.adjust)(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||Hm(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||Hm(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||Hm(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||Hm(this.noteBkgColor,this.darkMode),this.noteBkgColor=this.noteBkgColor||"#fff5ad",this.noteTextColor=this.noteTextColor||"#333",this.secondaryTextColor=this.secondaryTextColor||(0,qm.invert)(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||(0,qm.invert)(this.tertiaryColor),this.lineColor=this.lineColor||(0,qm.invert)(this.background),this.textColor=this.textColor||this.primaryTextColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.primaryBorderColor,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?(0,qm.darken)(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||"grey",this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||(0,qm.darken)(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||(0,qm.invert)(this.lineColor),this.sectionBkgColor=this.sectionBkgColor||this.tertiaryColor,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||this.secondaryColor,this.sectionBkgColor2=this.sectionBkgColor2||this.primaryColor,this.excludeBkgColor=this.excludeBkgColor||"#eeeeee",this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||this.primaryColor,this.activeTaskBorderColor=this.activeTaskBorderColor||this.primaryColor,this.activeTaskBkgColor=this.activeTaskBkgColor||(0,qm.lighten)(this.primaryColor,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.taskTextColor=this.taskTextColor||this.textColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||this.tertiaryColor,this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.specialStateColor=this.lineColor,this.classText=this.classText||this.textColor,this.fillType0=this.fillType0||this.primaryColor,this.fillType1=this.fillType1||this.secondaryColor,this.fillType2=this.fillType2||(0,qm.adjust)(this.primaryColor,{h:64}),this.fillType3=this.fillType3||(0,qm.adjust)(this.secondaryColor,{h:64}),this.fillType4=this.fillType4||(0,qm.adjust)(this.primaryColor,{h:-64}),this.fillType5=this.fillType5||(0,qm.adjust)(this.secondaryColor,{h:-64}),this.fillType6=this.fillType6||(0,qm.adjust)(this.primaryColor,{h:128}),this.fillType7=this.fillType7||(0,qm.adjust)(this.secondaryColor,{h:128}),this.pie1=this.pie1||this.primaryColor,this.pie2=this.pie2||this.secondaryColor,this.pie3=this.pie3||this.tertiaryColor,this.pie4=this.pie4||(0,qm.adjust)(this.primaryColor,{l:-10}),this.pie5=this.pie5||(0,qm.adjust)(this.secondaryColor,{l:-10}),this.pie6=this.pie6||(0,qm.adjust)(this.tertiaryColor,{l:-10}),this.pie7=this.pie7||(0,qm.adjust)(this.primaryColor,{h:60,l:-10}),this.pie8=this.pie8||(0,qm.adjust)(this.primaryColor,{h:-60,l:-10}),this.pie9=this.pie9||(0,qm.adjust)(this.primaryColor,{h:120,l:0}),this.pie10=this.pie10||(0,qm.adjust)(this.primaryColor,{h:60,l:-20}),this.pie11=this.pie11||(0,qm.adjust)(this.primaryColor,{h:-60,l:-20}),this.pie12=this.pie12||(0,qm.adjust)(this.primaryColor,{h:120,l:-10}),this.pieTitleTextSize=this.pieTitleTextSize||"25px",this.pieTitleTextColor=this.pieTitleTextColor||this.taskTextDarkColor,this.pieSectionTextSize=this.pieSectionTextSize||"17px",this.pieSectionTextColor=this.pieSectionTextColor||this.textColor,this.pieLegendTextSize=this.pieLegendTextSize||"17px",this.pieLegendTextColor=this.pieLegendTextColor||this.taskTextDarkColor,this.pieStrokeColor=this.pieStrokeColor||"black",this.pieStrokeWidth=this.pieStrokeWidth||"2px",this.pieOpacity=this.pieOpacity||"0.7",this.requirementBackground=this.requirementBackground||this.primaryColor,this.requirementBorderColor=this.requirementBorderColor||this.primaryBorderColor,this.requirementBorderSize=this.requirementBorderSize||this.primaryBorderColor,this.requirementTextColor=this.requirementTextColor||this.primaryTextColor,this.relationColor=this.relationColor||this.lineColor,this.relationLabelBackground=this.relationLabelBackground||(this.darkMode?(0,qm.darken)(this.secondaryColor,30):this.secondaryColor),this.relationLabelColor=this.relationLabelColor||this.actorTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===$m(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}],n&&Wm(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();function Gm(t){return Gm="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Gm(t)}function Xm(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var Zm=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#333",this.primaryColor="#1f2020",this.secondaryColor=(0,qm.lighten)(this.primaryColor,16),this.tertiaryColor=(0,qm.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=Hm(this.primaryColor,this.darkMode),this.secondaryBorderColor=Hm(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Hm(this.tertiaryColor,this.darkMode),this.primaryTextColor=(0,qm.invert)(this.primaryColor),this.secondaryTextColor=(0,qm.invert)(this.secondaryColor),this.tertiaryTextColor=(0,qm.invert)(this.tertiaryColor),this.lineColor=(0,qm.invert)(this.background),this.textColor=(0,qm.invert)(this.background),this.mainBkg="#1f2020",this.secondBkg="calculated",this.mainContrastColor="lightgrey",this.darkTextColor=(0,qm.lighten)((0,qm.invert)("#323D47"),10),this.lineColor="calculated",this.border1="#81B1DB",this.border2=(0,qm.rgba)(255,255,255,.25),this.arrowheadColor="calculated",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="#181818",this.textColor="#ccc",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#F9FFFE",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="calculated",this.activationBkgColor="calculated",this.sequenceNumberColor="black",this.sectionBkgColor=(0,qm.darken)("#EAE8D9",30),this.altSectionBkgColor="calculated",this.sectionBkgColor2="#EAE8D9",this.taskBorderColor=(0,qm.rgba)(255,255,255,70),this.taskBkgColor="calculated",this.taskTextColor="calculated",this.taskTextLightColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor=(0,qm.rgba)(255,255,255,50),this.activeTaskBkgColor="#81B1DB",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="grey",this.critBorderColor="#E83737",this.critBkgColor="#E83737",this.taskTextDarkColor="calculated",this.todayLineColor="#DB5757",this.labelColor="calculated",this.errorBkgColor="#a44141",this.errorTextColor="#ddd"}var e,n;return e=t,n=[{key:"updateColors",value:function(){this.secondBkg=(0,qm.lighten)(this.mainBkg,16),this.lineColor=this.mainContrastColor,this.arrowheadColor=this.mainContrastColor,this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.edgeLabelBackground=(0,qm.lighten)(this.labelBackground,25),this.actorBorder=this.border1,this.actorBkg=this.mainBkg,this.actorTextColor=this.mainContrastColor,this.actorLineColor=this.mainContrastColor,this.signalColor=this.mainContrastColor,this.signalTextColor=this.mainContrastColor,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.mainContrastColor,this.loopTextColor=this.mainContrastColor,this.noteBorderColor=this.secondaryBorderColor,this.noteBkgColor=this.secondBkg,this.noteTextColor=this.secondaryTextColor,this.activationBorderColor=this.border1,this.activationBkgColor=this.secondBkg,this.altSectionBkgColor=this.background,this.taskBkgColor=(0,qm.lighten)(this.mainBkg,23),this.taskTextColor=this.darkTextColor,this.taskTextLightColor=this.mainContrastColor,this.taskTextOutsideColor=this.taskTextLightColor,this.gridColor=this.mainContrastColor,this.doneTaskBkgColor=this.mainContrastColor,this.taskTextDarkColor=this.darkTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#555",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.primaryBorderColor,this.specialStateColor="#f4f4f4",this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=(0,qm.adjust)(this.primaryColor,{h:64}),this.fillType3=(0,qm.adjust)(this.secondaryColor,{h:64}),this.fillType4=(0,qm.adjust)(this.primaryColor,{h:-64}),this.fillType5=(0,qm.adjust)(this.secondaryColor,{h:-64}),this.fillType6=(0,qm.adjust)(this.primaryColor,{h:128}),this.fillType7=(0,qm.adjust)(this.secondaryColor,{h:128}),this.pie1=this.pie1||"#0b0000",this.pie2=this.pie2||"#4d1037",this.pie3=this.pie3||"#3f5258",this.pie4=this.pie4||"#4f2f1b",this.pie5=this.pie5||"#6e0a0a",this.pie6=this.pie6||"#3b0048",this.pie7=this.pie7||"#995a01",this.pie8=this.pie8||"#154706",this.pie9=this.pie9||"#161722",this.pie10=this.pie10||"#00296f",this.pie11=this.pie11||"#01629c",this.pie12=this.pie12||"#010029",this.pieTitleTextSize=this.pieTitleTextSize||"25px",this.pieTitleTextColor=this.pieTitleTextColor||this.taskTextDarkColor,this.pieSectionTextSize=this.pieSectionTextSize||"17px",this.pieSectionTextColor=this.pieSectionTextColor||this.textColor,this.pieLegendTextSize=this.pieLegendTextSize||"17px",this.pieLegendTextColor=this.pieLegendTextColor||this.taskTextDarkColor,this.pieStrokeColor=this.pieStrokeColor||"black",this.pieStrokeWidth=this.pieStrokeWidth||"2px",this.pieOpacity=this.pieOpacity||"0.7",this.classText=this.primaryTextColor,this.requirementBackground=this.requirementBackground||this.primaryColor,this.requirementBorderColor=this.requirementBorderColor||this.primaryBorderColor,this.requirementBorderSize=this.requirementBorderSize||this.primaryBorderColor,this.requirementTextColor=this.requirementTextColor||this.primaryTextColor,this.relationColor=this.relationColor||this.lineColor,this.relationLabelBackground=this.relationLabelBackground||(this.darkMode?(0,qm.darken)(this.secondaryColor,30):this.secondaryColor),this.relationLabelColor=this.relationLabelColor||this.actorTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===Gm(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}],n&&Xm(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();function Qm(t){return Qm="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Qm(t)}function Km(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var Jm=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.primaryColor="#ECECFF",this.secondaryColor=(0,qm.adjust)(this.primaryColor,{h:120}),this.secondaryColor="#ffffde",this.tertiaryColor=(0,qm.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=Hm(this.primaryColor,this.darkMode),this.secondaryBorderColor=Hm(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Hm(this.tertiaryColor,this.darkMode),this.primaryTextColor=(0,qm.invert)(this.primaryColor),this.secondaryTextColor=(0,qm.invert)(this.secondaryColor),this.tertiaryTextColor=(0,qm.invert)(this.tertiaryColor),this.lineColor=(0,qm.invert)(this.background),this.textColor=(0,qm.invert)(this.background),this.background="white",this.mainBkg="#ECECFF",this.secondBkg="#ffffde",this.lineColor="#333333",this.border1="#9370DB",this.border2="#aaaa33",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="#e8e8e8",this.textColor="#333",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="grey",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="calculated",this.sectionBkgColor2="calculated",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="calculated",this.taskTextColor=this.taskTextLightColor,this.taskTextDarkColor="calculated",this.taskTextOutsideColor=this.taskTextDarkColor,this.taskTextClickableColor="calculated",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBorderColor="calculated",this.critBkgColor="calculated",this.todayLineColor="calculated",this.sectionBkgColor=(0,qm.rgba)(102,102,255,.49),this.altSectionBkgColor="white",this.sectionBkgColor2="#fff400",this.taskBorderColor="#534fbc",this.taskBkgColor="#8a90dd",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="#534fbc",this.activeTaskBkgColor="#bfc7ff",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222",this.updateColors()}var e,n;return e=t,n=[{key:"updateColors",value:function(){this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.titleColor=this.textColor,this.edgeLabelBackground=this.labelBackground,this.actorBorder=(0,qm.lighten)(this.border1,23),this.actorBkg=this.mainBkg,this.labelBoxBkgColor=this.actorBkg,this.signalColor=this.textColor,this.signalTextColor=this.textColor,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.actorTextColor,this.loopTextColor=this.actorTextColor,this.noteBorderColor=this.border2,this.noteTextColor=this.actorTextColor,this.taskTextColor=this.taskTextLightColor,this.taskTextOutsideColor=this.taskTextDarkColor,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f0f0f0",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.specialStateColor=this.lineColor,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=(0,qm.adjust)(this.primaryColor,{h:64}),this.fillType3=(0,qm.adjust)(this.secondaryColor,{h:64}),this.fillType4=(0,qm.adjust)(this.primaryColor,{h:-64}),this.fillType5=(0,qm.adjust)(this.secondaryColor,{h:-64}),this.fillType6=(0,qm.adjust)(this.primaryColor,{h:128}),this.fillType7=(0,qm.adjust)(this.secondaryColor,{h:128}),this.pie1=this.pie1||this.primaryColor,this.pie2=this.pie2||this.secondaryColor,this.pie3=this.pie3||(0,qm.adjust)(this.tertiaryColor,{l:-40}),this.pie4=this.pie4||(0,qm.adjust)(this.primaryColor,{l:-10}),this.pie5=this.pie5||(0,qm.adjust)(this.secondaryColor,{l:-30}),this.pie6=this.pie6||(0,qm.adjust)(this.tertiaryColor,{l:-20}),this.pie7=this.pie7||(0,qm.adjust)(this.primaryColor,{h:60,l:-20}),this.pie8=this.pie8||(0,qm.adjust)(this.primaryColor,{h:-60,l:-40}),this.pie9=this.pie9||(0,qm.adjust)(this.primaryColor,{h:120,l:-40}),this.pie10=this.pie10||(0,qm.adjust)(this.primaryColor,{h:60,l:-40}),this.pie11=this.pie11||(0,qm.adjust)(this.primaryColor,{h:-90,l:-40}),this.pie12=this.pie12||(0,qm.adjust)(this.primaryColor,{h:120,l:-30}),this.pieTitleTextSize=this.pieTitleTextSize||"25px",this.pieTitleTextColor=this.pieTitleTextColor||this.taskTextDarkColor,this.pieSectionTextSize=this.pieSectionTextSize||"17px",this.pieSectionTextColor=this.pieSectionTextColor||this.textColor,this.pieLegendTextSize=this.pieLegendTextSize||"17px",this.pieLegendTextColor=this.pieLegendTextColor||this.taskTextDarkColor,this.pieStrokeColor=this.pieStrokeColor||"black",this.pieStrokeWidth=this.pieStrokeWidth||"2px",this.pieOpacity=this.pieOpacity||"0.7",this.requirementBackground=this.requirementBackground||this.primaryColor,this.requirementBorderColor=this.requirementBorderColor||this.primaryBorderColor,this.requirementBorderSize=this.requirementBorderSize||this.primaryBorderColor,this.requirementTextColor=this.requirementTextColor||this.primaryTextColor,this.relationColor=this.relationColor||this.lineColor,this.relationLabelBackground=this.relationLabelBackground||this.labelBackground,this.relationLabelColor=this.relationLabelColor||this.actorTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===Qm(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}],n&&Km(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();function tv(t){return tv="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},tv(t)}function ev(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var nv=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.primaryColor="#cde498",this.secondaryColor="#cdffb2",this.background="white",this.mainBkg="#cde498",this.secondBkg="#cdffb2",this.lineColor="green",this.border1="#13540c",this.border2="#6eaa49",this.arrowheadColor="green",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.tertiaryColor=(0,qm.lighten)("#cde498",10),this.primaryBorderColor=Hm(this.primaryColor,this.darkMode),this.secondaryBorderColor=Hm(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Hm(this.tertiaryColor,this.darkMode),this.primaryTextColor=(0,qm.invert)(this.primaryColor),this.secondaryTextColor=(0,qm.invert)(this.secondaryColor),this.tertiaryTextColor=(0,qm.invert)(this.primaryColor),this.lineColor=(0,qm.invert)(this.background),this.textColor=(0,qm.invert)(this.background),this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#333",this.edgeLabelBackground="#e8e8e8",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="grey",this.signalColor="#333",this.signalTextColor="#333",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="#326932",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="#6eaa49",this.altSectionBkgColor="white",this.sectionBkgColor2="#6eaa49",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="#487e3a",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222"}var e,n;return e=t,n=[{key:"updateColors",value:function(){this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.actorBorder=(0,qm.darken)(this.mainBkg,20),this.actorBkg=this.mainBkg,this.labelBoxBkgColor=this.actorBkg,this.labelTextColor=this.actorTextColor,this.loopTextColor=this.actorTextColor,this.noteBorderColor=this.border2,this.noteTextColor=this.actorTextColor,this.taskBorderColor=this.border1,this.taskTextColor=this.taskTextLightColor,this.taskTextOutsideColor=this.taskTextDarkColor,this.activeTaskBorderColor=this.taskBorderColor,this.activeTaskBkgColor=this.mainBkg,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f0f0f0",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.primaryBorderColor,this.specialStateColor=this.lineColor,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=(0,qm.adjust)(this.primaryColor,{h:64}),this.fillType3=(0,qm.adjust)(this.secondaryColor,{h:64}),this.fillType4=(0,qm.adjust)(this.primaryColor,{h:-64}),this.fillType5=(0,qm.adjust)(this.secondaryColor,{h:-64}),this.fillType6=(0,qm.adjust)(this.primaryColor,{h:128}),this.fillType7=(0,qm.adjust)(this.secondaryColor,{h:128}),this.pie1=this.pie1||this.primaryColor,this.pie2=this.pie2||this.secondaryColor,this.pie3=this.pie3||this.tertiaryColor,this.pie4=this.pie4||(0,qm.adjust)(this.primaryColor,{l:-30}),this.pie5=this.pie5||(0,qm.adjust)(this.secondaryColor,{l:-30}),this.pie6=this.pie6||(0,qm.adjust)(this.tertiaryColor,{h:40,l:-40}),this.pie7=this.pie7||(0,qm.adjust)(this.primaryColor,{h:60,l:-10}),this.pie8=this.pie8||(0,qm.adjust)(this.primaryColor,{h:-60,l:-10}),this.pie9=this.pie9||(0,qm.adjust)(this.primaryColor,{h:120,l:0}),this.pie10=this.pie10||(0,qm.adjust)(this.primaryColor,{h:60,l:-50}),this.pie11=this.pie11||(0,qm.adjust)(this.primaryColor,{h:-60,l:-50}),this.pie12=this.pie12||(0,qm.adjust)(this.primaryColor,{h:120,l:-50}),this.pieTitleTextSize=this.pieTitleTextSize||"25px",this.pieTitleTextColor=this.pieTitleTextColor||this.taskTextDarkColor,this.pieSectionTextSize=this.pieSectionTextSize||"17px",this.pieSectionTextColor=this.pieSectionTextColor||this.textColor,this.pieLegendTextSize=this.pieLegendTextSize||"17px",this.pieLegendTextColor=this.pieLegendTextColor||this.taskTextDarkColor,this.pieStrokeColor=this.pieStrokeColor||"black",this.pieStrokeWidth=this.pieStrokeWidth||"2px",this.pieOpacity=this.pieOpacity||"0.7",this.requirementBackground=this.requirementBackground||this.primaryColor,this.requirementBorderColor=this.requirementBorderColor||this.primaryBorderColor,this.requirementBorderSize=this.requirementBorderSize||this.primaryBorderColor,this.requirementTextColor=this.requirementTextColor||this.primaryTextColor,this.relationColor=this.relationColor||this.lineColor,this.relationLabelBackground=this.relationLabelBackground||this.edgeLabelBackground,this.relationLabelColor=this.relationLabelColor||this.actorTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===tv(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}],n&&ev(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();function rv(t){return rv="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},rv(t)}function iv(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var av=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.primaryColor="#eee",this.contrast="#707070",this.secondaryColor=(0,qm.lighten)(this.contrast,55),this.background="#ffffff",this.tertiaryColor=(0,qm.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=Hm(this.primaryColor,this.darkMode),this.secondaryBorderColor=Hm(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Hm(this.tertiaryColor,this.darkMode),this.primaryTextColor=(0,qm.invert)(this.primaryColor),this.secondaryTextColor=(0,qm.invert)(this.secondaryColor),this.tertiaryTextColor=(0,qm.invert)(this.tertiaryColor),this.lineColor=(0,qm.invert)(this.background),this.textColor=(0,qm.invert)(this.background),this.mainBkg="#eee",this.secondBkg="calculated",this.lineColor="#666",this.border1="#999",this.border2="calculated",this.note="#ffa",this.text="#333",this.critical="#d42",this.done="#bbb",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="white",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="calculated",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="white",this.sectionBkgColor2="calculated",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBkgColor="calculated",this.critBorderColor="calculated",this.todayLineColor="calculated",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222"}var e,n;return e=t,n=[{key:"updateColors",value:function(){this.secondBkg=(0,qm.lighten)(this.contrast,55),this.border2=this.contrast,this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.titleColor=this.text,this.actorBorder=(0,qm.lighten)(this.border1,23),this.actorBkg=this.mainBkg,this.actorTextColor=this.text,this.actorLineColor=this.lineColor,this.signalColor=this.text,this.signalTextColor=this.text,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.text,this.loopTextColor=this.text,this.noteBorderColor="#999",this.noteBkgColor="#666",this.noteTextColor="#fff",this.sectionBkgColor=(0,qm.lighten)(this.contrast,30),this.sectionBkgColor2=(0,qm.lighten)(this.contrast,30),this.taskBorderColor=(0,qm.darken)(this.contrast,10),this.taskBkgColor=this.contrast,this.taskTextColor=this.taskTextLightColor,this.taskTextDarkColor=this.text,this.taskTextOutsideColor=this.taskTextDarkColor,this.activeTaskBorderColor=this.taskBorderColor,this.activeTaskBkgColor=this.mainBkg,this.gridColor=(0,qm.lighten)(this.border1,30),this.doneTaskBkgColor=this.done,this.doneTaskBorderColor=this.lineColor,this.critBkgColor=this.critical,this.critBorderColor=(0,qm.darken)(this.critBkgColor,10),this.todayLineColor=this.critBkgColor,this.transitionColor=this.transitionColor||"#000",this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f4f4f4",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.stateBorder=this.stateBorder||"#000",this.innerEndBackground=this.primaryBorderColor,this.specialStateColor="#222",this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=(0,qm.adjust)(this.primaryColor,{h:64}),this.fillType3=(0,qm.adjust)(this.secondaryColor,{h:64}),this.fillType4=(0,qm.adjust)(this.primaryColor,{h:-64}),this.fillType5=(0,qm.adjust)(this.secondaryColor,{h:-64}),this.fillType6=(0,qm.adjust)(this.primaryColor,{h:128}),this.fillType7=(0,qm.adjust)(this.secondaryColor,{h:128}),this.pie1=this.pie1||"#F4F4F4",this.pie2=this.pie2||"#555",this.pie3=this.pie3||"#BBB",this.pie4=this.pie4||"#777",this.pie5=this.pie5||"#999",this.pie6=this.pie6||"#DDD",this.pie7=this.pie7||"#FFF",this.pie8=this.pie8||"#DDD",this.pie9=this.pie9||"#BBB",this.pie10=this.pie10||"#999",this.pie11=this.pie11||"#777",this.pie12=this.pie12||"#555",this.pieTitleTextSize=this.pieTitleTextSize||"25px",this.pieTitleTextColor=this.pieTitleTextColor||this.taskTextDarkColor,this.pieSectionTextSize=this.pieSectionTextSize||"17px",this.pieSectionTextColor=this.pieSectionTextColor||this.textColor,this.pieLegendTextSize=this.pieLegendTextSize||"17px",this.pieLegendTextColor=this.pieLegendTextColor||this.taskTextDarkColor,this.pieStrokeColor=this.pieStrokeColor||"black",this.pieStrokeWidth=this.pieStrokeWidth||"2px",this.pieOpacity=this.pieOpacity||"0.7",this.requirementBackground=this.requirementBackground||this.primaryColor,this.requirementBorderColor=this.requirementBorderColor||this.primaryBorderColor,this.requirementBorderSize=this.requirementBorderSize||this.primaryBorderColor,this.requirementTextColor=this.requirementTextColor||this.primaryTextColor,this.relationColor=this.relationColor||this.lineColor,this.relationLabelBackground=this.relationLabelBackground||this.edgeLabelBackground,this.relationLabelColor=this.relationLabelColor||this.actorTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===rv(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}],n&&iv(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();const ov={base:{getThemeVariables:function(t){var e=new Vm;return e.calculate(t),e}},dark:{getThemeVariables:function(t){var e=new Zm;return e.calculate(t),e}},default:{getThemeVariables:function(t){var e=new Jm;return e.calculate(t),e}},forest:{getThemeVariables:function(t){var e=new nv;return e.calculate(t),e}},neutral:{getThemeVariables:function(t){var e=new av;return e.calculate(t),e}}};function sv(t){return function(t){if(Array.isArray(t))return cv(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return cv(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?cv(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function cv(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}function uv(t){return uv="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},uv(t)}var lv={theme:"default",themeVariables:ov.default.getThemeVariables(),themeCSS:void 0,maxTextSize:5e4,darkMode:!1,fontFamily:'"trebuchet ms", verdana, arial, sans-serif;',logLevel:5,securityLevel:"strict",startOnLoad:!0,arrowMarkerAbsolute:!1,secure:["secure","securityLevel","startOnLoad","maxTextSize"],deterministicIds:!1,deterministicIDSeed:void 0,flowchart:{diagramPadding:8,htmlLabels:!0,nodeSpacing:50,rankSpacing:50,curve:"basis",padding:15,useMaxWidth:!0,defaultRenderer:"dagre-d3"},sequence:{activationWidth:10,diagramMarginX:50,diagramMarginY:10,actorMargin:50,width:150,height:65,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",mirrorActors:!0,forceMenus:!1,bottomMarginAdj:1,useMaxWidth:!0,rightAngles:!1,showSequenceNumbers:!1,actorFontSize:14,actorFontFamily:'"Open-Sans", "sans-serif"',actorFontWeight:400,noteFontSize:14,noteFontFamily:'"trebuchet ms", verdana, arial, sans-serif',noteFontWeight:400,noteAlign:"center",messageFontSize:16,messageFontFamily:'"trebuchet ms", verdana, arial, sans-serif',messageFontWeight:400,wrap:!1,wrapPadding:10,labelBoxWidth:50,labelBoxHeight:20,messageFont:function(){return{fontFamily:this.messageFontFamily,fontSize:this.messageFontSize,fontWeight:this.messageFontWeight}},noteFont:function(){return{fontFamily:this.noteFontFamily,fontSize:this.noteFontSize,fontWeight:this.noteFontWeight}},actorFont:function(){return{fontFamily:this.actorFontFamily,fontSize:this.actorFontSize,fontWeight:this.actorFontWeight}}},gantt:{titleTopMargin:25,barHeight:20,barGap:4,topPadding:50,rightPadding:75,leftPadding:75,gridLineStartPadding:35,fontSize:11,sectionFontSize:11,numberSectionStyles:4,axisFormat:"%Y-%m-%d",useMaxWidth:!0,topAxis:!1,useWidth:void 0},journey:{diagramMarginX:50,diagramMarginY:10,leftMargin:150,width:150,height:50,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",bottomMarginAdj:1,useMaxWidth:!0,rightAngles:!1,taskFontSize:14,taskFontFamily:'"Open-Sans", "sans-serif"',taskMargin:50,activationWidth:10,textPlacement:"fo",actorColours:["#8FBC8F","#7CFC00","#00FFFF","#20B2AA","#B0E0E6","#FFFFE0"],sectionFills:["#191970","#8B008B","#4B0082","#2F4F4F","#800000","#8B4513","#00008B"],sectionColours:["#fff"]},class:{arrowMarkerAbsolute:!1,useMaxWidth:!0,defaultRenderer:"dagre-wrapper"},git:{arrowMarkerAbsolute:!1,useWidth:void 0,useMaxWidth:!0},state:{dividerMargin:10,sizeUnit:5,padding:8,textHeight:10,titleShift:-15,noteMargin:10,forkWidth:70,forkHeight:7,miniPadding:2,fontSizeFactor:5.02,fontSize:24,labelHeight:16,edgeLengthFactor:"20",compositTitleSize:35,radius:5,useMaxWidth:!0,defaultRenderer:"dagre-wrapper"},er:{diagramPadding:20,layoutDirection:"TB",minEntityWidth:100,minEntityHeight:75,entityPadding:15,stroke:"gray",fill:"honeydew",fontSize:12,useMaxWidth:!0},pie:{useWidth:void 0,useMaxWidth:!0},requirement:{useWidth:void 0,useMaxWidth:!0,rect_fill:"#f9f9f9",text_color:"#333",rect_border_size:"0.5px",rect_border_color:"#bbb",rect_min_width:200,rect_min_height:200,fontSize:14,rect_padding:10,line_height:20}};lv.class.arrowMarkerAbsolute=lv.arrowMarkerAbsolute,lv.git.arrowMarkerAbsolute=lv.arrowMarkerAbsolute;var hv=function t(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return Object.keys(e).reduce((function(r,i){return Array.isArray(e[i])?r:"object"===uv(e[i])&&null!==e[i]?[].concat(sv(r),[n+i],sv(t(e[i],""))):[].concat(sv(r),[n+i])}),[])}(lv,"");const fv=lv;var dv=void 0;function pv(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}function yv(t,e){var n="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!n){if(Array.isArray(t)||(n=vv(t))||e&&t&&"number"==typeof t.length){n&&(t=n);var r=0,i=function(){};return{s:i,n:function(){return r>=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,o=!0,s=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return o=t.done,t},e:function(t){s=!0,a=t},f:function(){try{o||null==n.return||n.return()}finally{if(s)throw a}}}}function gv(t){return gv="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},gv(t)}function mv(t){return function(t){if(Array.isArray(t))return bv(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||vv(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function vv(t,e){if(t){if("string"==typeof t)return bv(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?bv(t,e):void 0}}function bv(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var _v,xv={curveBasis:Vu,curveBasisClosed:function(t){return new Gu(t)},curveBasisOpen:function(t){return new Xu(t)},curveLinear:Pu,curveLinearClosed:function(t){return new Zu(t)},curveMonotoneX:function(t){return new el(t)},curveMonotoneY:function(t){return new nl(t)},curveNatural:function(t){return new il(t)},curveStep:function(t){return new ol(t,.5)},curveStepAfter:function(t){return new ol(t,1)},curveStepBefore:function(t){return new ol(t,0)}},wv=/[%]{2}[{]\s*(?:(?:(\w+)\s*:|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi,kv=/\s*(?:(?:(\w+)(?=:):|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi,Tv=/\s*%%.*\n/gm,Ev=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;try{var n=new RegExp("[%]{2}(?![{]".concat(kv.source,")(?=[}][%]{2}).*\n"),"ig");t=t.trim().replace(n,"").replace(/'/gm,'"'),o.debug("Detecting diagram directive".concat(null!==e?" type:"+e:""," based on the text:").concat(t));for(var r,i=[];null!==(r=wv.exec(t));)if(r.index===wv.lastIndex&&wv.lastIndex++,r&&!e||e&&r[1]&&r[1].match(e)||e&&r[2]&&r[2].match(e)){var a=r[1]?r[1]:r[2],s=r[3]?r[3].trim():r[4]?JSON.parse(r[4].trim()):null;i.push({type:a,args:s})}return 0===i.length&&i.push({type:t,args:null}),1===i.length?i[0]:i}catch(n){return o.error("ERROR: ".concat(n.message," - Unable to parse directive\n ").concat(null!==e?" type:"+e:""," based on the text:").concat(t)),{type:null,args:null}}},Cv=function(t,e){return(t=t.replace(wv,"").replace(Tv,"\n")).match(/^\s*sequenceDiagram/)?"sequence":t.match(/^\s*gantt/)?"gantt":t.match(/^\s*classDiagram-v2/)?"classDiagram":t.match(/^\s*classDiagram/)?e&&e.class&&"dagre-wrapper"===e.class.defaultRenderer?"classDiagram":"class":t.match(/^\s*stateDiagram-v2/)?"stateDiagram":t.match(/^\s*stateDiagram/)?e&&e.class&&"dagre-wrapper"===e.state.defaultRenderer?"stateDiagram":"state":t.match(/^\s*gitGraph/)?"git":t.match(/^\s*flowchart/)?"flowchart-v2":t.match(/^\s*info/)?"info":t.match(/^\s*pie/)?"pie":t.match(/^\s*erDiagram/)?"er":t.match(/^\s*journey/)?"journey":t.match(/^\s*requirement/)||t.match(/^\s*requirementDiagram/)?"requirement":e&&e.flowchart&&"dagre-wrapper"===e.flowchart.defaultRenderer?"flowchart-v2":"flowchart"},Sv=function(t,e){var n={};return function(){for(var r=arguments.length,i=new Array(r),a=0;a<r;a++)i[a]=arguments[a];var o=e?e.apply(dv,i):i[0];if(o in n)return n[o];var s=t.apply(void 0,i);return n[o]=s,s}},Av=function(t,e){if(!t)return e;var n="curve".concat(t.charAt(0).toUpperCase()+t.slice(1));return xv[n]||e},Mv=function(t,e){return t&&e?Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2)):0},Nv=function(t){for(var e="",n="",r=0;r<t.length;r++)void 0!==t[r]&&(t[r].startsWith("color:")||t[r].startsWith("text-align:")?n=n+t[r]+";":e=e+t[r]+";");return{style:e,labelStyle:n}},Dv=0,Ov=function(){return Dv++,"id-"+Math.random().toString(36).substr(2,12)+"-"+Dv},Bv=function(t){return function(t){for(var e="",n="0123456789abcdef",r=n.length,i=0;i<t;i++)e+=n.charAt(Math.floor(Math.random()*r));return e}(t.length)},Lv=function t(e,n,r){var i=Object.assign({depth:2,clobber:!1},r),a=i.depth,o=i.clobber;return Array.isArray(n)&&!Array.isArray(e)?(n.forEach((function(n){return t(e,n,r)})),e):Array.isArray(n)&&Array.isArray(e)?(n.forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),e):void 0===e||a<=0?null!=e&&"object"===gv(e)&&"object"===gv(n)?Object.assign(e,n):n:(void 0!==n&&"object"===gv(e)&&"object"===gv(n)&&Object.keys(n).forEach((function(r){"object"!==gv(n[r])||void 0!==e[r]&&"object"!==gv(e[r])?(o||"object"!==gv(e[r])&&"object"!==gv(n[r]))&&(e[r]=n[r]):(void 0===e[r]&&(e[r]=Array.isArray(n[r])?[]:{}),e[r]=t(e[r],n[r],{depth:a-1,clobber:o}))})),e)},Iv=function(t,e){var n=e.text.replace(Um.lineBreakRegex," "),r=t.append("text");r.attr("x",e.x),r.attr("y",e.y),r.style("text-anchor",e.anchor),r.style("font-family",e.fontFamily),r.style("font-size",e.fontSize),r.style("font-weight",e.fontWeight),r.attr("fill",e.fill),void 0!==e.class&&r.attr("class",e.class);var i=r.append("tspan");return i.attr("x",e.x+2*e.textMargin),i.attr("fill",e.fill),i.text(n),r},Rv=Sv((function(t,e,n){if(!t)return t;if(n=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",joinWith:"<br/>"},n),Um.lineBreakRegex.test(t))return t;var r=t.split(" "),i=[],a="";return r.forEach((function(t,o){var s=Pv("".concat(t," "),n),c=Pv(a,n);if(s>e){var u=Fv(t,e,"-",n),l=u.hyphenatedStrings,h=u.remainingWord;i.push.apply(i,[a].concat(mv(l))),a=h}else c+s>=e?(i.push(a),a=t):a=[a,t].filter(Boolean).join(" ");o+1===r.length&&i.push(a)})),i.filter((function(t){return""!==t})).join(n.joinWith)}),(function(t,e,n){return"".concat(t,"-").concat(e,"-").concat(n.fontSize,"-").concat(n.fontWeight,"-").concat(n.fontFamily,"-").concat(n.joinWith)})),Fv=Sv((function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"-",r=arguments.length>3?arguments[3]:void 0;r=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:0},r);var i=t.split(""),a=[],o="";return i.forEach((function(t,s){var c="".concat(o).concat(t);if(Pv(c,r)>=e){var u=s+1,l=i.length===u,h="".concat(c).concat(n);a.push(l?c:h),o=""}else o=c})),{hyphenatedStrings:a,remainingWord:o}}),(function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"-",r=arguments.length>3?arguments[3]:void 0;return"".concat(t,"-").concat(e,"-").concat(n,"-").concat(r.fontSize,"-").concat(r.fontWeight,"-").concat(r.fontFamily)})),Pv=function(t,e){return e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial"},e),jv(t,e).width},jv=Sv((function(t,e){var n=e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial"},e),r=n.fontSize,i=n.fontFamily,a=n.fontWeight;if(!t)return{width:0,height:0};var o=["sans-serif",i],s=t.split(Um.lineBreakRegex),c=[],u=au("body");if(!u.remove)return{width:0,height:0,lineHeight:0};for(var l=u.append("svg"),h=0,f=o;h<f.length;h++){var d,p=f[h],y=0,g={width:0,height:0,lineHeight:0},m=yv(s);try{for(m.s();!(d=m.n()).done;){var v=d.value,b={x:0,y:0,fill:void 0,anchor:"start",style:"#666",width:100,height:100,textMargin:0,rx:0,ry:0,valign:void 0};b.text=v;var _=Iv(l,b).style("font-size",r).style("font-weight",a).style("font-family",p),x=(_._groups||_)[0][0].getBBox();g.width=Math.round(Math.max(g.width,x.width)),y=Math.round(x.height),g.height+=y,g.lineHeight=Math.round(Math.max(g.lineHeight,y))}}catch(t){m.e(t)}finally{m.f()}c.push(g)}return l.remove(),c[isNaN(c[1].height)||isNaN(c[1].width)||isNaN(c[1].lineHeight)||c[0].height>c[1].height&&c[0].width>c[1].width&&c[0].lineHeight>c[1].lineHeight?0:1]}),(function(t,e){return"".concat(t,"-").concat(e.fontSize,"-").concat(e.fontWeight,"-").concat(e.fontFamily)})),Yv=function(t,e,n){var r=new Map;return r.set("height",t),n?(r.set("width","100%"),r.set("style","max-width: ".concat(e,"px;"))):r.set("width",e),r},zv=function(t,e,n,r){!function(t,e){var n,r=yv(e);try{for(r.s();!(n=r.n()).done;){var i=n.value;t.attr(i[0],i[1])}}catch(t){r.e(t)}finally{r.f()}}(t,Yv(e,n,r))},Uv=function t(e){o.debug("directiveSanitizer called with",e),"object"===gv(e)&&(e.length?e.forEach((function(e){return t(e)})):Object.keys(e).forEach((function(n){o.debug("Checking key",n),0===n.indexOf("__")&&(o.debug("sanitize deleting __ option",n),delete e[n]),n.indexOf("proto")>=0&&(o.debug("sanitize deleting proto option",n),delete e[n]),n.indexOf("constr")>=0&&(o.debug("sanitize deleting constr option",n),delete e[n]),n.indexOf("themeCSS")>=0&&(o.debug("sanitizing themeCss option"),e[n]=qv(e[n])),hv.indexOf(n)<0?(o.debug("sanitize deleting option",n),delete e[n]):"object"===gv(e[n])&&(o.debug("sanitize deleting object",n),t(e[n]))})))},qv=function(t){return(t.match(/\{/g)||[]).length!==(t.match(/\}/g)||[]).length?"{ /* ERROR: Unbalanced CSS */ }":t};const Hv={assignWithDepth:Lv,wrapLabel:Rv,calculateTextHeight:function(t,e){return e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:15},e),jv(t,e).height},calculateTextWidth:Pv,calculateTextDimensions:jv,calculateSvgSizeAttrs:Yv,configureSvgSize:zv,detectInit:function(t,e){var n=Ev(t,/(?:init\b)|(?:initialize\b)/),r={};if(Array.isArray(n)){var i=n.map((function(t){return t.args}));Uv(i),r=Lv(r,mv(i))}else r=n.args;if(r){var a=Cv(t,e);["config"].forEach((function(t){void 0!==r[t]&&("flowchart-v2"===a&&(a="flowchart"),r[a]=r[t],delete r[t])}))}return r},detectDirective:Ev,detectType:Cv,isSubstringInArray:function(t,e){for(var n=0;n<e.length;n++)if(e[n].match(t))return n;return-1},interpolateToCurve:Av,calcLabelPosition:function(t){return function(t){var e,n=0;t.forEach((function(t){n+=Mv(t,e),e=t}));var r=n/2,i=void 0;return e=void 0,t.forEach((function(t){if(e&&!i){var n=Mv(t,e);if(n<r)r-=n;else{var a=r/n;a<=0&&(i=e),a>=1&&(i={x:t.x,y:t.y}),a>0&&a<1&&(i={x:(1-a)*e.x+a*t.x,y:(1-a)*e.y+a*t.y})}}e=t})),i}(t)},calcCardinalityPosition:function(t,e,n){var r;o.info("our points",e),e[0]!==n&&(e=e.reverse()),e.forEach((function(t){Mv(t,r),r=t}));var i,a=25;r=void 0,e.forEach((function(t){if(r&&!i){var e=Mv(t,r);if(e<a)a-=e;else{var n=a/e;n<=0&&(i=r),n>=1&&(i={x:t.x,y:t.y}),n>0&&n<1&&(i={x:(1-n)*r.x+n*t.x,y:(1-n)*r.y+n*t.y})}}r=t}));var s=t?10:5,c=Math.atan2(e[0].y-i.y,e[0].x-i.x),u={x:0,y:0};return u.x=Math.sin(c)*s+(e[0].x+i.x)/2,u.y=-Math.cos(c)*s+(e[0].y+i.y)/2,u},calcTerminalLabelPosition:function(t,e,n){var r,i=JSON.parse(JSON.stringify(n));o.info("our points",i),"start_left"!==e&&"start_right"!==e&&(i=i.reverse()),i.forEach((function(t){Mv(t,r),r=t}));var a,s=25+t;r=void 0,i.forEach((function(t){if(r&&!a){var e=Mv(t,r);if(e<s)s-=e;else{var n=s/e;n<=0&&(a=r),n>=1&&(a={x:t.x,y:t.y}),n>0&&n<1&&(a={x:(1-n)*r.x+n*t.x,y:(1-n)*r.y+n*t.y})}}r=t}));var c=10+.5*t,u=Math.atan2(i[0].y-a.y,i[0].x-a.x),l={x:0,y:0};return l.x=Math.sin(u)*c+(i[0].x+a.x)/2,l.y=-Math.cos(u)*c+(i[0].y+a.y)/2,"start_left"===e&&(l.x=Math.sin(u+Math.PI)*c+(i[0].x+a.x)/2,l.y=-Math.cos(u+Math.PI)*c+(i[0].y+a.y)/2),"end_right"===e&&(l.x=Math.sin(u-Math.PI)*c+(i[0].x+a.x)/2-5,l.y=-Math.cos(u-Math.PI)*c+(i[0].y+a.y)/2-5),"end_left"===e&&(l.x=Math.sin(u)*c+(i[0].x+a.x)/2-5,l.y=-Math.cos(u)*c+(i[0].y+a.y)/2-5),l},formatUrl:function(t,e){var n=t.trim();if(n)return"loose"!==e.securityLevel?(0,Om.sanitizeUrl)(n):n},getStylesFromArray:Nv,generateId:Ov,random:Bv,memoize:Sv,runFunc:function(t){for(var e,n=t.split("."),r=n.length-1,i=n[r],a=window,o=0;o<r;o++)if(!(a=a[n[o]]))return;for(var s=arguments.length,c=new Array(s>1?s-1:0),u=1;u<s;u++)c[u-1]=arguments[u];(e=a)[i].apply(e,c)},entityDecode:function(t){return _v=_v||document.createElement("div"),t=escape(t).replace(/%26/g,"&").replace(/%23/g,"#").replace(/%3B/g,";"),_v.innerHTML=t,unescape(_v.textContent)},initIdGeneratior:function(){function t(e,n){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.deterministic=e,this.seed=n,this.count=n?n.length:0}var e,n;return e=t,(n=[{key:"next",value:function(){return this.deterministic?this.count++:Date.now()}}])&&pv(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}(),directiveSanitizer:Uv,sanitizeCss:qv};function $v(t){return $v="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},$v(t)}var Wv,Vv=Object.freeze(fv),Gv=Lv({},Vv),Xv=[],Zv=Lv({},Vv),Qv=function(t,e){for(var n=Lv({},t),r={},i=0;i<e.length;i++){var a=e[i];tb(a),r=Lv(r,a)}if(n=Lv(n,r),r.theme){var o=Lv({},Wv),s=Lv(o.themeVariables||{},r.themeVariables);n.themeVariables=ov[n.theme].getThemeVariables(s)}return Zv=n,n},Kv=function(){return Lv({},Gv)},Jv=function(){return Lv({},Zv)},tb=function t(e){Object.keys(Gv.secure).forEach((function(t){void 0!==e[Gv.secure[t]]&&(o.debug("Denied attempt to modify a secure key ".concat(Gv.secure[t]),e[Gv.secure[t]]),delete e[Gv.secure[t]])})),Object.keys(e).forEach((function(t){0===t.indexOf("__")&&delete e[t]})),Object.keys(e).forEach((function(n){"string"==typeof e[n]&&(e[n].indexOf("<")>-1||e[n].indexOf(">")>-1||e[n].indexOf("url(data:")>-1)&&delete e[n],"object"===$v(e[n])&&t(e[n])}))},eb=function(t){t.fontFamily&&(t.themeVariables&&t.themeVariables.fontFamily||(t.themeVariables={fontFamily:t.fontFamily})),Xv.push(t),Qv(Gv,Xv)},nb=function(){Qv(Gv,Xv=[])};function rb(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var ib="classid-",ab=[],ob={},sb=0,cb=[],ub=function(t){return Um.sanitizeText(t,Jv())},lb=function(t){var e="",n=t;if(t.indexOf("~")>0){var r=t.split("~");n=r[0],e=Um.sanitizeText(r[1],Jv())}return{className:n,type:e}},hb=function(t){var e=lb(t);void 0===ob[e.className]&&(ob[e.className]={id:e.className,type:e.type,cssClasses:[],methods:[],members:[],annotations:[],domId:ib+e.className+"-"+sb},sb++)},fb=function(t){for(var e=Object.keys(ob),n=0;n<e.length;n++)if(ob[e[n]].id===t)return ob[e[n]].domId},db=function(t,e){var n=lb(t).className,r=ob[n];if("string"==typeof e){var i=e.trim();i.startsWith("<<")&&i.endsWith(">>")?r.annotations.push(ub(i.substring(2,i.length-2))):i.indexOf(")")>0?r.methods.push(ub(i)):i&&r.members.push(ub(i))}},pb=function(t,e){t.split(",").forEach((function(t){var n=t;t[0].match(/\d/)&&(n=ib+n),void 0!==ob[n]&&ob[n].cssClasses.push(e)}))},yb=function(t,e,n){var r=Jv(),i=t,a=fb(i);if("loose"===r.securityLevel&&void 0!==e&&void 0!==ob[i]){var o=[];if("string"==typeof n){o=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var s=0;s<o.length;s++){var c=o[s].trim();'"'===c.charAt(0)&&'"'===c.charAt(c.length-1)&&(c=c.substr(1,c.length-2)),o[s]=c}}0===o.length&&o.push(a),cb.push((function(){var t=document.querySelector('[id="'.concat(a,'"]'));null!==t&&t.addEventListener("click",(function(){var t;Hv.runFunc.apply(Hv,[e].concat(function(t){if(Array.isArray(t))return rb(t)}(t=o)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return rb(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?rb(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()))}),!1)}))}},gb={AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3},mb=function(t){var e=au(".mermaidTooltip");null===(e._groups||e)[0][0]&&(e=au("body").append("div").attr("class","mermaidTooltip").style("opacity",0)),au(t).select("svg").selectAll("g.node").on("mouseover",(function(){var t=au(this);if(null!==t.attr("title")){var n=this.getBoundingClientRect();e.transition().duration(200).style("opacity",".9"),e.html(t.attr("title")).style("left",window.scrollX+n.left+(n.right-n.left)/2+"px").style("top",window.scrollY+n.top-14+document.body.scrollTop+"px"),t.classed("hover",!0)}})).on("mouseout",(function(){e.transition().duration(500).style("opacity",0),au(this).classed("hover",!1)}))};cb.push(mb);var vb="TB";const bb={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().class},addClass:hb,bindFunctions:function(t){cb.forEach((function(e){e(t)}))},clear:function(){ab=[],ob={},(cb=[]).push(mb)},getClass:function(t){return ob[t]},getClasses:function(){return ob},addAnnotation:function(t,e){var n=lb(t).className;ob[n].annotations.push(e)},getRelations:function(){return ab},addRelation:function(t){o.debug("Adding relation: "+JSON.stringify(t)),hb(t.id1),hb(t.id2),t.id1=lb(t.id1).className,t.id2=lb(t.id2).className,t.relationTitle1=Um.sanitizeText(t.relationTitle1.trim(),Jv()),t.relationTitle2=Um.sanitizeText(t.relationTitle2.trim(),Jv()),ab.push(t)},getDirection:function(){return vb},setDirection:function(t){vb=t},addMember:db,addMembers:function(t,e){Array.isArray(e)&&(e.reverse(),e.forEach((function(e){return db(t,e)})))},cleanupLabel:function(t){return":"===t.substring(0,1)?Um.sanitizeText(t.substr(1).trim(),Jv()):ub(t.trim())},lineType:{LINE:0,DOTTED_LINE:1},relationType:gb,setClickEvent:function(t,e,n){t.split(",").forEach((function(t){yb(t,e,n),ob[t].haveCallback=!0})),pb(t,"clickable")},setCssClass:pb,setLink:function(t,e,n){var r=Jv();t.split(",").forEach((function(t){var i=t;t[0].match(/\d/)&&(i=ib+i),void 0!==ob[i]&&(ob[i].link=Hv.formatUrl(e,r),ob[i].linkTarget="string"==typeof n?n:"_blank")})),pb(t,"clickable")},setTooltip:function(t,e){var n=Jv();t.split(",").forEach((function(t){void 0!==e&&(ob[t].tooltip=Um.sanitizeText(e,n))}))},lookUpDomId:fb};var _b=n(681),xb=n.n(_b),wb=n(8282),kb=n.n(wb),Tb=n(1362),Eb=n.n(Tb),Cb=0,Sb=function(t){var e=t.match(/^(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+) *(\*|\$)?$/),n=t.match(/^([+|\-|~|#])?(\w+) *\( *(.*)\) *(\*|\$)? *(\w*[~|[\]]*\s*\w*~?)$/);return e&&!n?Ab(e):n?Mb(n):Nb(t)},Ab=function(t){var e="",n="";try{var r=t[1]?t[1].trim():"",i=t[2]?t[2].trim():"",a=t[3]?Ob(t[3].trim()):"",o=t[4]?t[4].trim():"",s=t[5]?t[5].trim():"";n=r+i+a+" "+o,e=Bb(s)}catch(e){n=t}return{displayText:n,cssStyle:e}},Mb=function(t){var e="",n="";try{var r=t[1]?t[1].trim():"",i=t[2]?t[2].trim():"",a=t[3]?Ob(t[3].trim()):"",o=t[4]?t[4].trim():"";n=r+i+"("+a+")"+(t[5]?" : "+Ob(t[5]).trim():""),e=Bb(o)}catch(e){n=t}return{displayText:n,cssStyle:e}},Nb=function(t){var e="",n="",r="",i=t.indexOf("("),a=t.indexOf(")");if(i>1&&a>i&&a<=t.length){var o="",s="",c=t.substring(0,1);c.match(/\w/)?s=t.substring(0,i).trim():(c.match(/\+|-|~|#/)&&(o=c),s=t.substring(1,i).trim());var u=t.substring(i+1,a),l=t.substring(a+1,1);n=Bb(l),e=o+s+"("+Ob(u.trim())+")",a<"".length&&""!==(r=t.substring(a+2).trim())&&(r=" : "+Ob(r))}else e=Ob(t);return{displayText:e,cssStyle:n}},Db=function(t,e,n,r){var i=Sb(e),a=t.append("tspan").attr("x",r.padding).text(i.displayText);""!==i.cssStyle&&a.attr("style",i.cssStyle),n||a.attr("dy",r.textHeight)},Ob=function t(e){var n=e;return-1!=e.indexOf("~")?t(n=(n=n.replace("~","<")).replace("~",">")):n},Bb=function(t){switch(t){case"*":return"font-style:italic;";case"$":return"text-decoration:underline;";default:return""}};const Lb=function(t,e,n){o.info("Rendering class "+e);var r,i=e.id,a={id:i,label:e.id,width:0,height:0},s=t.append("g").attr("id",fb(i)).attr("class","classGroup");r=e.link?s.append("svg:a").attr("xlink:href",e.link).attr("target",e.linkTarget).append("text").attr("y",n.textHeight+n.padding).attr("x",0):s.append("text").attr("y",n.textHeight+n.padding).attr("x",0);var c=!0;e.annotations.forEach((function(t){var e=r.append("tspan").text("«"+t+"»");c||e.attr("dy",n.textHeight),c=!1}));var u=e.id;void 0!==e.type&&""!==e.type&&(u+="<"+e.type+">");var l=r.append("tspan").text(u).attr("class","title");c||l.attr("dy",n.textHeight);var h=r.node().getBBox().height,f=s.append("line").attr("x1",0).attr("y1",n.padding+h+n.dividerMargin/2).attr("y2",n.padding+h+n.dividerMargin/2),d=s.append("text").attr("x",n.padding).attr("y",h+n.dividerMargin+n.textHeight).attr("fill","white").attr("class","classText");c=!0,e.members.forEach((function(t){Db(d,t,c,n),c=!1}));var p=d.node().getBBox(),y=s.append("line").attr("x1",0).attr("y1",n.padding+h+n.dividerMargin+p.height).attr("y2",n.padding+h+n.dividerMargin+p.height),g=s.append("text").attr("x",n.padding).attr("y",h+2*n.dividerMargin+p.height+n.textHeight).attr("fill","white").attr("class","classText");c=!0,e.methods.forEach((function(t){Db(g,t,c,n),c=!1}));var m=s.node().getBBox(),v=" ";e.cssClasses.length>0&&(v+=e.cssClasses.join(" "));var b=s.insert("rect",":first-child").attr("x",0).attr("y",0).attr("width",m.width+2*n.padding).attr("height",m.height+n.padding+.5*n.dividerMargin).attr("class",v).node().getBBox().width;return r.node().childNodes.forEach((function(t){t.setAttribute("x",(b-t.getBBox().width)/2)})),e.tooltip&&r.insert("title").text(e.tooltip),f.attr("x2",b),y.attr("x2",b),a.width=b,a.height=m.height+n.padding+.5*n.dividerMargin,a};Tb.parser.yy=bb;var Ib={},Rb={dividerMargin:10,padding:5,textHeight:10},Fb=function(t){var e=Object.entries(Ib).find((function(e){return e[1].label===t}));if(e)return e[0]};const Pb=function(t){Object.keys(t).forEach((function(e){Rb[e]=t[e]}))},jb=function(t,e){Ib={},Tb.parser.yy.clear(),Tb.parser.parse(t),o.info("Rendering diagram "+t);var n,r=au("[id='".concat(e,"']"));r.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),(n=r).append("defs").append("marker").attr("id","extensionStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),n.append("defs").append("marker").attr("id","extensionEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z"),n.append("defs").append("marker").attr("id","compositionStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","compositionEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","aggregationStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","aggregationEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","dependencyStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z");var i=new(kb().Graph)({multigraph:!0});i.setGraph({isMultiGraph:!0}),i.setDefaultEdgeLabel((function(){return{}}));for(var a=bb.getClasses(),s=Object.keys(a),c=0;c<s.length;c++){var u=a[s[c]],l=Lb(r,u,Rb);Ib[l.id]=l,i.setNode(l.id,l),o.info("Org height: "+l.height)}bb.getRelations().forEach((function(t){o.info("tjoho"+Fb(t.id1)+Fb(t.id2)+JSON.stringify(t)),i.setEdge(Fb(t.id1),Fb(t.id2),{relation:t},t.title||"DEFAULT")})),xb().layout(i),i.nodes().forEach((function(t){void 0!==t&&void 0!==i.node(t)&&(o.debug("Node "+t+": "+JSON.stringify(i.node(t))),au("#"+fb(t)).attr("transform","translate("+(i.node(t).x-i.node(t).width/2)+","+(i.node(t).y-i.node(t).height/2)+" )"))})),i.edges().forEach((function(t){void 0!==t&&void 0!==i.edge(t)&&(o.debug("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(i.edge(t))),function(t,e,n,r){var i=function(t){switch(t){case gb.AGGREGATION:return"aggregation";case gb.EXTENSION:return"extension";case gb.COMPOSITION:return"composition";case gb.DEPENDENCY:return"dependency"}};e.points=e.points.filter((function(t){return!Number.isNaN(t.y)}));var a,s,c=e.points,u=zu().x((function(t){return t.x})).y((function(t){return t.y})).curve(Vu),l=t.append("path").attr("d",u(c)).attr("id","edge"+Cb).attr("class","relation"),h="";r.arrowMarkerAbsolute&&(h=(h=(h=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),1==n.relation.lineType&&l.attr("class","relation dashed-line"),"none"!==n.relation.type1&&l.attr("marker-start","url("+h+"#"+i(n.relation.type1)+"Start)"),"none"!==n.relation.type2&&l.attr("marker-end","url("+h+"#"+i(n.relation.type2)+"End)");var f,d,p,y,g=e.points.length,m=Hv.calcLabelPosition(e.points);if(a=m.x,s=m.y,g%2!=0&&g>1){var v=Hv.calcCardinalityPosition("none"!==n.relation.type1,e.points,e.points[0]),b=Hv.calcCardinalityPosition("none"!==n.relation.type2,e.points,e.points[g-1]);o.debug("cardinality_1_point "+JSON.stringify(v)),o.debug("cardinality_2_point "+JSON.stringify(b)),f=v.x,d=v.y,p=b.x,y=b.y}if(void 0!==n.title){var _=t.append("g").attr("class","classLabel"),x=_.append("text").attr("class","label").attr("x",a).attr("y",s).attr("fill","red").attr("text-anchor","middle").text(n.title);window.label=x;var w=x.node().getBBox();_.insert("rect",":first-child").attr("class","box").attr("x",w.x-r.padding/2).attr("y",w.y-r.padding/2).attr("width",w.width+r.padding).attr("height",w.height+r.padding)}o.info("Rendering relation "+JSON.stringify(n)),void 0!==n.relationTitle1&&"none"!==n.relationTitle1&&t.append("g").attr("class","cardinality").append("text").attr("class","type1").attr("x",f).attr("y",d).attr("fill","black").attr("font-size","6").text(n.relationTitle1),void 0!==n.relationTitle2&&"none"!==n.relationTitle2&&t.append("g").attr("class","cardinality").append("text").attr("class","type2").attr("x",p).attr("y",y).attr("fill","black").attr("font-size","6").text(n.relationTitle2),Cb++}(r,i.edge(t),i.edge(t).relation,Rb))}));var h=r.node().getBBox(),f=h.width+40,d=h.height+40;zv(r,d,f,Rb.useMaxWidth);var p="".concat(h.x-20," ").concat(h.y-20," ").concat(f," ").concat(d);o.debug("viewBox ".concat(p)),r.attr("viewBox",p)};var Yb={extension:function(t,e,n){o.trace("Making markers for ",n),t.append("defs").append("marker").attr("id",e+"-extensionStart").attr("class","marker extension "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),t.append("defs").append("marker").attr("id",e+"-extensionEnd").attr("class","marker extension "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z")},composition:function(t,e){t.append("defs").append("marker").attr("id",e+"-compositionStart").attr("class","marker composition "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-compositionEnd").attr("class","marker composition "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},aggregation:function(t,e){t.append("defs").append("marker").attr("id",e+"-aggregationStart").attr("class","marker aggregation "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-aggregationEnd").attr("class","marker aggregation "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},dependency:function(t,e){t.append("defs").append("marker").attr("id",e+"-dependencyStart").attr("class","marker dependency "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-dependencyEnd").attr("class","marker dependency "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},point:function(t,e){t.append("marker").attr("id",e+"-pointEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-pointStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",0).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 5 L 10 10 L 10 0 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},circle:function(t,e){t.append("marker").attr("id",e+"-circleEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",11).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-circleStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",-1).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},cross:function(t,e){t.append("marker").attr("id",e+"-crossEnd").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",12).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-crossStart").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",-1).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0")},barb:function(t,e){t.append("defs").append("marker").attr("id",e+"-barbEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",14).attr("markerUnits","strokeWidth").attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z")}};const zb=function(t,e,n,r){e.forEach((function(e){Yb[e](t,n,r)}))};function Ub(t){return Ub="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Ub(t)}const qb=function(t,e,n,r){var i,a,s,c,u,l,h=t||"";if("object"===Ub(h)&&(h=h[0]),zm(Jv().flowchart.htmlLabels))return h=h.replace(/\\n|\n/g,"<br />"),o.info("vertexText"+h),i={isNode:r,label:h.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")})),labelStyle:e.replace("fill:","color:")},s=au(document.createElementNS("http://www.w3.org/2000/svg","foreignObject")),c=s.append("xhtml:div"),u=i.label,l=i.isNode?"nodeLabel":"edgeLabel",c.html('<span class="'+l+'" '+(i.labelStyle?'style="'+i.labelStyle+'"':"")+">"+u+"</span>"),(a=i.labelStyle)&&c.attr("style",a),c.style("display","inline-block"),c.style("white-space","nowrap"),c.attr("xmlns","http://www.w3.org/1999/xhtml"),s.node();var f=document.createElementNS("http://www.w3.org/2000/svg","text");f.setAttribute("style",e.replace("color:","fill:"));var d=[];d="string"==typeof h?h.split(/\\n|\n|<br\s*\/?>/gi):Array.isArray(h)?h:[];for(var p=0;p<d.length;p++){var y=document.createElementNS("http://www.w3.org/2000/svg","tspan");y.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),y.setAttribute("dy","1em"),y.setAttribute("x","0"),n?y.setAttribute("class","title-row"):y.setAttribute("class","row"),y.textContent=d[p].trim(),f.appendChild(y)}return f};var Hb=function(t,e,n,r){var i;i=n||"node default";var a=t.insert("g").attr("class",i).attr("id",e.domId||e.id),o=a.insert("g").attr("class","label").attr("style",e.labelStyle),s="string"==typeof e.labelText?e.labelText:e.labelText[0],c=o.node().appendChild(qb(Fm(iC(s),Jv()),e.labelStyle,!1,r)),u=c.getBBox();if(zm(Jv().flowchart.htmlLabels)){var l=c.children[0],h=au(c);u=l.getBoundingClientRect(),h.attr("width",u.width),h.attr("height",u.height)}var f=e.padding/2;return o.attr("transform","translate("+-u.width/2+", "+-u.height/2+")"),{shapeSvg:a,bbox:u,halfPadding:f,label:o}},$b=function(t,e){var n=e.node().getBBox();t.width=n.width,t.height=n.height};function Wb(t,e,n,r){return t.insert("polygon",":first-child").attr("points",r.map((function(t){return t.x+","+t.y})).join(" ")).attr("class","label-container").attr("transform","translate("+-e/2+","+n/2+")")}var Vb={},Gb={},Xb={},Zb=function(t,e){return o.trace("In isDecendant",e," ",t," = ",Gb[e].indexOf(t)>=0),Gb[e].indexOf(t)>=0},Qb=function t(e,n,r,i){o.warn("Copying children of ",e,"root",i,"data",n.node(e),i);var a=n.children(e)||[];e!==i&&a.push(e),o.warn("Copying (nodes) clusterId",e,"nodes",a),a.forEach((function(a){if(n.children(a).length>0)t(a,n,r,i);else{var s=n.node(a);o.info("cp ",a," to ",i," with parent ",e),r.setNode(a,s),i!==n.parent(a)&&(o.warn("Setting parent",a,n.parent(a)),r.setParent(a,n.parent(a))),e!==i&&a!==e?(o.debug("Setting parent",a,e),r.setParent(a,e)):(o.info("In copy ",e,"root",i,"data",n.node(e),i),o.debug("Not Setting parent for node=",a,"cluster!==rootId",e!==i,"node!==clusterId",a!==e));var c=n.edges(a);o.debug("Copying Edges",c),c.forEach((function(t){o.info("Edge",t);var a=n.edge(t.v,t.w,t.name);o.info("Edge data",a,i);try{!function(t,e){return o.info("Decendants of ",e," is ",Gb[e]),o.info("Edge is ",t),t.v!==e&&t.w!==e&&(Gb[e]?(o.info("Here "),Gb[e].indexOf(t.v)>=0||!!Zb(t.v,e)||!!Zb(t.w,e)||Gb[e].indexOf(t.w)>=0):(o.debug("Tilt, ",e,",not in decendants"),!1))}(t,i)?o.info("Skipping copy of edge ",t.v,"--\x3e",t.w," rootId: ",i," clusterId:",e):(o.info("Copying as ",t.v,t.w,a,t.name),r.setEdge(t.v,t.w,a,t.name),o.info("newGraph edges ",r.edges(),r.edge(r.edges()[0])))}catch(t){o.error(t)}}))}o.debug("Removing node",a),n.removeNode(a)}))},Kb=function t(e,n){for(var r=n.children(e),i=[].concat(r),a=0;a<r.length;a++)Xb[r[a]]=e,i=i.concat(t(r[a],n));return i},Jb=function t(e,n){o.trace("Searching",e);var r=n.children(e);if(o.trace("Searching children of id ",e,r),r.length<1)return o.trace("This is a valid node",e),e;for(var i=0;i<r.length;i++){var a=t(r[i],n);if(a)return o.trace("Found replacement for",e," => ",a),a}},t_=function(t){return Vb[t]&&Vb[t].externalConnections&&Vb[t]?Vb[t].id:t},e_=function(t,e){!t||e>10?o.debug("Opting out, no graph "):(o.debug("Opting in, graph "),t.nodes().forEach((function(e){t.children(e).length>0&&(o.warn("Cluster identified",e," Replacement id in edges: ",Jb(e,t)),Gb[e]=Kb(e,t),Vb[e]={id:Jb(e,t),clusterData:t.node(e)})})),t.nodes().forEach((function(e){var n=t.children(e),r=t.edges();n.length>0?(o.debug("Cluster identified",e,Gb),r.forEach((function(t){t.v!==e&&t.w!==e&&Zb(t.v,e)^Zb(t.w,e)&&(o.warn("Edge: ",t," leaves cluster ",e),o.warn("Decendants of XXX ",e,": ",Gb[e]),Vb[e].externalConnections=!0)}))):o.debug("Not a cluster ",e,Gb)})),t.edges().forEach((function(e){var n=t.edge(e);o.warn("Edge "+e.v+" -> "+e.w+": "+JSON.stringify(e)),o.warn("Edge "+e.v+" -> "+e.w+": "+JSON.stringify(t.edge(e)));var r=e.v,i=e.w;o.warn("Fix XXX",Vb,"ids:",e.v,e.w,"Translateing: ",Vb[e.v]," --- ",Vb[e.w]),(Vb[e.v]||Vb[e.w])&&(o.warn("Fixing and trixing - removing XXX",e.v,e.w,e.name),r=t_(e.v),i=t_(e.w),t.removeEdge(e.v,e.w,e.name),r!==e.v&&(n.fromCluster=e.v),i!==e.w&&(n.toCluster=e.w),o.warn("Fix Replacing with XXX",r,i,e.name),t.setEdge(r,i,n,e.name))})),o.warn("Adjusted Graph",kb().json.write(t)),n_(t,0),o.trace(Vb))},n_=function t(e,n){if(o.warn("extractor - ",n,kb().json.write(e),e.children("D")),n>10)o.error("Bailing out");else{for(var r=e.nodes(),i=!1,a=0;a<r.length;a++){var s=r[a],c=e.children(s);i=i||c.length>0}if(i){o.debug("Nodes = ",r,n);for(var u=0;u<r.length;u++){var l=r[u];if(o.debug("Extracting node",l,Vb,Vb[l]&&!Vb[l].externalConnections,!e.parent(l),e.node(l),e.children("D")," Depth ",n),Vb[l])if(!Vb[l].externalConnections&&e.children(l)&&e.children(l).length>0){o.warn("Cluster without external connections, without a parent and with children",l,n);var h="TB"===e.graph().rankdir?"LR":"TB";Vb[l]&&Vb[l].clusterData&&Vb[l].clusterData.dir&&(h=Vb[l].clusterData.dir,o.warn("Fixing dir",Vb[l].clusterData.dir,h));var f=new(kb().Graph)({multigraph:!0,compound:!0}).setGraph({rankdir:h,nodesep:50,ranksep:50,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}}));o.warn("Old graph before copy",kb().json.write(e)),Qb(l,e,f,l),e.setNode(l,{clusterNode:!0,id:l,clusterData:Vb[l].clusterData,labelText:Vb[l].labelText,graph:f}),o.warn("New graph after copy node: (",l,")",kb().json.write(f)),o.debug("Old graph after copy",kb().json.write(e))}else o.warn("Cluster ** ",l," **not meeting the criteria !externalConnections:",!Vb[l].externalConnections," no parent: ",!e.parent(l)," children ",e.children(l)&&e.children(l).length>0,e.children("D"),n),o.debug(Vb);else o.debug("Not a cluster",l,n)}r=e.nodes(),o.warn("New list of nodes",r);for(var d=0;d<r.length;d++){var p=r[d],y=e.node(p);o.warn(" Now next level",p,y),y.clusterNode&&t(y.graph,n+1)}}else o.debug("Done, no node has children",e.nodes())}},r_=function t(e,n){if(0===n.length)return[];var r=Object.assign(n);return n.forEach((function(n){var i=e.children(n),a=t(e,i);r=r.concat(a)})),r},i_=function(t){return r_(t,t.children())},a_=n(3841);const o_=function(t,e,n,r){var i=t.x,a=t.y,o=i-r.x,s=a-r.y,c=Math.sqrt(e*e*s*s+n*n*o*o),u=Math.abs(e*n*o/c);r.x<i&&(u=-u);var l=Math.abs(e*n*s/c);return r.y<a&&(l=-l),{x:i+u,y:a+l}};function s_(t,e){return t*e>0}const c_=function(t,e,n,r){var i,a,o,s,c,u,l,h,f,d,p,y,g;if(i=e.y-t.y,o=t.x-e.x,c=e.x*t.y-t.x*e.y,f=i*n.x+o*n.y+c,d=i*r.x+o*r.y+c,!(0!==f&&0!==d&&s_(f,d)||(a=r.y-n.y,s=n.x-r.x,u=r.x*n.y-n.x*r.y,l=a*t.x+s*t.y+u,h=a*e.x+s*e.y+u,0!==l&&0!==h&&s_(l,h)||0==(p=i*s-a*o))))return y=Math.abs(p/2),{x:(g=o*u-s*c)<0?(g-y)/p:(g+y)/p,y:(g=a*c-i*u)<0?(g-y)/p:(g+y)/p}},u_=function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;return Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=0===s?0:u*o/s,r=u):(o<0&&(c=-c),n=c,r=0===o?0:c*s/o),{x:i+n,y:a+r}},l_=(n.n(a_)(),function(t,e,n){return o_(t,e,e,n)}),h_=function(t,e,n){var r=t.x,i=t.y,a=[],o=Number.POSITIVE_INFINITY,s=Number.POSITIVE_INFINITY;"function"==typeof e.forEach?e.forEach((function(t){o=Math.min(o,t.x),s=Math.min(s,t.y)})):(o=Math.min(o,e.x),s=Math.min(s,e.y));for(var c=r-t.width/2-o,u=i-t.height/2-s,l=0;l<e.length;l++){var h=e[l],f=e[l<e.length-1?l+1:0],d=c_(t,n,{x:c+h.x,y:u+h.y},{x:c+f.x,y:u+f.y});d&&a.push(d)}return a.length?(a.length>1&&a.sort((function(t,e){var r=t.x-n.x,i=t.y-n.y,a=Math.sqrt(r*r+i*i),o=e.x-n.x,s=e.y-n.y,c=Math.sqrt(o*o+s*s);return a<c?-1:a===c?0:1})),a[0]):t},f_=u_;function d_(t){return d_="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},d_(t)}var p_=function(t,e,n){var r=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),i=70,a=10;"LR"===n&&(i=10,a=70);var o=r.append("rect").attr("x",-1*i/2).attr("y",-1*a/2).attr("width",i).attr("height",a).attr("class","fork-join");return $b(e,o),e.height=e.height+e.padding/2,e.width=e.width+e.padding/2,e.intersect=function(t){return f_(e,t)},r},y_={question:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding+(i.height+e.padding),s=[{x:a/2,y:0},{x:a,y:-a/2},{x:a/2,y:-a},{x:0,y:-a/2}];o.info("Question main (Circle)");var c=Wb(r,a,a,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return o.warn("Intersect called"),h_(e,s,t)},r},rect:function(t,e){var n=Hb(t,e,"node "+e.classes,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding;o.trace("Classes = ",e.classes);var s=r.insert("rect",":first-child"),c=i.width+e.padding,u=i.height+e.padding;if(s.attr("class","basic label-container").attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",-i.width/2-a).attr("y",-i.height/2-a).attr("width",c).attr("height",u),e.props){var l=new Set(Object.keys(e.props));e.props.borders&&(function(t,e,n,r){var i=[],a=function(t){i.push(t),i.push(0)},s=function(t){i.push(0),i.push(t)};e.includes("t")?(o.debug("add top border"),a(n)):s(n),e.includes("r")?(o.debug("add right border"),a(r)):s(r),e.includes("b")?(o.debug("add bottom border"),a(n)):s(n),e.includes("l")?(o.debug("add left border"),a(r)):s(r),t.attr("stroke-dasharray",i.join(" "))}(s,e.props.borders,c,u),l.delete("borders")),l.forEach((function(t){o.warn("Unknown node property ".concat(t))}))}return $b(e,s),e.intersect=function(t){return f_(e,t)},r},rectWithTitle:function(t,e){var n;n=e.classes?"node "+e.classes:"node default";var r,i=t.insert("g").attr("class",n).attr("id",e.domId||e.id),a=i.insert("rect",":first-child"),s=i.insert("line"),c=i.insert("g").attr("class","label"),u=e.labelText.flat?e.labelText.flat():e.labelText;r="object"===d_(u)?u[0]:u,o.info("Label text abc79",r,u,"object"===d_(u));var l=c.node().appendChild(qb(r,e.labelStyle,!0,!0)),h={width:0,height:0};if(zm(Jv().flowchart.htmlLabels)){var f=l.children[0],d=au(l);h=f.getBoundingClientRect(),d.attr("width",h.width),d.attr("height",h.height)}o.info("Text 2",u);var p=u.slice(1,u.length),y=l.getBBox(),g=c.node().appendChild(qb(p.join?p.join("<br/>"):p,e.labelStyle,!0,!0));if(zm(Jv().flowchart.htmlLabels)){var m=g.children[0],v=au(g);h=m.getBoundingClientRect(),v.attr("width",h.width),v.attr("height",h.height)}var b=e.padding/2;return au(g).attr("transform","translate( "+(h.width>y.width?0:(y.width-h.width)/2)+", "+(y.height+b+5)+")"),au(l).attr("transform","translate( "+(h.width<y.width?0:-(y.width-h.width)/2)+", 0)"),h=c.node().getBBox(),c.attr("transform","translate("+-h.width/2+", "+(-h.height/2-b+3)+")"),a.attr("class","outer title-state").attr("x",-h.width/2-b).attr("y",-h.height/2-b).attr("width",h.width+e.padding).attr("height",h.height+e.padding),s.attr("class","divider").attr("x1",-h.width/2-b).attr("x2",h.width/2+b).attr("y1",-h.height/2-b+y.height+b).attr("y2",-h.height/2-b+y.height+b),$b(e,a),e.intersect=function(t){return f_(e,t)},i},choice:function(t,e){var n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id);return n.insert("polygon",":first-child").attr("points",[{x:0,y:14},{x:14,y:0},{x:0,y:-14},{x:-14,y:0}].map((function(t){return t.x+","+t.y})).join(" ")).attr("class","state-start").attr("r",7).attr("width",28).attr("height",28),e.width=28,e.height=28,e.intersect=function(t){return l_(e,14,t)},n},circle:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding,s=r.insert("circle",":first-child");return s.attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("r",i.width/2+a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),o.info("Circle main"),$b(e,s),e.intersect=function(t){return o.info("Circle intersect",e,i.width/2+a,t),l_(e,i.width/2+a,t)},r},stadium:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.height+e.padding,o=i.width+a/4+e.padding,s=r.insert("rect",":first-child").attr("style",e.style).attr("rx",a/2).attr("ry",a/2).attr("x",-o/2).attr("y",-a/2).attr("width",o).attr("height",a);return $b(e,s),e.intersect=function(t){return f_(e,t)},r},hexagon:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.height+e.padding,o=a/4,s=i.width+2*o+e.padding,c=[{x:o,y:0},{x:s-o,y:0},{x:s,y:-a/2},{x:s-o,y:-a},{x:o,y:-a},{x:0,y:-a/2}],u=Wb(r,s,a,c);return u.attr("style",e.style),$b(e,u),e.intersect=function(t){return h_(e,c,t)},r},rect_left_inv_arrow:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:-o/2,y:0},{x:a,y:0},{x:a,y:-o},{x:-o/2,y:-o},{x:0,y:-o/2}];return Wb(r,a,o,s).attr("style",e.style),e.width=a+o,e.height=o,e.intersect=function(t){return h_(e,s,t)},r},lean_right:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:-2*o/6,y:0},{x:a-o/6,y:0},{x:a+2*o/6,y:-o},{x:o/6,y:-o}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},lean_left:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:2*o/6,y:0},{x:a+o/6,y:0},{x:a-2*o/6,y:-o},{x:-o/6,y:-o}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},trapezoid:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:-2*o/6,y:0},{x:a+2*o/6,y:0},{x:a-o/6,y:-o},{x:o/6,y:-o}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},inv_trapezoid:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:o/6,y:0},{x:a-o/6,y:0},{x:a+2*o/6,y:-o},{x:-2*o/6,y:-o}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},rect_right_inv_arrow:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:0,y:0},{x:a+o/2,y:0},{x:a,y:-o/2},{x:a+o/2,y:-o},{x:0,y:-o}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},cylinder:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=a/2,s=o/(2.5+a/50),c=i.height+s+e.padding,u="M 0,"+s+" a "+o+","+s+" 0,0,0 "+a+" 0 a "+o+","+s+" 0,0,0 "+-a+" 0 l 0,"+c+" a "+o+","+s+" 0,0,0 "+a+" 0 l 0,"+-c,l=r.attr("label-offset-y",s).insert("path",":first-child").attr("style",e.style).attr("d",u).attr("transform","translate("+-a/2+","+-(c/2+s)+")");return $b(e,l),e.intersect=function(t){var n=f_(e,t),r=n.x-e.x;if(0!=o&&(Math.abs(r)<e.width/2||Math.abs(r)==e.width/2&&Math.abs(n.y-e.y)>e.height/2-s)){var i=s*s*(1-r*r/(o*o));0!=i&&(i=Math.sqrt(i)),i=s-i,t.y-e.y>0&&(i=-i),n.y+=i}return n},r},start:function(t,e){var n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),r=n.insert("circle",":first-child");return r.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),$b(e,r),e.intersect=function(t){return l_(e,7,t)},n},end:function(t,e){var n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),r=n.insert("circle",":first-child"),i=n.insert("circle",":first-child");return i.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),r.attr("class","state-end").attr("r",5).attr("width",10).attr("height",10),$b(e,i),e.intersect=function(t){return l_(e,7,t)},n},note:function(t,e){var n=Hb(t,e,"node "+e.classes,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding;o.info("Classes = ",e.classes);var s=r.insert("rect",":first-child");return s.attr("rx",e.rx).attr("ry",e.ry).attr("x",-i.width/2-a).attr("y",-i.height/2-a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),$b(e,s),e.intersect=function(t){return f_(e,t)},r},subroutine:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:0,y:0},{x:a,y:0},{x:a,y:-o},{x:0,y:-o},{x:0,y:0},{x:-8,y:0},{x:a+8,y:0},{x:a+8,y:-o},{x:-8,y:-o},{x:-8,y:0}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},fork:p_,join:p_,class_box:function(t,e){var n,r=e.padding/2;n=e.classes?"node "+e.classes:"node default";var i=t.insert("g").attr("class",n).attr("id",e.domId||e.id),a=i.insert("rect",":first-child"),o=i.insert("line"),s=i.insert("line"),c=0,u=4,l=i.insert("g").attr("class","label"),h=0,f=e.classData.annotations&&e.classData.annotations[0],d=e.classData.annotations[0]?"«"+e.classData.annotations[0]+"»":"",p=l.node().appendChild(qb(d,e.labelStyle,!0,!0)),y=p.getBBox();if(zm(Jv().flowchart.htmlLabels)){var g=p.children[0],m=au(p);y=g.getBoundingClientRect(),m.attr("width",y.width),m.attr("height",y.height)}e.classData.annotations[0]&&(u+=y.height+4,c+=y.width);var v=e.classData.id;void 0!==e.classData.type&&""!==e.classData.type&&(Jv().flowchart.htmlLabels?v+="<"+e.classData.type+">":v+="<"+e.classData.type+">");var b=l.node().appendChild(qb(v,e.labelStyle,!0,!0));au(b).attr("class","classTitle");var _=b.getBBox();if(zm(Jv().flowchart.htmlLabels)){var x=b.children[0],w=au(b);_=x.getBoundingClientRect(),w.attr("width",_.width),w.attr("height",_.height)}u+=_.height+4,_.width>c&&(c=_.width);var k=[];e.classData.members.forEach((function(t){var n=Sb(t),r=n.displayText;Jv().flowchart.htmlLabels&&(r=r.replace(/</g,"<").replace(/>/g,">"));var i=l.node().appendChild(qb(r,n.cssStyle?n.cssStyle:e.labelStyle,!0,!0)),a=i.getBBox();if(zm(Jv().flowchart.htmlLabels)){var o=i.children[0],s=au(i);a=o.getBoundingClientRect(),s.attr("width",a.width),s.attr("height",a.height)}a.width>c&&(c=a.width),u+=a.height+4,k.push(i)})),u+=8;var T=[];if(e.classData.methods.forEach((function(t){var n=Sb(t),r=n.displayText;Jv().flowchart.htmlLabels&&(r=r.replace(/</g,"<").replace(/>/g,">"));var i=l.node().appendChild(qb(r,n.cssStyle?n.cssStyle:e.labelStyle,!0,!0)),a=i.getBBox();if(zm(Jv().flowchart.htmlLabels)){var o=i.children[0],s=au(i);a=o.getBoundingClientRect(),s.attr("width",a.width),s.attr("height",a.height)}a.width>c&&(c=a.width),u+=a.height+4,T.push(i)})),u+=8,f){var E=(c-y.width)/2;au(p).attr("transform","translate( "+(-1*c/2+E)+", "+-1*u/2+")"),h=y.height+4}var C=(c-_.width)/2;return au(b).attr("transform","translate( "+(-1*c/2+C)+", "+(-1*u/2+h)+")"),h+=_.height+4,o.attr("class","divider").attr("x1",-c/2-r).attr("x2",c/2+r).attr("y1",-u/2-r+8+h).attr("y2",-u/2-r+8+h),h+=8,k.forEach((function(t){au(t).attr("transform","translate( "+-c/2+", "+(-1*u/2+h+4)+")"),h+=_.height+4})),h+=8,s.attr("class","divider").attr("x1",-c/2-r).attr("x2",c/2+r).attr("y1",-u/2-r+8+h).attr("y2",-u/2-r+8+h),h+=8,T.forEach((function(t){au(t).attr("transform","translate( "+-c/2+", "+(-1*u/2+h)+")"),h+=_.height+4})),a.attr("class","outer title-state").attr("x",-c/2-r).attr("y",-u/2-r).attr("width",c+e.padding).attr("height",u+e.padding),$b(e,a),e.intersect=function(t){return f_(e,t)},i}},g_={},m_=function(t){var e=g_[t.id];o.trace("Transforming node",t.diff,t,"translate("+(t.x-t.width/2-5)+", "+t.width/2+")");var n=t.diff||0;return t.clusterNode?e.attr("transform","translate("+(t.x+n-t.width/2)+", "+(t.y-t.height/2-8)+")"):e.attr("transform","translate("+t.x+", "+t.y+")"),n},v_={rect:function(t,e){o.trace("Creating subgraph rect for ",e.id,e);var n=t.insert("g").attr("class","cluster"+(e.class?" "+e.class:"")).attr("id",e.id),r=n.insert("rect",":first-child"),i=n.insert("g").attr("class","cluster-label"),a=i.node().appendChild(qb(e.labelText,e.labelStyle,void 0,!0)),s=a.getBBox();if(zm(Jv().flowchart.htmlLabels)){var c=a.children[0],u=au(a);s=c.getBoundingClientRect(),u.attr("width",s.width),u.attr("height",s.height)}var l=0*e.padding,h=l/2,f=e.width<=s.width+l?s.width+l:e.width;e.width<=s.width+l?e.diff=(s.width-e.width)/2:e.diff=-e.padding/2,o.trace("Data ",e,JSON.stringify(e)),r.attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-f/2).attr("y",e.y-e.height/2-h).attr("width",f).attr("height",e.height+l),i.attr("transform","translate("+(e.x-s.width/2)+", "+(e.y-e.height/2+e.padding/3)+")");var d=r.node().getBBox();return e.width=d.width,e.height=d.height,e.intersect=function(t){return u_(e,t)},n},roundedWithTitle:function(t,e){var n=t.insert("g").attr("class",e.classes).attr("id",e.id),r=n.insert("rect",":first-child"),i=n.insert("g").attr("class","cluster-label"),a=n.append("rect"),o=i.node().appendChild(qb(e.labelText,e.labelStyle,void 0,!0)),s=o.getBBox();if(zm(Jv().flowchart.htmlLabels)){var c=o.children[0],u=au(o);s=c.getBoundingClientRect(),u.attr("width",s.width),u.attr("height",s.height)}s=o.getBBox();var l=0*e.padding,h=l/2,f=e.width<=s.width+e.padding?s.width+e.padding:e.width;e.width<=s.width+e.padding?e.diff=(s.width+0*e.padding-e.width)/2:e.diff=-e.padding/2,r.attr("class","outer").attr("x",e.x-f/2-h).attr("y",e.y-e.height/2-h).attr("width",f+l).attr("height",e.height+l),a.attr("class","inner").attr("x",e.x-f/2-h).attr("y",e.y-e.height/2-h+s.height-1).attr("width",f+l).attr("height",e.height+l-s.height-3),i.attr("transform","translate("+(e.x-s.width/2)+", "+(e.y-e.height/2-e.padding/3+(zm(Jv().flowchart.htmlLabels)?5:3))+")");var d=r.node().getBBox();return e.height=d.height,e.intersect=function(t){return u_(e,t)},n},noteGroup:function(t,e){var n=t.insert("g").attr("class","note-cluster").attr("id",e.id),r=n.insert("rect",":first-child"),i=0*e.padding,a=i/2;r.attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2-a).attr("width",e.width+i).attr("height",e.height+i).attr("fill","none");var o=r.node().getBBox();return e.width=o.width,e.height=o.height,e.intersect=function(t){return u_(e,t)},n},divider:function(t,e){var n=t.insert("g").attr("class",e.classes).attr("id",e.id),r=n.insert("rect",":first-child"),i=0*e.padding,a=i/2;r.attr("class","divider").attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2).attr("width",e.width+i).attr("height",e.height+i);var o=r.node().getBBox();return e.width=o.width,e.height=o.height,e.diff=-e.padding/2,e.intersect=function(t){return u_(e,t)},n}},b_={},__={},x_={},w_=function(t,e){var n=qb(e.label,e.labelStyle),r=t.insert("g").attr("class","edgeLabel"),i=r.insert("g").attr("class","label");i.node().appendChild(n);var a,o=n.getBBox();if(zm(Jv().flowchart.htmlLabels)){var s=n.children[0],c=au(n);o=s.getBoundingClientRect(),c.attr("width",o.width),c.attr("height",o.height)}if(i.attr("transform","translate("+-o.width/2+", "+-o.height/2+")"),__[e.id]=r,e.width=o.width,e.height=o.height,e.startLabelLeft){var u=qb(e.startLabelLeft,e.labelStyle),l=t.insert("g").attr("class","edgeTerminals"),h=l.insert("g").attr("class","inner");a=h.node().appendChild(u);var f=u.getBBox();h.attr("transform","translate("+-f.width/2+", "+-f.height/2+")"),x_[e.id]||(x_[e.id]={}),x_[e.id].startLeft=l,k_(a,e.startLabelLeft)}if(e.startLabelRight){var d=qb(e.startLabelRight,e.labelStyle),p=t.insert("g").attr("class","edgeTerminals"),y=p.insert("g").attr("class","inner");a=p.node().appendChild(d),y.node().appendChild(d);var g=d.getBBox();y.attr("transform","translate("+-g.width/2+", "+-g.height/2+")"),x_[e.id]||(x_[e.id]={}),x_[e.id].startRight=p,k_(a,e.startLabelRight)}if(e.endLabelLeft){var m=qb(e.endLabelLeft,e.labelStyle),v=t.insert("g").attr("class","edgeTerminals"),b=v.insert("g").attr("class","inner");a=b.node().appendChild(m);var _=m.getBBox();b.attr("transform","translate("+-_.width/2+", "+-_.height/2+")"),v.node().appendChild(m),x_[e.id]||(x_[e.id]={}),x_[e.id].endLeft=v,k_(a,e.endLabelLeft)}if(e.endLabelRight){var x=qb(e.endLabelRight,e.labelStyle),w=t.insert("g").attr("class","edgeTerminals"),k=w.insert("g").attr("class","inner");a=k.node().appendChild(x);var T=x.getBBox();k.attr("transform","translate("+-T.width/2+", "+-T.height/2+")"),w.node().appendChild(x),x_[e.id]||(x_[e.id]={}),x_[e.id].endRight=w,k_(a,e.endLabelRight)}};function k_(t,e){Jv().flowchart.htmlLabels&&t&&(t.style.width=9*e.length+"px",t.style.height="12px")}var T_=function(t,e){o.info("Moving label abc78 ",t.id,t.label,__[t.id]);var n=e.updatedPath?e.updatedPath:e.originalPath;if(t.label){var r=__[t.id],i=t.x,a=t.y;if(n){var s=Hv.calcLabelPosition(n);o.info("Moving label from (",i,",",a,") to (",s.x,",",s.y,") abc78")}r.attr("transform","translate("+i+", "+a+")")}if(t.startLabelLeft){var c=x_[t.id].startLeft,u=t.x,l=t.y;if(n){var h=Hv.calcTerminalLabelPosition(t.arrowTypeStart?10:0,"start_left",n);u=h.x,l=h.y}c.attr("transform","translate("+u+", "+l+")")}if(t.startLabelRight){var f=x_[t.id].startRight,d=t.x,p=t.y;if(n){var y=Hv.calcTerminalLabelPosition(t.arrowTypeStart?10:0,"start_right",n);d=y.x,p=y.y}f.attr("transform","translate("+d+", "+p+")")}if(t.endLabelLeft){var g=x_[t.id].endLeft,m=t.x,v=t.y;if(n){var b=Hv.calcTerminalLabelPosition(t.arrowTypeEnd?10:0,"end_left",n);m=b.x,v=b.y}g.attr("transform","translate("+m+", "+v+")")}if(t.endLabelRight){var _=x_[t.id].endRight,x=t.x,w=t.y;if(n){var k=Hv.calcTerminalLabelPosition(t.arrowTypeEnd?10:0,"end_right",n);x=k.x,w=k.y}_.attr("transform","translate("+x+", "+w+")")}},E_=function(t,e){o.warn("abc88 cutPathAtIntersect",t,e);var n=[],r=t[0],i=!1;return t.forEach((function(t){if(o.info("abc88 checking point",t,e),function(t,e){var n=t.x,r=t.y,i=Math.abs(e.x-n),a=Math.abs(e.y-r),o=t.width/2,s=t.height/2;return i>=o||a>=s}(e,t)||i)o.warn("abc88 outside",t,r),r=t,i||n.push(t);else{var a=function(t,e,n){o.warn("intersection calc abc89:\n outsidePoint: ".concat(JSON.stringify(e),"\n insidePoint : ").concat(JSON.stringify(n),"\n node : x:").concat(t.x," y:").concat(t.y," w:").concat(t.width," h:").concat(t.height));var r=t.x,i=t.y,a=Math.abs(r-n.x),s=t.width/2,c=n.x<e.x?s-a:s+a,u=t.height/2,l=Math.abs(e.y-n.y),h=Math.abs(e.x-n.x);if(Math.abs(i-e.y)*s>Math.abs(r-e.x)*u){var f=n.y<e.y?e.y-u-i:i-u-e.y;c=h*f/l;var d={x:n.x<e.x?n.x+c:n.x-h+c,y:n.y<e.y?n.y+l-f:n.y-l+f};return 0===c&&(d.x=e.x,d.y=e.y),0===h&&(d.x=e.x),0===l&&(d.y=e.y),o.warn("abc89 topp/bott calc, Q ".concat(l,", q ").concat(f,", R ").concat(h,", r ").concat(c),d),d}var p=l*(c=n.x<e.x?e.x-s-r:r-s-e.x)/h,y=n.x<e.x?n.x+h-c:n.x-h+c,g=n.y<e.y?n.y+p:n.y-p;return o.warn("sides calc abc89, Q ".concat(l,", q ").concat(p,", R ").concat(h,", r ").concat(c),{_x:y,_y:g}),0===c&&(y=e.x,g=e.y),0===h&&(y=e.x),0===l&&(g=e.y),{x:y,y:g}}(e,r,t);o.warn("abc88 inside",t,r,a),o.warn("abc88 intersection",a);var s=!1;n.forEach((function(t){s=s||t.x===a.x&&t.y===a.y})),n.find((function(t){return t.x===a.x&&t.y===a.y}))?o.warn("abc88 no intersect",a,n):n.push(a),i=!0}})),o.warn("abc88 returning points",n),n},C_=function t(e,n,r,i){o.info("Graph in recursive render: XXX",kb().json.write(n),i);var a=n.graph().rankdir;o.trace("Dir in recursive render - dir:",a);var s=e.insert("g").attr("class","root");n.nodes()?o.info("Recursive render XXX",n.nodes()):o.info("No nodes found for",n),n.edges().length>0&&o.trace("Recursive edges",n.edge(n.edges()[0]));var c=s.insert("g").attr("class","clusters"),u=s.insert("g").attr("class","edgePaths"),l=s.insert("g").attr("class","edgeLabels"),h=s.insert("g").attr("class","nodes");n.nodes().forEach((function(e){var s=n.node(e);if(void 0!==i){var c=JSON.parse(JSON.stringify(i.clusterData));o.info("Setting data for cluster XXX (",e,") ",c,i),n.setNode(i.id,c),n.parent(e)||(o.trace("Setting parent",e,i.id),n.setParent(e,i.id,c))}if(o.info("(Insert) Node XXX"+e+": "+JSON.stringify(n.node(e))),s&&s.clusterNode){o.info("Cluster identified",e,s.width,n.node(e));var u=t(h,s.graph,r,n.node(e)),l=u.elem;$b(s,l),s.diff=u.diff||0,o.info("Node bounds (abc123)",e,s,s.width,s.x,s.y),function(t,e){g_[e.id]=t}(l,s),o.warn("Recursive render complete ",l,s)}else n.children(e).length>0?(o.info("Cluster - the non recursive path XXX",e,s.id,s,n),o.info(Jb(s.id,n)),Vb[s.id]={id:Jb(s.id,n),node:s}):(o.info("Node - the non recursive path",e,s.id,s),function(t,e,n){var r,i;e.link?(r=t.insert("svg:a").attr("xlink:href",e.link).attr("target",e.linkTarget||"_blank"),i=y_[e.shape](r,e,n)):r=i=y_[e.shape](t,e,n),e.tooltip&&i.attr("title",e.tooltip),e.class&&i.attr("class","node default "+e.class),g_[e.id]=r,e.haveCallback&&g_[e.id].attr("class",g_[e.id].attr("class")+" clickable")}(h,n.node(e),a))})),n.edges().forEach((function(t){var e=n.edge(t.v,t.w,t.name);o.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(t)),o.info("Edge "+t.v+" -> "+t.w+": ",t," ",JSON.stringify(n.edge(t))),o.info("Fix",Vb,"ids:",t.v,t.w,"Translateing: ",Vb[t.v],Vb[t.w]),w_(l,e)})),n.edges().forEach((function(t){o.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(t))})),o.info("#############################################"),o.info("### Layout ###"),o.info("#############################################"),o.info(n),xb().layout(n),o.info("Graph after layout:",kb().json.write(n));var f=0;return i_(n).forEach((function(t){var e=n.node(t);o.info("Position "+t+": "+JSON.stringify(n.node(t))),o.info("Position "+t+": ("+e.x,","+e.y,") width: ",e.width," height: ",e.height),e&&e.clusterNode?m_(e):n.children(t).length>0?(function(t,e){o.trace("Inserting cluster");var n=e.shape||"rect";b_[e.id]=v_[n](t,e)}(c,e),Vb[e.id].node=e):m_(e)})),n.edges().forEach((function(t){var e=n.edge(t);o.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(e),e);var i=function(t,e,n,r,i,a){var s=n.points,c=!1,u=a.node(e.v),l=a.node(e.w);o.info("abc88 InsertEdge: ",n),l.intersect&&u.intersect&&((s=s.slice(1,n.points.length-1)).unshift(u.intersect(s[0])),o.info("Last point",s[s.length-1],l,l.intersect(s[s.length-1])),s.push(l.intersect(s[s.length-1]))),n.toCluster&&(o.info("to cluster abc88",r[n.toCluster]),s=E_(n.points,r[n.toCluster].node),c=!0),n.fromCluster&&(o.info("from cluster abc88",r[n.fromCluster]),s=E_(s.reverse(),r[n.fromCluster].node).reverse(),c=!0);var h,f=s.filter((function(t){return!Number.isNaN(t.y)}));h=("graph"===i||"flowchart"===i)&&n.curve||Vu;var d,p=zu().x((function(t){return t.x})).y((function(t){return t.y})).curve(h);switch(n.thickness){case"normal":d="edge-thickness-normal";break;case"thick":d="edge-thickness-thick";break;default:d=""}switch(n.pattern){case"solid":d+=" edge-pattern-solid";break;case"dotted":d+=" edge-pattern-dotted";break;case"dashed":d+=" edge-pattern-dashed"}var y=t.append("path").attr("d",p(f)).attr("id",n.id).attr("class"," "+d+(n.classes?" "+n.classes:"")).attr("style",n.style),g="";switch(Jv().state.arrowMarkerAbsolute&&(g=(g=(g=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),o.info("arrowTypeStart",n.arrowTypeStart),o.info("arrowTypeEnd",n.arrowTypeEnd),n.arrowTypeStart){case"arrow_cross":y.attr("marker-start","url("+g+"#"+i+"-crossStart)");break;case"arrow_point":y.attr("marker-start","url("+g+"#"+i+"-pointStart)");break;case"arrow_barb":y.attr("marker-start","url("+g+"#"+i+"-barbStart)");break;case"arrow_circle":y.attr("marker-start","url("+g+"#"+i+"-circleStart)");break;case"aggregation":y.attr("marker-start","url("+g+"#"+i+"-aggregationStart)");break;case"extension":y.attr("marker-start","url("+g+"#"+i+"-extensionStart)");break;case"composition":y.attr("marker-start","url("+g+"#"+i+"-compositionStart)");break;case"dependency":y.attr("marker-start","url("+g+"#"+i+"-dependencyStart)")}switch(n.arrowTypeEnd){case"arrow_cross":y.attr("marker-end","url("+g+"#"+i+"-crossEnd)");break;case"arrow_point":y.attr("marker-end","url("+g+"#"+i+"-pointEnd)");break;case"arrow_barb":y.attr("marker-end","url("+g+"#"+i+"-barbEnd)");break;case"arrow_circle":y.attr("marker-end","url("+g+"#"+i+"-circleEnd)");break;case"aggregation":y.attr("marker-end","url("+g+"#"+i+"-aggregationEnd)");break;case"extension":y.attr("marker-end","url("+g+"#"+i+"-extensionEnd)");break;case"composition":y.attr("marker-end","url("+g+"#"+i+"-compositionEnd)");break;case"dependency":y.attr("marker-end","url("+g+"#"+i+"-dependencyEnd)")}var m={};return c&&(m.updatedPath=s),m.originalPath=n.points,m}(u,t,e,Vb,r,n);T_(e,i)})),n.nodes().forEach((function(t){var e=n.node(t);o.info(t,e.type,e.diff),"group"===e.type&&(f=e.diff)})),{elem:s,diff:f}},S_=function(t,e,n,r,i){zb(t,n,r,i),g_={},__={},x_={},b_={},Gb={},Xb={},Vb={},o.warn("Graph at first:",kb().json.write(e)),e_(e),o.warn("Graph after:",kb().json.write(e)),C_(t,e,r)};Tb.parser.yy=bb;var A_={dividerMargin:10,padding:5,textHeight:10};function M_(t){var e;switch(t){case 0:e="aggregation";break;case 1:e="extension";break;case 2:e="composition";break;case 3:e="dependency";break;default:e="none"}return e}var N_={},D_=[],O_="",B_=function(t){return void 0===N_[t]&&(N_[t]={attributes:[]},o.info("Added new entity :",t)),N_[t]};const L_={Cardinality:{ZERO_OR_ONE:"ZERO_OR_ONE",ZERO_OR_MORE:"ZERO_OR_MORE",ONE_OR_MORE:"ONE_OR_MORE",ONLY_ONE:"ONLY_ONE"},Identification:{NON_IDENTIFYING:"NON_IDENTIFYING",IDENTIFYING:"IDENTIFYING"},parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().er},addEntity:B_,addAttributes:function(t,e){var n,r=B_(t);for(n=e.length-1;n>=0;n--)r.attributes.push(e[n]),o.debug("Added attribute ",e[n].attributeName)},getEntities:function(){return N_},addRelationship:function(t,e,n,r){var i={entityA:t,roleA:e,entityB:n,relSpec:r};D_.push(i),o.debug("Added new relationship :",i)},getRelationships:function(){return D_},clear:function(){N_={},D_=[],O_=""},setTitle:function(t){O_=t},getTitle:function(){return O_}};var I_=n(5890),R_=n.n(I_),F_={ONLY_ONE_START:"ONLY_ONE_START",ONLY_ONE_END:"ONLY_ONE_END",ZERO_OR_ONE_START:"ZERO_OR_ONE_START",ZERO_OR_ONE_END:"ZERO_OR_ONE_END",ONE_OR_MORE_START:"ONE_OR_MORE_START",ONE_OR_MORE_END:"ONE_OR_MORE_END",ZERO_OR_MORE_START:"ZERO_OR_MORE_START",ZERO_OR_MORE_END:"ZERO_OR_MORE_END"};const P_=F_;var j_={},Y_=function(t){return(t.entityA+t.roleA+t.entityB).replace(/\s/g,"")},z_=0;const U_=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)j_[e[n]]=t[e[n]]},q_=function(t,e){o.info("Drawing ER diagram"),L_.clear();var n=R_().parser;n.yy=L_;try{n.parse(t)}catch(t){o.debug("Parsing failed")}var r,i=au("[id='".concat(e,"']"));(function(t,e){var n;t.append("defs").append("marker").attr("id",F_.ONLY_ONE_START).attr("refX",0).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M9,0 L9,18 M15,0 L15,18"),t.append("defs").append("marker").attr("id",F_.ONLY_ONE_END).attr("refX",18).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M3,0 L3,18 M9,0 L9,18"),(n=t.append("defs").append("marker").attr("id",F_.ZERO_OR_ONE_START).attr("refX",0).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",21).attr("cy",9).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M9,0 L9,18"),(n=t.append("defs").append("marker").attr("id",F_.ZERO_OR_ONE_END).attr("refX",30).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",9).attr("cy",9).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M21,0 L21,18"),t.append("defs").append("marker").attr("id",F_.ONE_OR_MORE_START).attr("refX",18).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M0,18 Q 18,0 36,18 Q 18,36 0,18 M42,9 L42,27"),t.append("defs").append("marker").attr("id",F_.ONE_OR_MORE_END).attr("refX",27).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M3,9 L3,27 M9,18 Q27,0 45,18 Q27,36 9,18"),(n=t.append("defs").append("marker").attr("id",F_.ZERO_OR_MORE_START).attr("refX",18).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",48).attr("cy",18).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M0,18 Q18,0 36,18 Q18,36 0,18"),(n=t.append("defs").append("marker").attr("id",F_.ZERO_OR_MORE_END).attr("refX",39).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",9).attr("cy",18).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M21,18 Q39,0 57,18 Q39,36 21,18")})(i,j_),r=new(kb().Graph)({multigraph:!0,directed:!0,compound:!1}).setGraph({rankdir:j_.layoutDirection,marginx:20,marginy:20,nodesep:100,edgesep:100,ranksep:100}).setDefaultEdgeLabel((function(){return{}}));var a=function(t,e,n){var r;return Object.keys(e).forEach((function(i){var a=t.append("g").attr("id",i);r=void 0===r?i:r;var o="entity-"+i,s=a.append("text").attr("class","er entityLabel").attr("id",o).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","middle").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+j_.fontSize+"px").text(i),c=function(t,e,n){var r=j_.entityPadding/3,i=j_.entityPadding/3,a=.85*j_.fontSize,o=e.node().getBBox(),s=[],c=!1,u=!1,l=0,h=0,f=0,d=0,p=o.height+2*r,y=1;n.forEach((function(t){void 0!==t.attributeKeyType&&(c=!0),void 0!==t.attributeComment&&(u=!0)})),n.forEach((function(n){var i="".concat(e.node().id,"-attr-").concat(y),o=0,g=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-type")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+a+"px").text(n.attributeType),m=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-name")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+a+"px").text(n.attributeName),v={};v.tn=g,v.nn=m;var b=g.node().getBBox(),_=m.node().getBBox();if(l=Math.max(l,b.width),h=Math.max(h,_.width),o=Math.max(b.height,_.height),c){var x=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-key")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+a+"px").text(n.attributeKeyType||"");v.kn=x;var w=x.node().getBBox();f=Math.max(f,w.width),o=Math.max(o,w.height)}if(u){var k=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-comment")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+a+"px").text(n.attributeComment||"");v.cn=k;var T=k.node().getBBox();d=Math.max(d,T.width),o=Math.max(o,T.height)}v.height=o,s.push(v),p+=o+2*r,y+=1}));var g=4;c&&(g+=2),u&&(g+=2);var m=l+h+f+d,v={width:Math.max(j_.minEntityWidth,Math.max(o.width+2*j_.entityPadding,m+i*g)),height:n.length>0?p:Math.max(j_.minEntityHeight,o.height+2*j_.entityPadding)};if(n.length>0){var b=Math.max(0,(v.width-m-i*g)/(g/2));e.attr("transform","translate("+v.width/2+","+(r+o.height/2)+")");var _=o.height+2*r,x="attributeBoxOdd";s.forEach((function(e){var n=_+r+e.height/2;e.tn.attr("transform","translate("+i+","+n+")");var a=t.insert("rect","#"+e.tn.node().id).attr("class","er ".concat(x)).attr("fill",j_.fill).attr("fill-opacity","100%").attr("stroke",j_.stroke).attr("x",0).attr("y",_).attr("width",l+2*i+b).attr("height",e.height+2*r),o=parseFloat(a.attr("x"))+parseFloat(a.attr("width"));e.nn.attr("transform","translate("+(o+i)+","+n+")");var s=t.insert("rect","#"+e.nn.node().id).attr("class","er ".concat(x)).attr("fill",j_.fill).attr("fill-opacity","100%").attr("stroke",j_.stroke).attr("x",o).attr("y",_).attr("width",h+2*i+b).attr("height",e.height+2*r),p=parseFloat(s.attr("x"))+parseFloat(s.attr("width"));if(c){e.kn.attr("transform","translate("+(p+i)+","+n+")");var y=t.insert("rect","#"+e.kn.node().id).attr("class","er ".concat(x)).attr("fill",j_.fill).attr("fill-opacity","100%").attr("stroke",j_.stroke).attr("x",p).attr("y",_).attr("width",f+2*i+b).attr("height",e.height+2*r);p=parseFloat(y.attr("x"))+parseFloat(y.attr("width"))}u&&(e.cn.attr("transform","translate("+(p+i)+","+n+")"),t.insert("rect","#"+e.cn.node().id).attr("class","er ".concat(x)).attr("fill",j_.fill).attr("fill-opacity","100%").attr("stroke",j_.stroke).attr("x",p).attr("y",_).attr("width",d+2*i+b).attr("height",e.height+2*r)),_+=e.height+2*r,x="attributeBoxOdd"==x?"attributeBoxEven":"attributeBoxOdd"}))}else v.height=Math.max(j_.minEntityHeight,p),e.attr("transform","translate("+v.width/2+","+v.height/2+")");return v}(a,s,e[i].attributes),u=c.width,l=c.height,h=a.insert("rect","#"+o).attr("class","er entityBox").attr("fill",j_.fill).attr("fill-opacity","100%").attr("stroke",j_.stroke).attr("x",0).attr("y",0).attr("width",u).attr("height",l).node().getBBox();n.setNode(i,{width:h.width,height:h.height,shape:"rect",id:i})})),r}(i,L_.getEntities(),r),s=function(t,e){return t.forEach((function(t){e.setEdge(t.entityA,t.entityB,{relationship:t},Y_(t))})),t}(L_.getRelationships(),r);xb().layout(r),function(t,e){e.nodes().forEach((function(n){void 0!==n&&void 0!==e.node(n)&&t.select("#"+n).attr("transform","translate("+(e.node(n).x-e.node(n).width/2)+","+(e.node(n).y-e.node(n).height/2)+" )")}))}(i,r),s.forEach((function(t){!function(t,e,n,r){z_++;var i=n.edge(e.entityA,e.entityB,Y_(e)),a=zu().x((function(t){return t.x})).y((function(t){return t.y})).curve(Vu),o=t.insert("path","#"+r).attr("class","er relationshipLine").attr("d",a(i.points)).attr("stroke",j_.stroke).attr("fill","none");e.relSpec.relType===L_.Identification.NON_IDENTIFYING&&o.attr("stroke-dasharray","8,8");var s="";switch(j_.arrowMarkerAbsolute&&(s=(s=(s=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),e.relSpec.cardA){case L_.Cardinality.ZERO_OR_ONE:o.attr("marker-end","url("+s+"#"+P_.ZERO_OR_ONE_END+")");break;case L_.Cardinality.ZERO_OR_MORE:o.attr("marker-end","url("+s+"#"+P_.ZERO_OR_MORE_END+")");break;case L_.Cardinality.ONE_OR_MORE:o.attr("marker-end","url("+s+"#"+P_.ONE_OR_MORE_END+")");break;case L_.Cardinality.ONLY_ONE:o.attr("marker-end","url("+s+"#"+P_.ONLY_ONE_END+")")}switch(e.relSpec.cardB){case L_.Cardinality.ZERO_OR_ONE:o.attr("marker-start","url("+s+"#"+P_.ZERO_OR_ONE_START+")");break;case L_.Cardinality.ZERO_OR_MORE:o.attr("marker-start","url("+s+"#"+P_.ZERO_OR_MORE_START+")");break;case L_.Cardinality.ONE_OR_MORE:o.attr("marker-start","url("+s+"#"+P_.ONE_OR_MORE_START+")");break;case L_.Cardinality.ONLY_ONE:o.attr("marker-start","url("+s+"#"+P_.ONLY_ONE_START+")")}var c=o.node().getTotalLength(),u=o.node().getPointAtLength(.5*c),l="rel"+z_,h=t.append("text").attr("class","er relationshipLabel").attr("id",l).attr("x",u.x).attr("y",u.y).attr("text-anchor","middle").attr("dominant-baseline","middle").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+j_.fontSize+"px").text(e.roleA).node().getBBox();t.insert("rect","#"+l).attr("class","er relationshipLabelBox").attr("x",u.x-h.width/2).attr("y",u.y-h.height/2).attr("width",h.width).attr("height",h.height).attr("fill","white").attr("fill-opacity","85%")}(i,t,r,a)}));var c=j_.diagramPadding,u=i.node().getBBox(),l=u.width+2*c,h=u.height+2*c;zv(i,h,l,j_.useMaxWidth),i.attr("viewBox","".concat(u.x-c," ").concat(u.y-c," ").concat(l," ").concat(h))};function H_(t){return H_="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},H_(t)}function $_(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var W_,V_,G_="flowchart-",X_=0,Z_=Jv(),Q_={},K_=[],J_=[],tx=[],ex={},nx={},rx=0,ix=!0,ax=[],ox=function(t){for(var e=Object.keys(Q_),n=0;n<e.length;n++)if(Q_[e[n]].id===t)return Q_[e[n]].domId;return t},sx=function(t,e,n,r){var i={start:t,end:e,type:void 0,text:""};void 0!==(r=n.text)&&(i.text=Um.sanitizeText(r.trim(),Z_),'"'===i.text[0]&&'"'===i.text[i.text.length-1]&&(i.text=i.text.substring(1,i.text.length-1))),void 0!==n&&(i.type=n.type,i.stroke=n.stroke,i.length=n.length),K_.push(i)},cx=function(t,e){t.split(",").forEach((function(t){var n=t;void 0!==Q_[n]&&Q_[n].classes.push(e),void 0!==ex[n]&&ex[n].classes.push(e)}))},ux=function(t){var e=au(".mermaidTooltip");null===(e._groups||e)[0][0]&&(e=au("body").append("div").attr("class","mermaidTooltip").style("opacity",0)),au(t).select("svg").selectAll("g.node").on("mouseover",(function(){var t=au(this);if(null!==t.attr("title")){var n=this.getBoundingClientRect();e.transition().duration(200).style("opacity",".9"),e.html(t.attr("title")).style("left",window.scrollX+n.left+(n.right-n.left)/2+"px").style("top",window.scrollY+n.top-14+document.body.scrollTop+"px"),t.classed("hover",!0)}})).on("mouseout",(function(){e.transition().duration(500).style("opacity",0),au(this).classed("hover",!1)}))};ax.push(ux);var lx=function(t){for(var e=0;e<tx.length;e++)if(tx[e].id===t)return e;return-1},hx=-1,fx=[],dx=function t(e,n){var r=tx[n].nodes;if(!((hx+=1)>2e3)){if(fx[hx]=n,tx[n].id===e)return{result:!0,count:0};for(var i=0,a=1;i<r.length;){var o=lx(r[i]);if(o>=0){var s=t(e,o);if(s.result)return{result:!0,count:a+s.count};a+=s.count}i+=1}return{result:!1,count:a}}},px=function(t,e){var n=!1;return t.forEach((function(t){t.nodes.indexOf(e)>=0&&(n=!0)})),n},yx=function(t,e){var n=[];return t.nodes.forEach((function(r,i){px(e,r)||n.push(t.nodes[i])})),{nodes:n}};const gx={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},defaultConfig:function(){return Vv.flowchart},addVertex:function(t,e,n,r,i,a){var o,s=arguments.length>6&&void 0!==arguments[6]?arguments[6]:{},c=t;void 0!==c&&0!==c.trim().length&&(void 0===Q_[c]&&(Q_[c]={id:c,domId:G_+c+"-"+X_,styles:[],classes:[]}),X_++,void 0!==e?(Z_=Jv(),'"'===(o=Um.sanitizeText(e.trim(),Z_))[0]&&'"'===o[o.length-1]&&(o=o.substring(1,o.length-1)),Q_[c].text=o):void 0===Q_[c].text&&(Q_[c].text=t),void 0!==n&&(Q_[c].type=n),null!=r&&r.forEach((function(t){Q_[c].styles.push(t)})),null!=i&&i.forEach((function(t){Q_[c].classes.push(t)})),void 0!==a&&(Q_[c].dir=a),Q_[c].props=s)},lookUpDomId:ox,addLink:function(t,e,n,r){var i,a;for(i=0;i<t.length;i++)for(a=0;a<e.length;a++)sx(t[i],e[a],n,r)},updateLinkInterpolate:function(t,e){t.forEach((function(t){"default"===t?K_.defaultInterpolate=e:K_[t].interpolate=e}))},updateLink:function(t,e){t.forEach((function(t){"default"===t?K_.defaultStyle=e:(-1===Hv.isSubstringInArray("fill",e)&&e.push("fill:none"),K_[t].style=e)}))},addClass:function(t,e){void 0===J_[t]&&(J_[t]={id:t,styles:[],textStyles:[]}),null!=e&&e.forEach((function(e){if(e.match("color")){var n=e.replace("fill","bgFill").replace("color","fill");J_[t].textStyles.push(n)}J_[t].styles.push(e)}))},setDirection:function(t){(W_=t).match(/.*</)&&(W_="RL"),W_.match(/.*\^/)&&(W_="BT"),W_.match(/.*>/)&&(W_="LR"),W_.match(/.*v/)&&(W_="TB")},setClass:cx,setTooltip:function(t,e){t.split(",").forEach((function(t){void 0!==e&&(nx["gen-1"===V_?ox(t):t]=Um.sanitizeText(e,Z_))}))},getTooltip:function(t){return nx[t]},setClickEvent:function(t,e,n){t.split(",").forEach((function(t){!function(t,e,n){var r=ox(t);if("loose"===Jv().securityLevel&&void 0!==e){var i=[];if("string"==typeof n){i=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var a=0;a<i.length;a++){var o=i[a].trim();'"'===o.charAt(0)&&'"'===o.charAt(o.length-1)&&(o=o.substr(1,o.length-2)),i[a]=o}}0===i.length&&i.push(t),void 0!==Q_[t]&&(Q_[t].haveCallback=!0,ax.push((function(){var t=document.querySelector('[id="'.concat(r,'"]'));null!==t&&t.addEventListener("click",(function(){var t;Hv.runFunc.apply(Hv,[e].concat(function(t){if(Array.isArray(t))return $_(t)}(t=i)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return $_(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?$_(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()))}),!1)})))}}(t,e,n)})),cx(t,"clickable")},setLink:function(t,e,n){t.split(",").forEach((function(t){void 0!==Q_[t]&&(Q_[t].link=Hv.formatUrl(e,Z_),Q_[t].linkTarget=n)})),cx(t,"clickable")},bindFunctions:function(t){ax.forEach((function(e){e(t)}))},getDirection:function(){return W_.trim()},getVertices:function(){return Q_},getEdges:function(){return K_},getClasses:function(){return J_},clear:function(t){Q_={},J_={},K_=[],(ax=[]).push(ux),tx=[],ex={},rx=0,nx=[],ix=!0,V_=t||"gen-1"},setGen:function(t){V_=t||"gen-1"},defaultStyle:function(){return"fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;"},addSubGraph:function(t,e,n){var r=t.trim(),i=n;t===n&&n.match(/\s/)&&(r=void 0);var a=[],s=function(t){var e,n={boolean:{},number:{},string:{}},r=[],i=t.filter((function(t){var i=H_(t);return t.stmt&&"dir"===t.stmt?(e=t.value,!1):""!==t.trim()&&(i in n?!n[i].hasOwnProperty(t)&&(n[i][t]=!0):!(r.indexOf(t)>=0)&&r.push(t))}));return{nodeList:i,dir:e}}(a.concat.apply(a,e)),c=s.nodeList,u=s.dir;if(a=c,"gen-1"===V_){o.warn("LOOKING UP");for(var l=0;l<a.length;l++)a[l]=ox(a[l])}r=r||"subGraph"+rx,i=i||"",i=Um.sanitizeText(i,Z_),rx+=1;var h={id:r,nodes:a,title:i.trim(),classes:[],dir:u};return o.info("Adding",h.id,h.nodes,h.dir),h.nodes=yx(h,tx).nodes,tx.push(h),ex[r]=h,r},getDepthFirstPos:function(t){return fx[t]},indexNodes:function(){hx=-1,tx.length>0&&dx("none",tx.length-1)},getSubGraphs:function(){return tx},destructLink:function(t,e){var n,r=function(t){var e=t.trim(),n=e.slice(0,-1),r="arrow_open";switch(e.slice(-1)){case"x":r="arrow_cross","x"===e[0]&&(r="double_"+r,n=n.slice(1));break;case">":r="arrow_point","<"===e[0]&&(r="double_"+r,n=n.slice(1));break;case"o":r="arrow_circle","o"===e[0]&&(r="double_"+r,n=n.slice(1))}var i="normal",a=n.length-1;"="===n[0]&&(i="thick");var o=function(t,e){for(var n=e.length,r=0,i=0;i<n;++i)"."===e[i]&&++r;return r}(0,n);return o&&(i="dotted",a=o),{type:r,stroke:i,length:a}}(t);if(e){if(n=function(t){var e=t.trim(),n="arrow_open";switch(e[0]){case"<":n="arrow_point",e=e.slice(1);break;case"x":n="arrow_cross",e=e.slice(1);break;case"o":n="arrow_circle",e=e.slice(1)}var r="normal";return-1!==e.indexOf("=")&&(r="thick"),-1!==e.indexOf(".")&&(r="dotted"),{type:n,stroke:r}}(e),n.stroke!==r.stroke)return{type:"INVALID",stroke:"INVALID"};if("arrow_open"===n.type)n.type=r.type;else{if(n.type!==r.type)return{type:"INVALID",stroke:"INVALID"};n.type="double_"+n.type}return"double_arrow"===n.type&&(n.type="double_arrow_point"),n.length=r.length,n}return r},lex:{firstGraph:function(){return!!ix&&(ix=!1,!0)}},exists:px,makeUniq:yx};var mx=n(3602),vx=n.n(mx),bx=n(4949),_x=n.n(bx),xx=n(8284),wx=n.n(xx);function kx(t,e,n){var r=.9*(e.width+e.height),i=[{x:r/2,y:0},{x:r,y:-r/2},{x:r/2,y:-r},{x:0,y:-r/2}],a=Lx(t,r,r,i);return n.intersect=function(t){return _x().intersect.polygon(n,i,t)},a}function Tx(t,e,n){var r=e.height,i=r/4,a=e.width+2*i,o=[{x:i,y:0},{x:a-i,y:0},{x:a,y:-r/2},{x:a-i,y:-r},{x:i,y:-r},{x:0,y:-r/2}],s=Lx(t,a,r,o);return n.intersect=function(t){return _x().intersect.polygon(n,o,t)},s}function Ex(t,e,n){var r=e.width,i=e.height,a=[{x:-i/2,y:0},{x:r,y:0},{x:r,y:-i},{x:-i/2,y:-i},{x:0,y:-i/2}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Cx(t,e,n){var r=e.width,i=e.height,a=[{x:-2*i/6,y:0},{x:r-i/6,y:0},{x:r+2*i/6,y:-i},{x:i/6,y:-i}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Sx(t,e,n){var r=e.width,i=e.height,a=[{x:2*i/6,y:0},{x:r+i/6,y:0},{x:r-2*i/6,y:-i},{x:-i/6,y:-i}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Ax(t,e,n){var r=e.width,i=e.height,a=[{x:-2*i/6,y:0},{x:r+2*i/6,y:0},{x:r-i/6,y:-i},{x:i/6,y:-i}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Mx(t,e,n){var r=e.width,i=e.height,a=[{x:i/6,y:0},{x:r-i/6,y:0},{x:r+2*i/6,y:-i},{x:-2*i/6,y:-i}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Nx(t,e,n){var r=e.width,i=e.height,a=[{x:0,y:0},{x:r+i/2,y:0},{x:r,y:-i/2},{x:r+i/2,y:-i},{x:0,y:-i}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Dx(t,e,n){var r=e.height,i=e.width+r/4,a=t.insert("rect",":first-child").attr("rx",r/2).attr("ry",r/2).attr("x",-i/2).attr("y",-r/2).attr("width",i).attr("height",r);return n.intersect=function(t){return _x().intersect.rect(n,t)},a}function Ox(t,e,n){var r=e.width,i=e.height,a=[{x:0,y:0},{x:r,y:0},{x:r,y:-i},{x:0,y:-i},{x:0,y:0},{x:-8,y:0},{x:r+8,y:0},{x:r+8,y:-i},{x:-8,y:-i},{x:-8,y:0}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Bx(t,e,n){var r=e.width,i=r/2,a=i/(2.5+r/50),o=e.height+a,s="M 0,"+a+" a "+i+","+a+" 0,0,0 "+r+" 0 a "+i+","+a+" 0,0,0 "+-r+" 0 l 0,"+o+" a "+i+","+a+" 0,0,0 "+r+" 0 l 0,"+-o,c=t.attr("label-offset-y",a).insert("path",":first-child").attr("d",s).attr("transform","translate("+-r/2+","+-(o/2+a)+")");return n.intersect=function(t){var e=_x().intersect.rect(n,t),r=e.x-n.x;if(0!=i&&(Math.abs(r)<n.width/2||Math.abs(r)==n.width/2&&Math.abs(e.y-n.y)>n.height/2-a)){var o=a*a*(1-r*r/(i*i));0!=o&&(o=Math.sqrt(o)),o=a-o,t.y-n.y>0&&(o=-o),e.y+=o}return e},c}function Lx(t,e,n,r){return t.insert("polygon",":first-child").attr("points",r.map((function(t){return t.x+","+t.y})).join(" ")).attr("transform","translate("+-e/2+","+n/2+")")}const Ix=function(t){t.shapes().question=kx,t.shapes().hexagon=Tx,t.shapes().stadium=Dx,t.shapes().subroutine=Ox,t.shapes().cylinder=Bx,t.shapes().rect_left_inv_arrow=Ex,t.shapes().lean_right=Cx,t.shapes().lean_left=Sx,t.shapes().trapezoid=Ax,t.shapes().inv_trapezoid=Mx,t.shapes().rect_right_inv_arrow=Nx};var Rx={};const Fx=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)Rx[e[n]]=t[e[n]]},Px=function(t,e){o.info("Drawing flowchart"),gx.clear(),gx.setGen("gen-1");var n=vx().parser;n.yy=gx,n.parse(t);var r=gx.getDirection();void 0===r&&(r="TD");for(var i,a=Jv().flowchart,s=a.nodeSpacing||50,c=a.rankSpacing||50,u=new(kb().Graph)({multigraph:!0,compound:!0}).setGraph({rankdir:r,nodesep:s,ranksep:c,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),l=gx.getSubGraphs(),h=l.length-1;h>=0;h--)i=l[h],gx.addVertex(i.id,i.title,"group",void 0,i.classes);var f=gx.getVertices();o.warn("Get vertices",f);var d=gx.getEdges(),p=0;for(p=l.length-1;p>=0;p--){i=l[p],ou("cluster").append("text");for(var y=0;y<i.nodes.length;y++)o.warn("Setting subgraph",i.nodes[y],gx.lookUpDomId(i.nodes[y]),gx.lookUpDomId(i.id)),u.setParent(gx.lookUpDomId(i.nodes[y]),gx.lookUpDomId(i.id))}(function(t,e,n){var r=au('[id="'.concat(n,'"]'));Object.keys(t).forEach((function(n){var i=t[n],a="default";i.classes.length>0&&(a=i.classes.join(" "));var s,c=Nv(i.styles),u=void 0!==i.text?i.text:i.id;if(zm(Jv().flowchart.htmlLabels)){var l={label:u.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")}))};(s=wx()(r,l).node()).parentNode.removeChild(s)}else{var h=document.createElementNS("http://www.w3.org/2000/svg","text");h.setAttribute("style",c.labelStyle.replace("color:","fill:"));for(var f=u.split(Um.lineBreakRegex),d=0;d<f.length;d++){var p=document.createElementNS("http://www.w3.org/2000/svg","tspan");p.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),p.setAttribute("dy","1em"),p.setAttribute("x","1"),p.textContent=f[d],h.appendChild(p)}s=h}var y=0,g="";switch(i.type){case"round":y=5,g="rect";break;case"square":case"group":default:g="rect";break;case"diamond":g="question";break;case"hexagon":g="hexagon";break;case"odd":case"odd_right":g="rect_left_inv_arrow";break;case"lean_right":g="lean_right";break;case"lean_left":g="lean_left";break;case"trapezoid":g="trapezoid";break;case"inv_trapezoid":g="inv_trapezoid";break;case"circle":g="circle";break;case"ellipse":g="ellipse";break;case"stadium":g="stadium";break;case"subroutine":g="subroutine";break;case"cylinder":g="cylinder"}o.warn("Adding node",i.id,i.domId),e.setNode(gx.lookUpDomId(i.id),{labelType:"svg",labelStyle:c.labelStyle,shape:g,label:s,rx:y,ry:y,class:a,style:c.style,id:gx.lookUpDomId(i.id)})}))})(f,u,e),function(t,e){var n,r,i=0;if(void 0!==t.defaultStyle){var a=Nv(t.defaultStyle);n=a.style,r=a.labelStyle}t.forEach((function(a){i++;var o="L-"+a.start+"-"+a.end,s="LS-"+a.start,c="LE-"+a.end,u={};"arrow_open"===a.type?u.arrowhead="none":u.arrowhead="normal";var l="",h="";if(void 0!==a.style){var f=Nv(a.style);l=f.style,h=f.labelStyle}else switch(a.stroke){case"normal":l="fill:none",void 0!==n&&(l=n),void 0!==r&&(h=r);break;case"dotted":l="fill:none;stroke-width:2px;stroke-dasharray:3;";break;case"thick":l=" stroke-width: 3.5px;fill:none"}u.style=l,u.labelStyle=h,void 0!==a.interpolate?u.curve=Av(a.interpolate,Pu):void 0!==t.defaultInterpolate?u.curve=Av(t.defaultInterpolate,Pu):u.curve=Av(Rx.curve,Pu),void 0===a.text?void 0!==a.style&&(u.arrowheadStyle="fill: #333"):(u.arrowheadStyle="fill: #333",u.labelpos="c",zm(Jv().flowchart.htmlLabels)?(u.labelType="html",u.label='<span id="L-'.concat(o,'" class="edgeLabel L-').concat(s,"' L-").concat(c,'">').concat(a.text.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")})),"</span>")):(u.labelType="text",u.label=a.text.replace(Um.lineBreakRegex,"\n"),void 0===a.style&&(u.style=u.style||"stroke: #333; stroke-width: 1.5px;fill:none"),u.labelStyle=u.labelStyle.replace("color:","fill:"))),u.id=o,u.class=s+" "+c,u.minlen=a.length||1,e.setEdge(gx.lookUpDomId(a.start),gx.lookUpDomId(a.end),u,i)}))}(d,u);var g=new(0,_x().render);Ix(g),g.arrows().none=function(t,e,n,r){var i=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 0 0 L 0 0 z");_x().util.applyStyle(i,n[r+"Style"])},g.arrows().normal=function(t,e){t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowheadPath").style("stroke-width",1).style("stroke-dasharray","1,0")};var m=au('[id="'.concat(e,'"]'));m.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),o.warn(u);var v=au("#"+e+" g");g(v,u),v.selectAll("g.node").attr("title",(function(){return gx.getTooltip(this.id)}));var b=a.diagramPadding,_=m.node().getBBox(),x=_.width+2*b,w=_.height+2*b;zv(m,w,x,a.useMaxWidth);var k="".concat(_.x-b," ").concat(_.y-b," ").concat(x," ").concat(w);for(o.debug("viewBox ".concat(k)),m.attr("viewBox",k),gx.indexNodes("subGraph"+p),p=0;p<l.length;p++)if("undefined"!==(i=l[p]).title){var T=document.querySelectorAll("#"+e+' [id="'+gx.lookUpDomId(i.id)+'"] rect'),E=document.querySelectorAll("#"+e+' [id="'+gx.lookUpDomId(i.id)+'"]'),C=T[0].x.baseVal.value,S=T[0].y.baseVal.value,A=T[0].width.baseVal.value,M=au(E[0]).select(".label");M.attr("transform","translate(".concat(C+A/2,", ").concat(S+14,")")),M.attr("id",e+"Text");for(var N=0;N<i.classes.length;N++)E[0].classList.add(i.classes[N])}zm(a.htmlLabels);for(var D=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),O=0;O<D.length;O++){var B=D[O],L=B.getBBox(),I=document.createElementNS("http://www.w3.org/2000/svg","rect");I.setAttribute("rx",0),I.setAttribute("ry",0),I.setAttribute("width",L.width),I.setAttribute("height",L.height),B.insertBefore(I,B.firstChild)}Object.keys(f).forEach((function(t){var n=f[t];if(n.link){var r=au("#"+e+' [id="'+gx.lookUpDomId(t)+'"]');if(r){var i=document.createElementNS("http://www.w3.org/2000/svg","a");i.setAttributeNS("http://www.w3.org/2000/svg","class",n.classes.join(" ")),i.setAttributeNS("http://www.w3.org/2000/svg","href",n.link),i.setAttributeNS("http://www.w3.org/2000/svg","rel","noopener"),n.linkTarget&&i.setAttributeNS("http://www.w3.org/2000/svg","target",n.linkTarget);var a=r.insert((function(){return i}),":first-child"),o=r.select(".label-container");o&&a.append((function(){return o.node()}));var s=r.select(".label");s&&a.append((function(){return s.node()}))}}}))};var jx={};const Yx=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)jx[e[n]]=t[e[n]]},zx=function(t,e){o.info("Drawing flowchart"),gx.clear(),gx.setGen("gen-2");var n=vx().parser;n.yy=gx,n.parse(t);var r=gx.getDirection();void 0===r&&(r="TD");var i,a=Jv().flowchart,s=a.nodeSpacing||50,c=a.rankSpacing||50,u=new(kb().Graph)({multigraph:!0,compound:!0}).setGraph({rankdir:r,nodesep:s,ranksep:c,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),l=gx.getSubGraphs();o.info("Subgraphs - ",l);for(var h=l.length-1;h>=0;h--)i=l[h],o.info("Subgraph - ",i),gx.addVertex(i.id,i.title,"group",void 0,i.classes,i.dir);var f=gx.getVertices(),d=gx.getEdges();o.info(d);var p=0;for(p=l.length-1;p>=0;p--){i=l[p],ou("cluster").append("text");for(var y=0;y<i.nodes.length;y++)o.info("Setting up subgraphs",i.nodes[y],i.id),u.setParent(i.nodes[y],i.id)}(function(t,e,n){var r=au('[id="'.concat(n,'"]'));Object.keys(t).forEach((function(n){var i=t[n],a="default";i.classes.length>0&&(a=i.classes.join(" "));var s,c=Nv(i.styles),u=void 0!==i.text?i.text:i.id;if(zm(Jv().flowchart.htmlLabels)){var l={label:u.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")}))};(s=wx()(r,l).node()).parentNode.removeChild(s)}else{var h=document.createElementNS("http://www.w3.org/2000/svg","text");h.setAttribute("style",c.labelStyle.replace("color:","fill:"));for(var f=u.split(Um.lineBreakRegex),d=0;d<f.length;d++){var p=document.createElementNS("http://www.w3.org/2000/svg","tspan");p.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),p.setAttribute("dy","1em"),p.setAttribute("x","1"),p.textContent=f[d],h.appendChild(p)}s=h}var y=0,g="";switch(i.type){case"round":y=5,g="rect";break;case"square":case"group":default:g="rect";break;case"diamond":g="question";break;case"hexagon":g="hexagon";break;case"odd":case"odd_right":g="rect_left_inv_arrow";break;case"lean_right":g="lean_right";break;case"lean_left":g="lean_left";break;case"trapezoid":g="trapezoid";break;case"inv_trapezoid":g="inv_trapezoid";break;case"circle":g="circle";break;case"ellipse":g="ellipse";break;case"stadium":g="stadium";break;case"subroutine":g="subroutine";break;case"cylinder":g="cylinder"}e.setNode(i.id,{labelStyle:c.labelStyle,shape:g,labelText:u,rx:y,ry:y,class:a,style:c.style,id:i.id,link:i.link,linkTarget:i.linkTarget,tooltip:gx.getTooltip(i.id)||"",domId:gx.lookUpDomId(i.id),haveCallback:i.haveCallback,width:"group"===i.type?500:void 0,dir:i.dir,type:i.type,props:i.props,padding:Jv().flowchart.padding}),o.info("setNode",{labelStyle:c.labelStyle,shape:g,labelText:u,rx:y,ry:y,class:a,style:c.style,id:i.id,domId:gx.lookUpDomId(i.id),width:"group"===i.type?500:void 0,type:i.type,dir:i.dir,props:i.props,padding:Jv().flowchart.padding})}))})(f,u,e),function(t,e){o.info("abc78 edges = ",t);var n,r,i=0,a={};if(void 0!==t.defaultStyle){var s=Nv(t.defaultStyle);n=s.style,r=s.labelStyle}t.forEach((function(s){i++;var c="L-"+s.start+"-"+s.end;void 0===a[c]?(a[c]=0,o.info("abc78 new entry",c,a[c])):(a[c]++,o.info("abc78 new entry",c,a[c]));var u=c+"-"+a[c];o.info("abc78 new link id to be used is",c,u,a[c]);var l="LS-"+s.start,h="LE-"+s.end,f={style:"",labelStyle:""};switch(f.minlen=s.length||1,"arrow_open"===s.type?f.arrowhead="none":f.arrowhead="normal",f.arrowTypeStart="arrow_open",f.arrowTypeEnd="arrow_open",s.type){case"double_arrow_cross":f.arrowTypeStart="arrow_cross";case"arrow_cross":f.arrowTypeEnd="arrow_cross";break;case"double_arrow_point":f.arrowTypeStart="arrow_point";case"arrow_point":f.arrowTypeEnd="arrow_point";break;case"double_arrow_circle":f.arrowTypeStart="arrow_circle";case"arrow_circle":f.arrowTypeEnd="arrow_circle"}var d="",p="";switch(s.stroke){case"normal":d="fill:none;",void 0!==n&&(d=n),void 0!==r&&(p=r),f.thickness="normal",f.pattern="solid";break;case"dotted":f.thickness="normal",f.pattern="dotted",f.style="fill:none;stroke-width:2px;stroke-dasharray:3;";break;case"thick":f.thickness="thick",f.pattern="solid",f.style="stroke-width: 3.5px;fill:none;"}if(void 0!==s.style){var y=Nv(s.style);d=y.style,p=y.labelStyle}f.style=f.style+=d,f.labelStyle=f.labelStyle+=p,void 0!==s.interpolate?f.curve=Av(s.interpolate,Pu):void 0!==t.defaultInterpolate?f.curve=Av(t.defaultInterpolate,Pu):f.curve=Av(jx.curve,Pu),void 0===s.text?void 0!==s.style&&(f.arrowheadStyle="fill: #333"):(f.arrowheadStyle="fill: #333",f.labelpos="c"),f.labelType="text",f.label=s.text.replace(Um.lineBreakRegex,"\n"),void 0===s.style&&(f.style=f.style||"stroke: #333; stroke-width: 1.5px;fill:none;"),f.labelStyle=f.labelStyle.replace("color:","fill:"),f.id=u,f.classes="flowchart-link "+l+" "+h,e.setEdge(s.start,s.end,f,i)}))}(d,u);var g=au('[id="'.concat(e,'"]'));g.attr("xmlns:xlink","http://www.w3.org/1999/xlink");var m=au("#"+e+" g");S_(m,u,["point","circle","cross"],"flowchart",e);var v=a.diagramPadding,b=g.node().getBBox(),_=b.width+2*v,x=b.height+2*v;if(o.debug("new ViewBox 0 0 ".concat(_," ").concat(x),"translate(".concat(v-u._label.marginx,", ").concat(v-u._label.marginy,")")),zv(g,x,_,a.useMaxWidth),g.attr("viewBox","0 0 ".concat(_," ").concat(x)),g.select("g").attr("transform","translate(".concat(v-u._label.marginx,", ").concat(v-b.y,")")),gx.indexNodes("subGraph"+p),!a.htmlLabels)for(var w=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),k=0;k<w.length;k++){var T=w[k],E=T.getBBox(),C=document.createElementNS("http://www.w3.org/2000/svg","rect");C.setAttribute("rx",0),C.setAttribute("ry",0),C.setAttribute("width",E.width),C.setAttribute("height",E.height),T.insertBefore(C,T.firstChild)}Object.keys(f).forEach((function(t){var n=f[t];if(n.link){var r=au("#"+e+' [id="'+t+'"]');if(r){var i=document.createElementNS("http://www.w3.org/2000/svg","a");i.setAttributeNS("http://www.w3.org/2000/svg","class",n.classes.join(" ")),i.setAttributeNS("http://www.w3.org/2000/svg","href",n.link),i.setAttributeNS("http://www.w3.org/2000/svg","rel","noopener"),n.linkTarget&&i.setAttributeNS("http://www.w3.org/2000/svg","target",n.linkTarget);var a=r.insert((function(){return i}),":first-child"),o=r.select(".label-container");o&&a.append((function(){return o.node()}));var s=r.select(".label");s&&a.append((function(){return s.node()}))}}}))};function Ux(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var qx,Hx,$x="",Wx="",Vx="",Gx=[],Xx=[],Zx="",Qx=[],Kx=[],Jx="",tw=["active","done","crit","milestone"],ew=[],nw=!1,rw=!1,iw=0,aw=function(t,e,n,r){return!(r.indexOf(t.format(e.trim()))>=0)&&(t.isoWeekday()>=6&&n.indexOf("weekends")>=0||n.indexOf(t.format("dddd").toLowerCase())>=0||n.indexOf(t.format(e.trim()))>=0)},ow=function(t,e,n,r){if(n.length&&!t.manualEndTime){var a=i()(t.startTime,e,!0);a.add(1,"d");var o=i()(t.endTime,e,!0),s=sw(a,o,e,n,r);t.endTime=o.toDate(),t.renderEndTime=s}},sw=function(t,e,n,r,i){for(var a=!1,o=null;t<=e;)a||(o=e.toDate()),(a=aw(t,n,r,i))&&e.add(1,"d"),t.add(1,"d");return o},cw=function(t,e,n){n=n.trim();var r=/^after\s+([\d\w- ]+)/.exec(n.trim());if(null!==r){var a=null;if(r[1].split(" ").forEach((function(t){var e=yw(t);void 0!==e&&(a?e.endTime>a.endTime&&(a=e):a=e)})),a)return a.endTime;var s=new Date;return s.setHours(0,0,0,0),s}var c=i()(n,e.trim(),!0);return c.isValid()?c.toDate():(o.debug("Invalid date:"+n),o.debug("With date format:"+e.trim()),new Date)},uw=function(t,e){if(null!==t)switch(t[2]){case"s":e.add(t[1],"seconds");break;case"m":e.add(t[1],"minutes");break;case"h":e.add(t[1],"hours");break;case"d":e.add(t[1],"days");break;case"w":e.add(t[1],"weeks")}return e.toDate()},lw=function(t,e,n,r){r=r||!1,n=n.trim();var a=i()(n,e.trim(),!0);return a.isValid()?(r&&a.add(1,"d"),a.toDate()):uw(/^([\d]+)([wdhms])/.exec(n.trim()),i()(t))},hw=0,fw=function(t){return void 0===t?"task"+(hw+=1):t},dw=[],pw={},yw=function(t){var e=pw[t];return dw[e]},gw=function(){for(var t=function(t){var e=dw[t],n="";switch(dw[t].raw.startTime.type){case"prevTaskEnd":var r=yw(e.prevTaskId);e.startTime=r.endTime;break;case"getStartDate":(n=cw(0,$x,dw[t].raw.startTime.startData))&&(dw[t].startTime=n)}return dw[t].startTime&&(dw[t].endTime=lw(dw[t].startTime,$x,dw[t].raw.endTime.data,nw),dw[t].endTime&&(dw[t].processed=!0,dw[t].manualEndTime=i()(dw[t].raw.endTime.data,"YYYY-MM-DD",!0).isValid(),ow(dw[t],$x,Xx,Gx))),dw[t].processed},e=!0,n=0;n<dw.length;n++)t(n),e=e&&dw[n].processed;return e},mw=function(t,e){t.split(",").forEach((function(t){var n=yw(t);void 0!==n&&n.classes.push(e)}))},vw=function(t,e){ew.push((function(){var n=document.querySelector('[id="'.concat(t,'"]'));null!==n&&n.addEventListener("click",(function(){e()}))})),ew.push((function(){var n=document.querySelector('[id="'.concat(t,'-text"]'));null!==n&&n.addEventListener("click",(function(){e()}))}))};const bw={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().gantt},clear:function(){Qx=[],Kx=[],Jx="",ew=[],Zx="",hw=0,qx=void 0,Hx=void 0,dw=[],$x="",Wx="",Vx="",Gx=[],Xx=[],nw=!1,rw=!1,iw=0},setDateFormat:function(t){$x=t},getDateFormat:function(){return $x},enableInclusiveEndDates:function(){nw=!0},endDatesAreInclusive:function(){return nw},enableTopAxis:function(){rw=!0},topAxisEnabled:function(){return rw},setAxisFormat:function(t){Wx=t},getAxisFormat:function(){return Wx},setTodayMarker:function(t){Vx=t},getTodayMarker:function(){return Vx},setTitle:function(t){Zx=t},getTitle:function(){return Zx},addSection:function(t){Jx=t,Qx.push(t)},getSections:function(){return Qx},getTasks:function(){for(var t=gw(),e=0;!t&&e<10;)t=gw(),e++;return Kx=dw},addTask:function(t,e){var n={section:Jx,type:Jx,processed:!1,manualEndTime:!1,renderEndTime:null,raw:{data:e},task:t,classes:[]},r=function(t,e){var n=(":"===e.substr(0,1)?e.substr(1,e.length):e).split(","),r={};_w(n,r,tw);for(var i=0;i<n.length;i++)n[i]=n[i].trim();switch(n.length){case 1:r.id=fw(),r.startTime={type:"prevTaskEnd",id:t},r.endTime={data:n[0]};break;case 2:r.id=fw(),r.startTime={type:"getStartDate",startData:n[0]},r.endTime={data:n[1]};break;case 3:r.id=fw(n[0]),r.startTime={type:"getStartDate",startData:n[1]},r.endTime={data:n[2]}}return r}(Hx,e);n.raw.startTime=r.startTime,n.raw.endTime=r.endTime,n.id=r.id,n.prevTaskId=Hx,n.active=r.active,n.done=r.done,n.crit=r.crit,n.milestone=r.milestone,n.order=iw,iw++;var i=dw.push(n);Hx=n.id,pw[n.id]=i-1},findTaskById:yw,addTaskOrg:function(t,e){var n={section:Jx,type:Jx,description:t,task:t,classes:[]},r=function(t,e){var n=(":"===e.substr(0,1)?e.substr(1,e.length):e).split(","),r={};_w(n,r,tw);for(var a=0;a<n.length;a++)n[a]=n[a].trim();var o="";switch(n.length){case 1:r.id=fw(),r.startTime=t.endTime,o=n[0];break;case 2:r.id=fw(),r.startTime=cw(0,$x,n[0]),o=n[1];break;case 3:r.id=fw(n[0]),r.startTime=cw(0,$x,n[1]),o=n[2]}return o&&(r.endTime=lw(r.startTime,$x,o,nw),r.manualEndTime=i()(o,"YYYY-MM-DD",!0).isValid(),ow(r,$x,Xx,Gx)),r}(qx,e);n.startTime=r.startTime,n.endTime=r.endTime,n.id=r.id,n.active=r.active,n.done=r.done,n.crit=r.crit,n.milestone=r.milestone,qx=n,Kx.push(n)},setIncludes:function(t){Gx=t.toLowerCase().split(/[\s,]+/)},getIncludes:function(){return Gx},setExcludes:function(t){Xx=t.toLowerCase().split(/[\s,]+/)},getExcludes:function(){return Xx},setClickEvent:function(t,e,n){t.split(",").forEach((function(t){!function(t,e,n){if("loose"===Jv().securityLevel&&void 0!==e){var r=[];if("string"==typeof n){r=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var i=0;i<r.length;i++){var a=r[i].trim();'"'===a.charAt(0)&&'"'===a.charAt(a.length-1)&&(a=a.substr(1,a.length-2)),r[i]=a}}0===r.length&&r.push(t),void 0!==yw(t)&&vw(t,(function(){var t;Hv.runFunc.apply(Hv,[e].concat(function(t){if(Array.isArray(t))return Ux(t)}(t=r)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return Ux(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Ux(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()))}))}}(t,e,n)})),mw(t,"clickable")},setLink:function(t,e){var n=e;"loose"!==Jv().securityLevel&&(n=(0,Om.sanitizeUrl)(e)),t.split(",").forEach((function(t){void 0!==yw(t)&&vw(t,(function(){window.open(n,"_self")}))})),mw(t,"clickable")},bindFunctions:function(t){ew.forEach((function(e){e(t)}))},durationToDate:uw,isInvalidDate:aw};function _w(t,e,n){for(var r=!0;r;)r=!1,n.forEach((function(n){var i=new RegExp("^\\s*"+n+"\\s*$");t[0].match(i)&&(e[n]=!0,t.shift(1),r=!0)}))}var xw,ww=n(9959),kw=n.n(ww);ww.parser.yy=bw;const Tw=function(t,e){var n=Jv().gantt;ww.parser.yy.clear(),ww.parser.parse(t);var r=document.getElementById(e);void 0===(xw=r.parentElement.offsetWidth)&&(xw=1200),void 0!==n.useWidth&&(xw=n.useWidth);var a=ww.parser.yy.getTasks(),o=a.length*(n.barHeight+n.barGap)+2*n.topPadding;r.setAttribute("viewBox","0 0 "+xw+" "+o);for(var s=au('[id="'.concat(e,'"]')),c=function(){return Zi.apply(qs(mo,vo,Ga,Wa,Pa,Ra,La,Oa,Na,ko).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)}().domain([l(a,(function(t){return t.startTime})),u(a,(function(t){return t.endTime}))]).rangeRound([0,xw-n.leftPadding-n.rightPadding]),h=[],f=0;f<a.length;f++)h.push(a[f].type);var d=h;h=function(t){for(var e={},n=[],r=0,i=t.length;r<i;++r)Object.prototype.hasOwnProperty.call(e,t[r])||(e[t[r]]=!0,n.push(t[r]));return n}(h),a.sort((function(t,e){var n=t.startTime,r=e.startTime,i=0;return n>r?i=1:n<r&&(i=-1),i})),function(t,e,r){var a=n.barHeight,o=a+n.barGap,u=n.topPadding,l=n.leftPadding;fa().domain([0,h.length]).range(["#00B9FA","#F95002"]).interpolate(jr),function(t,e,r,a,o,u,l,h){var f=u.reduce((function(t,e){var n=e.startTime;return t?Math.min(t,n):n}),0),d=u.reduce((function(t,e){var n=e.endTime;return t?Math.max(t,n):n}),0),p=ww.parser.yy.getDateFormat();if(f&&d){for(var y=[],g=null,m=i()(f);m.valueOf()<=d;)ww.parser.yy.isInvalidDate(m,p,l,h)?g?g.end=m.clone():g={start:m.clone(),end:m.clone()}:g&&(y.push(g),g=null),m.add(1,"d");s.append("g").selectAll("rect").data(y).enter().append("rect").attr("id",(function(t){return"exclude-"+t.start.format("YYYY-MM-DD")})).attr("x",(function(t){return c(t.start)+r})).attr("y",n.gridLineStartPadding).attr("width",(function(t){var e=t.end.clone().add(1,"day");return c(e)-c(t.start)})).attr("height",o-e-n.gridLineStartPadding).attr("transform-origin",(function(e,n){return(c(e.start)+r+.5*(c(e.end)-c(e.start))).toString()+"px "+(n*t+.5*o).toString()+"px"})).attr("class","exclude-range")}}(o,u,l,0,r,t,ww.parser.yy.getExcludes(),ww.parser.yy.getIncludes()),function(t,e,r,i){var a,o=(a=c,v(3,a)).tickSize(-i+e+n.gridLineStartPadding).tickFormat(Yl(ww.parser.yy.getAxisFormat()||n.axisFormat||"%Y-%m-%d"));if(s.append("g").attr("class","grid").attr("transform","translate("+t+", "+(i-50)+")").call(o).selectAll("text").style("text-anchor","middle").attr("fill","#000").attr("stroke","none").attr("font-size",10).attr("dy","1em"),bw.topAxisEnabled()||n.topAxis){var u=function(t){return v(1,t)}(c).tickSize(-i+e+n.gridLineStartPadding).tickFormat(Yl(ww.parser.yy.getAxisFormat()||n.axisFormat||"%Y-%m-%d"));s.append("g").attr("class","grid").attr("transform","translate("+t+", "+e+")").call(u).selectAll("text").style("text-anchor","middle").attr("fill","#000").attr("stroke","none").attr("font-size",10)}}(l,u,0,r),function(t,e,r,i,a,o,u){s.append("g").selectAll("rect").data(t).enter().append("rect").attr("x",0).attr("y",(function(t,n){return t.order*e+r-2})).attr("width",(function(){return u-n.rightPadding/2})).attr("height",e).attr("class",(function(t){for(var e=0;e<h.length;e++)if(t.type===h[e])return"section section"+e%n.numberSectionStyles;return"section section0"}));var l=s.append("g").selectAll("rect").data(t).enter();l.append("rect").attr("id",(function(t){return t.id})).attr("rx",3).attr("ry",3).attr("x",(function(t){return t.milestone?c(t.startTime)+i+.5*(c(t.endTime)-c(t.startTime))-.5*a:c(t.startTime)+i})).attr("y",(function(t,n){return t.order*e+r})).attr("width",(function(t){return t.milestone?a:c(t.renderEndTime||t.endTime)-c(t.startTime)})).attr("height",a).attr("transform-origin",(function(t,n){return n=t.order,(c(t.startTime)+i+.5*(c(t.endTime)-c(t.startTime))).toString()+"px "+(n*e+r+.5*a).toString()+"px"})).attr("class",(function(t){var e="";t.classes.length>0&&(e=t.classes.join(" "));for(var r=0,i=0;i<h.length;i++)t.type===h[i]&&(r=i%n.numberSectionStyles);var a="";return t.active?t.crit?a+=" activeCrit":a=" active":t.done?a=t.crit?" doneCrit":" done":t.crit&&(a+=" crit"),0===a.length&&(a=" task"),t.milestone&&(a=" milestone "+a),"task"+(a+=r)+" "+e})),l.append("text").attr("id",(function(t){return t.id+"-text"})).text((function(t){return t.task})).attr("font-size",n.fontSize).attr("x",(function(t){var e=c(t.startTime),r=c(t.renderEndTime||t.endTime);t.milestone&&(e+=.5*(c(t.endTime)-c(t.startTime))-.5*a),t.milestone&&(r=e+a);var o=this.getBBox().width;return o>r-e?r+o+1.5*n.leftPadding>u?e+i-5:r+i+5:(r-e)/2+e+i})).attr("y",(function(t,i){return t.order*e+n.barHeight/2+(n.fontSize/2-2)+r})).attr("text-height",a).attr("class",(function(t){var e=c(t.startTime),r=c(t.endTime);t.milestone&&(r=e+a);var i=this.getBBox().width,o="";t.classes.length>0&&(o=t.classes.join(" "));for(var s=0,l=0;l<h.length;l++)t.type===h[l]&&(s=l%n.numberSectionStyles);var f="";return t.active&&(f=t.crit?"activeCritText"+s:"activeText"+s),t.done?f=t.crit?f+" doneCritText"+s:f+" doneText"+s:t.crit&&(f=f+" critText"+s),t.milestone&&(f+=" milestoneText"),i>r-e?r+i+1.5*n.leftPadding>u?o+" taskTextOutsideLeft taskTextOutside"+s+" "+f:o+" taskTextOutsideRight taskTextOutside"+s+" "+f+" width-"+i:o+" taskText taskText"+s+" "+f+" width-"+i}))}(t,o,u,l,a,0,e),function(t,e){for(var r=[],i=0,a=0;a<h.length;a++)r[a]=[h[a],(o=h[a],c=d,function(t){for(var e=t.length,n={};e;)n[t[--e]]=(n[t[e]]||0)+1;return n}(c)[o]||0)];var o,c;s.append("g").selectAll("text").data(r).enter().append((function(t){var e=t[0].split(Um.lineBreakRegex),n=-(e.length-1)/2,r=document.createElementNS("http://www.w3.org/2000/svg","text");r.setAttribute("dy",n+"em");for(var i=0;i<e.length;i++){var a=document.createElementNS("http://www.w3.org/2000/svg","tspan");a.setAttribute("alignment-baseline","central"),a.setAttribute("x","10"),i>0&&a.setAttribute("dy","1em"),a.textContent=e[i],r.appendChild(a)}return r})).attr("x",10).attr("y",(function(n,a){if(!(a>0))return n[1]*t/2+e;for(var o=0;o<a;o++)return i+=r[a-1][1],n[1]*t/2+i*t+e})).attr("font-size",n.sectionFontSize).attr("font-size",n.sectionFontSize).attr("class",(function(t){for(var e=0;e<h.length;e++)if(t[0]===h[e])return"sectionTitle sectionTitle"+e%n.numberSectionStyles;return"sectionTitle"}))}(o,u),function(t,e,r,i){var a=bw.getTodayMarker();if("off"!==a){var o=s.append("g").attr("class","today"),u=new Date,l=o.append("line");l.attr("x1",c(u)+t).attr("x2",c(u)+t).attr("y1",n.titleTopMargin).attr("y2",i-n.titleTopMargin).attr("class","today"),""!==a&&l.attr("style",a.replace(/,/g,";"))}}(l,0,0,r)}(a,xw,o),zv(s,o,xw,n.useMaxWidth),s.append("text").text(ww.parser.yy.getTitle()).attr("x",xw/2).attr("y",n.titleTopMargin).attr("class","titleText")};var Ew={},Cw=null,Sw={master:Cw},Aw="master",Mw="LR",Nw=0;function Dw(){return Bv({length:7})}function Ow(t,e){o.debug("Entering isfastforwardable:",t.id,e.id);for(var n=0;t.seq<=e.seq&&t!==e&&n<1e3&&(n++,null!=e.parent);){if(Array.isArray(e.parent))return o.debug("In merge commit:",e.parent),Ow(t,Ew[e.parent[0]])||Ow(t,Ew[e.parent[1]]);e=Ew[e.parent]}return o.debug(t.id,e.id),t.id===e.id}var Bw={};function Lw(t,e,n){var r=t.indexOf(e);-1===r?t.push(n):t.splice(r,1,n)}function Iw(t){var e=t.reduce((function(t,e){return t.seq>e.seq?t:e}),t[0]),n="";t.forEach((function(t){n+=t===e?"\t*":"\t|"}));var r,i,a,s=[n,e.id,e.seq];for(var c in Sw)Sw[c]===e.id&&s.push(c);if(o.debug(s.join(" ")),Array.isArray(e.parent)){var u=Ew[e.parent[0]];Lw(t,e,u),t.push(Ew[e.parent[1]])}else{if(null==e.parent)return;var l=Ew[e.parent];Lw(t,e,l)}r=t,i=function(t){return t.id},a=Object.create(null),Iw(t=r.reduce((function(t,e){var n=i(e);return a[n]||(a[n]=!0,t.push(e)),t}),[]))}var Rw=function(){var t=Object.keys(Ew).map((function(t){return Ew[t]}));return t.forEach((function(t){o.debug(t.id)})),t.sort((function(t,e){return e.seq-t.seq})),t};const Fw={setDirection:function(t){Mw=t},setOptions:function(t){o.debug("options str",t),t=(t=t&&t.trim())||"{}";try{Bw=JSON.parse(t)}catch(t){o.error("error while parsing gitGraph options",t.message)}},getOptions:function(){return Bw},commit:function(t){var e={id:Dw(),message:t,seq:Nw++,parent:null==Cw?null:Cw.id};Cw=e,Ew[e.id]=e,Sw[Aw]=e.id,o.debug("in pushCommit "+e.id)},branch:function(t){Sw[t]=null!=Cw?Cw.id:null,o.debug("in createBranch")},merge:function(t){var e=Ew[Sw[Aw]],n=Ew[Sw[t]];if(function(t,e){return t.seq>e.seq&&Ow(e,t)}(e,n))o.debug("Already merged");else{if(Ow(e,n))Sw[Aw]=Sw[t],Cw=Ew[Sw[Aw]];else{var r={id:Dw(),message:"merged branch "+t+" into "+Aw,seq:Nw++,parent:[null==Cw?null:Cw.id,Sw[t]]};Cw=r,Ew[r.id]=r,Sw[Aw]=r.id}o.debug(Sw),o.debug("in mergeBranch")}},checkout:function(t){o.debug("in checkout");var e=Sw[Aw=t];Cw=Ew[e]},reset:function(t){o.debug("in reset",t);var e=t.split(":")[0],n=parseInt(t.split(":")[1]),r="HEAD"===e?Cw:Ew[Sw[e]];for(o.debug(r,n);n>0;)if(n--,!(r=Ew[r.parent])){var i="Critical error - unique parent commit not found during reset";throw o.error(i),i}Cw=r,Sw[Aw]=r.id},prettyPrint:function(){o.debug(Ew),Iw([Rw()[0]])},clear:function(){Ew={},Sw={master:Cw=null},Aw="master",Nw=0},getBranchesAsObjArray:function(){var t=[];for(var e in Sw)t.push({name:e,commit:Ew[Sw[e]]});return t},getBranches:function(){return Sw},getCommits:function(){return Ew},getCommitsArray:Rw,getCurrentBranch:function(){return Aw},getDirection:function(){return Mw},getHead:function(){return Cw}};var Pw,jw=n(2553),Yw=n.n(jw),zw={},Uw={nodeSpacing:150,nodeFillColor:"yellow",nodeStrokeWidth:2,nodeStrokeColor:"grey",lineStrokeWidth:4,branchOffset:50,lineColor:"grey",leftMargin:50,branchColors:["#442f74","#983351","#609732","#AA9A39"],nodeRadius:10,nodeLabel:{width:75,height:100,x:-25,y:0}},qw={};function Hw(t,e,n,r){var i=Av(r,Vu),a=Uw.branchColors[n%Uw.branchColors.length],o=zu().x((function(t){return Math.round(t.x)})).y((function(t){return Math.round(t.y)})).curve(i);t.append("svg:path").attr("d",o(e)).style("stroke",a).style("stroke-width",Uw.lineStrokeWidth).style("fill","none")}function $w(t,e){e=e||t.node().getBBox();var n=t.node().getCTM();return{left:n.e+e.x*n.a,top:n.f+e.y*n.d,width:e.width,height:e.height}}function Ww(t,e,n,r,i){o.debug("svgDrawLineForCommits: ",e,n);var a=$w(t.select("#node-"+e+" circle")),s=$w(t.select("#node-"+n+" circle"));switch(r){case"LR":if(a.left-s.left>Uw.nodeSpacing){var c={x:a.left-Uw.nodeSpacing,y:s.top+s.height/2};Hw(t,[c,{x:s.left+s.width,y:s.top+s.height/2}],i,"linear"),Hw(t,[{x:a.left,y:a.top+a.height/2},{x:a.left-Uw.nodeSpacing/2,y:a.top+a.height/2},{x:a.left-Uw.nodeSpacing/2,y:c.y},c],i)}else Hw(t,[{x:a.left,y:a.top+a.height/2},{x:a.left-Uw.nodeSpacing/2,y:a.top+a.height/2},{x:a.left-Uw.nodeSpacing/2,y:s.top+s.height/2},{x:s.left+s.width,y:s.top+s.height/2}],i);break;case"BT":if(s.top-a.top>Uw.nodeSpacing){var u={x:s.left+s.width/2,y:a.top+a.height+Uw.nodeSpacing};Hw(t,[u,{x:s.left+s.width/2,y:s.top}],i,"linear"),Hw(t,[{x:a.left+a.width/2,y:a.top+a.height},{x:a.left+a.width/2,y:a.top+a.height+Uw.nodeSpacing/2},{x:s.left+s.width/2,y:u.y-Uw.nodeSpacing/2},u],i)}else Hw(t,[{x:a.left+a.width/2,y:a.top+a.height},{x:a.left+a.width/2,y:a.top+Uw.nodeSpacing/2},{x:s.left+s.width/2,y:s.top-Uw.nodeSpacing/2},{x:s.left+s.width/2,y:s.top}],i)}}function Vw(t,e){return t.select(e).node().cloneNode(!0)}function Gw(t,e,n,r){var i,a=Object.keys(zw).length;if("string"==typeof e){var s=0;do{if(s++,i=zw[e],o.debug("in renderCommitHistory",i.id,i.seq),t.select("#node-"+e).size()>0)return;t.append((function(){return Vw(t,"#def-commit")})).attr("class","commit").attr("id",(function(){return"node-"+i.id})).attr("transform",(function(){switch(r){case"LR":return"translate("+(i.seq*Uw.nodeSpacing+Uw.leftMargin)+", "+Pw*Uw.branchOffset+")";case"BT":return"translate("+(Pw*Uw.branchOffset+Uw.leftMargin)+", "+(a-i.seq)*Uw.nodeSpacing+")"}})).attr("fill",Uw.nodeFillColor).attr("stroke",Uw.nodeStrokeColor).attr("stroke-width",Uw.nodeStrokeWidth);var c=void 0;for(var u in n)if(n[u].commit===i){c=n[u];break}c&&(o.debug("found branch ",c.name),t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","branch-label").text(c.name+", ")),t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","commit-id").text(i.id),""!==i.message&&"BT"===r&&t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","commit-msg").text(", "+i.message),e=i.parent}while(e&&zw[e]&&s<1e3)}Array.isArray(e)&&(o.debug("found merge commmit",e),Gw(t,e[0],n,r),Pw++,Gw(t,e[1],n,r),Pw--)}function Xw(t,e,n,r){r=r||0;for(var i=0;e.seq>0&&!e.lineDrawn&&i<1e3;)i++,"string"==typeof e.parent?(Ww(t,e.id,e.parent,n,r),e.lineDrawn=!0,e=zw[e.parent]):Array.isArray(e.parent)&&(Ww(t,e.id,e.parent[0],n,r),Ww(t,e.id,e.parent[1],n,r+1),Xw(t,zw[e.parent[1]],n,r+1),e.lineDrawn=!0,e=zw[e.parent[0]])}const Zw=function(t){qw=t};var Qw="",Kw=!1;const Jw={setMessage:function(t){o.debug("Setting message to: "+t),Qw=t},getMessage:function(){return Qw},setInfo:function(t){Kw=t},getInfo:function(){return Kw}};var tk=n(6765),ek=n.n(tk),nk={};const rk=function(t){Object.keys(t).forEach((function(e){nk[e]=t[e]}))};var ik=n(7062),ak=n.n(ik),ok={},sk="",ck=!1;const uk={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().pie},addSection:function(t,e){void 0===ok[t]&&(ok[t]=e,o.debug("Added new section :",t))},getSections:function(){return ok},cleanupValue:function(t){return":"===t.substring(0,1)?(t=t.substring(1).trim(),Number(t.trim())):Number(t.trim())},clear:function(){ok={},sk="",ck=!1},setTitle:function(t){sk=t},getTitle:function(){return sk},setShowData:function(t){ck=t},getShowData:function(){return ck}};var lk,hk=Jv();const fk=function(t,e){try{hk=Jv();var n=ak().parser;n.yy=uk,o.debug("Rendering info diagram\n"+t),n.yy.clear(),n.parse(t),o.debug("Parsed info diagram");var r=document.getElementById(e);void 0===(lk=r.parentElement.offsetWidth)&&(lk=1200),void 0!==hk.useWidth&&(lk=hk.useWidth),void 0!==hk.pie.useWidth&&(lk=hk.pie.useWidth);var i=au("#"+e);zv(i,450,lk,hk.pie.useMaxWidth),r.setAttribute("viewBox","0 0 "+lk+" 450");var a=Math.min(lk,450)/2-40,s=i.append("g").attr("transform","translate("+lk/2+",225)"),c=uk.getSections(),u=0;Object.keys(c).forEach((function(t){u+=c[t]}));var l=hk.themeVariables,h=[l.pie1,l.pie2,l.pie3,l.pie4,l.pie5,l.pie6,l.pie7,l.pie8,l.pie9,l.pie10,l.pie11,l.pie12],f=ma().range(h),d=function(){var t=qu,e=Uu,n=null,r=pu(0),i=pu(Eu),a=pu(0);function o(o){var s,c,u,l,h,f=(o=Ru(o)).length,d=0,p=new Array(f),y=new Array(f),g=+r.apply(this,arguments),m=Math.min(Eu,Math.max(-Eu,i.apply(this,arguments)-g)),v=Math.min(Math.abs(m)/f,a.apply(this,arguments)),b=v*(m<0?-1:1);for(s=0;s<f;++s)(h=y[p[s]=s]=+t(o[s],s,o))>0&&(d+=h);for(null!=e?p.sort((function(t,n){return e(y[t],y[n])})):null!=n&&p.sort((function(t,e){return n(o[t],o[e])})),s=0,u=d?(m-f*b)/d:0;s<f;++s,g=l)c=p[s],l=g+((h=y[c])>0?h*u:0)+b,y[c]={data:o[c],index:s,value:h,startAngle:g,endAngle:l,padAngle:v};return y}return o.value=function(e){return arguments.length?(t="function"==typeof e?e:pu(+e),o):t},o.sortValues=function(t){return arguments.length?(e=t,n=null,o):e},o.sort=function(t){return arguments.length?(n=t,e=null,o):n},o.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:pu(+t),o):r},o.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:pu(+t),o):i},o.padAngle=function(t){return arguments.length?(a="function"==typeof t?t:pu(+t),o):a},o}().value((function(t){return t[1]})),p=d(Object.entries(c)),y=Iu().innerRadius(0).outerRadius(a);s.selectAll("mySlices").data(p).enter().append("path").attr("d",y).attr("fill",(function(t){return f(t.data[0])})).attr("class","pieCircle"),s.selectAll("mySlices").data(p).enter().append("text").text((function(t){return(t.data[1]/u*100).toFixed(0)+"%"})).attr("transform",(function(t){return"translate("+y.centroid(t)+")"})).style("text-anchor","middle").attr("class","slice"),s.append("text").text(n.yy.getTitle()).attr("x",0).attr("y",-200).attr("class","pieTitleText");var g=s.selectAll(".legend").data(f.domain()).enter().append("g").attr("class","legend").attr("transform",(function(t,e){return"translate(216,"+(22*e-22*f.domain().length/2)+")"}));g.append("rect").attr("width",18).attr("height",18).style("fill",f).style("stroke",f),g.data(p).append("text").attr("x",22).attr("y",14).text((function(t){return n.yy.getShowData()||hk.showData||hk.pie.showData?t.data[0]+" ["+t.data[1]+"]":t.data[0]}))}catch(t){o.error("Error while rendering info diagram"),o.error(t)}};var dk=n(3176),pk=n.n(dk),yk=[],gk={},mk={},vk={},bk={};const _k={RequirementType:{REQUIREMENT:"Requirement",FUNCTIONAL_REQUIREMENT:"Functional Requirement",INTERFACE_REQUIREMENT:"Interface Requirement",PERFORMANCE_REQUIREMENT:"Performance Requirement",PHYSICAL_REQUIREMENT:"Physical Requirement",DESIGN_CONSTRAINT:"Design Constraint"},RiskLevel:{LOW_RISK:"Low",MED_RISK:"Medium",HIGH_RISK:"High"},VerifyType:{VERIFY_ANALYSIS:"Analysis",VERIFY_DEMONSTRATION:"Demonstration",VERIFY_INSPECTION:"Inspection",VERIFY_TEST:"Test"},Relationships:{CONTAINS:"contains",COPIES:"copies",DERIVES:"derives",SATISFIES:"satisfies",VERIFIES:"verifies",REFINES:"refines",TRACES:"traces"},parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().req},addRequirement:function(t,e){return void 0===mk[t]&&(mk[t]={name:t,type:e,id:gk.id,text:gk.text,risk:gk.risk,verifyMethod:gk.verifyMethod}),gk={},mk[t]},getRequirements:function(){return mk},setNewReqId:function(t){void 0!==gk&&(gk.id=t)},setNewReqText:function(t){void 0!==gk&&(gk.text=t)},setNewReqRisk:function(t){void 0!==gk&&(gk.risk=t)},setNewReqVerifyMethod:function(t){void 0!==gk&&(gk.verifyMethod=t)},addElement:function(t){return void 0===bk[t]&&(bk[t]={name:t,type:vk.type,docRef:vk.docRef},o.info("Added new requirement: ",t)),vk={},bk[t]},getElements:function(){return bk},setNewElementType:function(t){void 0!==vk&&(vk.type=t)},setNewElementDocRef:function(t){void 0!==vk&&(vk.docRef=t)},addRelationship:function(t,e,n){yk.push({type:t,src:e,dst:n})},getRelationships:function(){return yk},clear:function(){yk=[],gk={},mk={},vk={},bk={}}};var xk={CONTAINS:"contains",ARROW:"arrow"};const wk=xk;var kk={},Tk=0,Ek=function(t,e){return t.insert("rect","#"+e).attr("class","req reqBox").attr("x",0).attr("y",0).attr("width",kk.rect_min_width+"px").attr("height",kk.rect_min_height+"px")},Ck=function(t,e,n){var r=kk.rect_min_width/2,i=t.append("text").attr("class","req reqLabel reqTitle").attr("id",e).attr("x",r).attr("y",kk.rect_padding).attr("dominant-baseline","hanging"),a=0;n.forEach((function(t){0==a?i.append("tspan").attr("text-anchor","middle").attr("x",kk.rect_min_width/2).attr("dy",0).text(t):i.append("tspan").attr("text-anchor","middle").attr("x",kk.rect_min_width/2).attr("dy",.75*kk.line_height).text(t),a++}));var o=1.5*kk.rect_padding+a*kk.line_height*.75;return t.append("line").attr("class","req-title-line").attr("x1","0").attr("x2",kk.rect_min_width).attr("y1",o).attr("y2",o),{titleNode:i,y:o}},Sk=function(t,e,n,r){var i=t.append("text").attr("class","req reqLabel").attr("id",e).attr("x",kk.rect_padding).attr("y",r).attr("dominant-baseline","hanging"),a=0,o=[];return n.forEach((function(t){for(var e=t.length;e>30&&a<3;){var n=t.substring(0,30);e=(t=t.substring(30,t.length)).length,o[o.length]=n,a++}if(3==a){var r=o[o.length-1];o[o.length-1]=r.substring(0,r.length-4)+"..."}else o[o.length]=t;a=0})),o.forEach((function(t){i.append("tspan").attr("x",kk.rect_padding).attr("dy",kk.line_height).text(t)})),i},Ak=function(t){return t.replace(/\s/g,"").replace(/\./g,"_")};const Mk=function(t){if(void 0!==t)for(var e=Object.keys(t),n=0;n<e.length;n++)kk[e[n]]=t[e[n]]},Nk=function(t,e){dk.parser.yy=_k,dk.parser.yy.clear(),dk.parser.parse(t);var n=au("[id='".concat(e,"']"));!function(t,e){var n=t.append("defs").append("marker").attr("id",xk.CONTAINS+"_line_ending").attr("refX",0).attr("refY",e.line_height/2).attr("markerWidth",e.line_height).attr("markerHeight",e.line_height).attr("orient","auto").append("g");n.append("circle").attr("cx",e.line_height/2).attr("cy",e.line_height/2).attr("r",e.line_height/2).attr("fill","none"),n.append("line").attr("x1",0).attr("x2",e.line_height).attr("y1",e.line_height/2).attr("y2",e.line_height/2).attr("stroke-width",1),n.append("line").attr("y1",0).attr("y2",e.line_height).attr("x1",e.line_height/2).attr("x2",e.line_height/2).attr("stroke-width",1),t.append("defs").append("marker").attr("id",xk.ARROW+"_line_ending").attr("refX",e.line_height).attr("refY",.5*e.line_height).attr("markerWidth",e.line_height).attr("markerHeight",e.line_height).attr("orient","auto").append("path").attr("d","M0,0\n L".concat(e.line_height,",").concat(e.line_height/2,"\n M").concat(e.line_height,",").concat(e.line_height/2,"\n L0,").concat(e.line_height)).attr("stroke-width",1)}(n,kk);var r=new(kb().Graph)({multigraph:!1,compound:!1,directed:!0}).setGraph({rankdir:kk.layoutDirection,marginx:20,marginy:20,nodesep:100,edgesep:100,ranksep:100}).setDefaultEdgeLabel((function(){return{}})),i=_k.getRequirements(),a=_k.getElements(),s=_k.getRelationships();!function(t,e,n){Object.keys(t).forEach((function(r){var i=t[r];r=Ak(r),o.info("Added new requirement: ",r);var a=n.append("g").attr("id",r),s=Ek(a,"req-"+r),c=[],u=Ck(a,r+"_title",["<<".concat(i.type,">>"),"".concat(i.name)]);c.push(u.titleNode);var l=Sk(a,r+"_body",["Id: ".concat(i.id),"Text: ".concat(i.text),"Risk: ".concat(i.risk),"Verification: ".concat(i.verifyMethod)],u.y);c.push(l);var h=s.node().getBBox();e.setNode(r,{width:h.width,height:h.height,shape:"rect",id:r})}))}(i,r,n),function(t,e,n){Object.keys(t).forEach((function(r){var i=t[r],a=Ak(r),o=n.append("g").attr("id",a),s="element-"+a,c=Ek(o,s),u=[],l=Ck(o,s+"_title",["<<Element>>","".concat(r)]);u.push(l.titleNode);var h=Sk(o,s+"_body",["Type: ".concat(i.type||"Not Specified"),"Doc Ref: ".concat(i.docRef||"None")],l.y);u.push(h);var f=c.node().getBBox();e.setNode(a,{width:f.width,height:f.height,shape:"rect",id:a})}))}(a,r,n),function(t,e){t.forEach((function(t){var n=Ak(t.src),r=Ak(t.dst);e.setEdge(n,r,{relationship:t})}))}(s,r),xb().layout(r),function(t,e){e.nodes().forEach((function(n){void 0!==n&&void 0!==e.node(n)&&(t.select("#"+n),t.select("#"+n).attr("transform","translate("+(e.node(n).x-e.node(n).width/2)+","+(e.node(n).y-e.node(n).height/2)+" )"))}))}(n,r),s.forEach((function(t){!function(t,e,n,r){var i=n.edge(Ak(e.src),Ak(e.dst)),a=zu().x((function(t){return t.x})).y((function(t){return t.y})),o=t.insert("path","#"+r).attr("class","er relationshipLine").attr("d",a(i.points)).attr("fill","none");e.type==_k.Relationships.CONTAINS?o.attr("marker-start","url("+Um.getUrl(kk.arrowMarkerAbsolute)+"#"+e.type+"_line_ending)"):(o.attr("stroke-dasharray","10,7"),o.attr("marker-end","url("+Um.getUrl(kk.arrowMarkerAbsolute)+"#"+wk.ARROW+"_line_ending)")),function(t,e,n,r){var i=e.node().getTotalLength(),a=e.node().getPointAtLength(.5*i),o="rel"+Tk;Tk++;var s=t.append("text").attr("class","req relationshipLabel").attr("id",o).attr("x",a.x).attr("y",a.y).attr("text-anchor","middle").attr("dominant-baseline","middle").text(r).node().getBBox();t.insert("rect","#"+o).attr("class","req reqLabelBox").attr("x",a.x-s.width/2).attr("y",a.y-s.height/2).attr("width",s.width).attr("height",s.height).attr("fill","white").attr("fill-opacity","85%")}(t,o,0,"<<".concat(e.type,">>"))}(n,t,r,e)}));var c=kk.rect_padding,u=n.node().getBBox(),l=u.width+2*c,h=u.height+2*c;zv(n,h,l,kk.useMaxWidth),n.attr("viewBox","".concat(u.x-c," ").concat(u.y-c," ").concat(l," ").concat(h))};var Dk=n(6876),Ok=n.n(Dk),Bk=void 0,Lk={},Ik=[],Rk=[],Fk="",Pk=!1,jk=!1,Yk=!1,zk=function(t,e,n,r){var i=Lk[t];i&&e===i.name&&null==n||(null!=n&&null!=n.text||(n={text:e,wrap:null,type:r}),null!=r&&null!=n.text||(n={text:e,wrap:null,type:r}),Lk[t]={name:e,description:n.text,wrap:void 0===n.wrap&&$k()||!!n.wrap,prevActor:Bk,links:{},properties:{},actorCnt:null,rectData:null,type:r||"participant"},Bk&&Lk[Bk]&&(Lk[Bk].nextActor=t),Bk=t)},Uk=function(t){var e,n=0;for(e=0;e<Ik.length;e++)Ik[e].type===Wk.ACTIVE_START&&Ik[e].from.actor===t&&n++,Ik[e].type===Wk.ACTIVE_END&&Ik[e].from.actor===t&&n--;return n},qk=function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{text:void 0,wrap:void 0},r=arguments.length>3?arguments[3]:void 0;if(r===Wk.ACTIVE_END){var i=Uk(t.actor);if(i<1){var a=new Error("Trying to inactivate an inactive participant ("+t.actor+")");throw a.hash={text:"->>-",token:"->>-",line:"1",loc:{first_line:1,last_line:1,first_column:1,last_column:1},expected:["'ACTIVE_PARTICIPANT'"]},a}}return Ik.push({from:t,to:e,message:n.text,wrap:void 0===n.wrap&&$k()||!!n.wrap,type:r}),!0},Hk=function(t){return Lk[t]},$k=function(){return Yk},Wk={SOLID:0,DOTTED:1,NOTE:2,SOLID_CROSS:3,DOTTED_CROSS:4,SOLID_OPEN:5,DOTTED_OPEN:6,LOOP_START:10,LOOP_END:11,ALT_START:12,ALT_ELSE:13,ALT_END:14,OPT_START:15,OPT_END:16,ACTIVE_START:17,ACTIVE_END:18,PAR_START:19,PAR_AND:20,PAR_END:21,RECT_START:22,RECT_END:23,SOLID_POINT:24,DOTTED_POINT:25},Vk=function(t,e,n){var r={actor:t,placement:e,message:n.text,wrap:void 0===n.wrap&&$k()||!!n.wrap},i=[].concat(t,t);Rk.push(r),Ik.push({from:i[0],to:i[1],message:n.text,wrap:void 0===n.wrap&&$k()||!!n.wrap,type:Wk.NOTE,placement:e})},Gk=function(t,e){var n=Hk(t);try{var r=Fm(e.text,Jv());r=(r=r.replace(/&/g,"&")).replace(/=/g,"="),Xk(n,JSON.parse(r))}catch(t){o.error("error while parsing actor link text",t)}};function Xk(t,e){if(null==t.links)t.links=e;else for(var n in e)t.links[n]=e[n]}var Zk=function(t,e){var n=Hk(t);try{var r=Fm(e.text,Jv());Qk(n,JSON.parse(r))}catch(t){o.error("error while parsing actor properties text",t)}};function Qk(t,e){if(null==t.properties)t.properties=e;else for(var n in e)t.properties[n]=e[n]}var Kk=function(t,e){var n=Hk(t),r=document.getElementById(e.text);try{var i=r.innerHTML,a=JSON.parse(i);a.properties&&Qk(n,a.properties),a.links&&Xk(n,a.links)}catch(t){o.error("error while parsing actor details text",t)}},Jk=function(t){Fk=t.text,Pk=void 0===t.wrap&&$k()||!!t.wrap};const tT={addActor:zk,addMessage:function(t,e,n,r){Ik.push({from:t,to:e,message:n.text,wrap:void 0===n.wrap&&$k()||!!n.wrap,answer:r})},addSignal:qk,addLinks:Gk,addDetails:Kk,addProperties:Zk,autoWrap:$k,setWrap:function(t){Yk=t},enableSequenceNumbers:function(){jk=!0},showSequenceNumbers:function(){return jk},getMessages:function(){return Ik},getActors:function(){return Lk},getActor:Hk,getActorKeys:function(){return Object.keys(Lk)},getActorProperty:function(t,e){if(void 0!==t&&void 0!==t.properties)return t.properties[e]},getTitle:function(){return Fk},parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().sequence},getTitleWrapped:function(){return Pk},clear:function(){Lk={},Ik=[]},parseMessage:function(t){var e=t.trim(),n={text:e.replace(/^[:]?(?:no)?wrap:/,"").trim(),wrap:null!==e.match(/^[:]?wrap:/)||null===e.match(/^[:]?nowrap:/)&&void 0};return o.debug("parseMessage:",n),n},LINETYPE:Wk,ARROWTYPE:{FILLED:0,OPEN:1},PLACEMENT:{LEFTOF:0,RIGHTOF:1,OVER:2},addNote:Vk,setTitle:Jk,apply:function t(e){if(e instanceof Array)e.forEach((function(e){t(e)}));else switch(e.type){case"addParticipant":zk(e.actor,e.actor,e.description,"participant");break;case"addActor":zk(e.actor,e.actor,e.description,"actor");break;case"activeStart":case"activeEnd":qk(e.actor,void 0,void 0,e.signalType);break;case"addNote":Vk(e.actor,e.placement,e.text);break;case"addLinks":Gk(e.actor,e.text);break;case"addALink":!function(t,e){var n=Hk(t);try{var r={},i=Fm(e.text,Jv()),a=i.indexOf("@"),s=(i=(i=i.replace(/&/g,"&")).replace(/=/g,"=")).slice(0,a-1).trim(),c=i.slice(a+1).trim();r[s]=c,Xk(n,r)}catch(t){o.error("error while parsing actor link text",t)}}(e.actor,e.text);break;case"addProperties":Zk(e.actor,e.text);break;case"addDetails":Kk(e.actor,e.text);break;case"addMessage":qk(e.from,e.to,e.msg,e.signalType);break;case"loopStart":qk(void 0,void 0,e.loopText,e.signalType);break;case"loopEnd":case"rectEnd":case"optEnd":case"altEnd":case"parEnd":qk(void 0,void 0,void 0,e.signalType);break;case"rectStart":qk(void 0,void 0,e.color,e.signalType);break;case"optStart":qk(void 0,void 0,e.optText,e.signalType);break;case"altStart":case"else":qk(void 0,void 0,e.altText,e.signalType);break;case"setTitle":Jk(e.text);break;case"parStart":case"and":qk(void 0,void 0,e.parText,e.signalType)}}};var eT=[],nT=function(t,e){var n=t.append("rect");return n.attr("x",e.x),n.attr("y",e.y),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("width",e.width),n.attr("height",e.height),n.attr("rx",e.rx),n.attr("ry",e.ry),void 0!==e.class&&n.attr("class",e.class),n},rT=function(t,e){var n;n=function(){var n=document.querySelectorAll(t);n[0].addEventListener("mouseover",(function(){oT("actor"+e+"_popup")})),n[0].addEventListener("mouseout",(function(){sT("actor"+e+"_popup")}))},eT.push(n)},iT=function(t,e,n,r){var i=t.append("image");i.attr("x",e),i.attr("y",n);var a=(0,Om.sanitizeUrl)(r);i.attr("xlink:href",a)},aT=function(t,e,n,r){var i=t.append("use");i.attr("x",e),i.attr("y",n);var a=(0,Om.sanitizeUrl)(r);i.attr("xlink:href","#"+a)},oT=function(t){var e=document.getElementById(t);null!=e&&(e.style.display="block")},sT=function(t){var e=document.getElementById(t);null!=e&&(e.style.display="none")},cT=function(t,e){var n=0,r=0,i=e.text.split(Um.lineBreakRegex),a=[],o=0,s=function(){return e.y};if(void 0!==e.valign&&void 0!==e.textMargin&&e.textMargin>0)switch(e.valign){case"top":case"start":s=function(){return Math.round(e.y+e.textMargin)};break;case"middle":case"center":s=function(){return Math.round(e.y+(n+r+e.textMargin)/2)};break;case"bottom":case"end":s=function(){return Math.round(e.y+(n+r+2*e.textMargin)-e.textMargin)}}if(void 0!==e.anchor&&void 0!==e.textMargin&&void 0!==e.width)switch(e.anchor){case"left":case"start":e.x=Math.round(e.x+e.textMargin),e.anchor="start",e.dominantBaseline="text-after-edge",e.alignmentBaseline="middle";break;case"middle":case"center":e.x=Math.round(e.x+e.width/2),e.anchor="middle",e.dominantBaseline="middle",e.alignmentBaseline="middle";break;case"right":case"end":e.x=Math.round(e.x+e.width-e.textMargin),e.anchor="end",e.dominantBaseline="text-before-edge",e.alignmentBaseline="middle"}for(var c=0;c<i.length;c++){var u=i[c];void 0!==e.textMargin&&0===e.textMargin&&void 0!==e.fontSize&&(o=c*e.fontSize);var l=t.append("text");if(l.attr("x",e.x),l.attr("y",s()),void 0!==e.anchor&&l.attr("text-anchor",e.anchor).attr("dominant-baseline",e.dominantBaseline).attr("alignment-baseline",e.alignmentBaseline),void 0!==e.fontFamily&&l.style("font-family",e.fontFamily),void 0!==e.fontSize&&l.style("font-size",e.fontSize),void 0!==e.fontWeight&&l.style("font-weight",e.fontWeight),void 0!==e.fill&&l.attr("fill",e.fill),void 0!==e.class&&l.attr("class",e.class),void 0!==e.dy?l.attr("dy",e.dy):0!==o&&l.attr("dy",o),e.tspan){var h=l.append("tspan");h.attr("x",e.x),void 0!==e.fill&&h.attr("fill",e.fill),h.text(u)}else l.text(u);void 0!==e.valign&&void 0!==e.textMargin&&e.textMargin>0&&(r+=(l._groups||l)[0][0].getBBox().height,n=r),a.push(l)}return a},uT=function(t,e){var n=t.append("polygon");return n.attr("points",function(t,e,n,r,i){return t+","+e+" "+(t+n)+","+e+" "+(t+n)+","+(e+r-7)+" "+(t+n-8.4)+","+(e+r)+" "+t+","+(e+r)}(e.x,e.y,e.width,e.height)),n.attr("class","labelBox"),e.y=e.y+e.height/2,cT(t,e),n},lT=-1,hT=function(t,e){t.selectAll&&t.selectAll(".actor-line").attr("class","200").attr("y2",e-55)},fT=function(){return{x:0,y:0,fill:void 0,anchor:void 0,style:"#666",width:void 0,height:void 0,textMargin:0,rx:0,ry:0,tspan:!0,valign:void 0}},dT=function(){return{x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0}},pT=function(){function t(t,e,n,i,a,o,s){r(e.append("text").attr("x",n+a/2).attr("y",i+o/2+5).style("text-anchor","middle").text(t),s)}function e(t,e,n,i,a,o,s,c){for(var u=c.actorFontSize,l=c.actorFontFamily,h=c.actorFontWeight,f=t.split(Um.lineBreakRegex),d=0;d<f.length;d++){var p=d*u-u*(f.length-1)/2,y=e.append("text").attr("x",n+a/2).attr("y",i).style("text-anchor","middle").style("font-size",u).style("font-weight",h).style("font-family",l);y.append("tspan").attr("x",n+a/2).attr("dy",p).text(f[d]),y.attr("y",i+o/2).attr("dominant-baseline","central").attr("alignment-baseline","central"),r(y,s)}}function n(t,n,i,a,o,s,c,u){var l=n.append("switch"),h=l.append("foreignObject").attr("x",i).attr("y",a).attr("width",o).attr("height",s).append("xhtml:div").style("display","table").style("height","100%").style("width","100%");h.append("div").style("display","table-cell").style("text-align","center").style("vertical-align","middle").text(t),e(t,l,i,a,o,s,c,u),r(h,c)}function r(t,e){for(var n in e)e.hasOwnProperty(n)&&t.attr(n,e[n])}return function(r){return"fo"===r.textPlacement?n:"old"===r.textPlacement?t:e}}(),yT=function(){function t(t,e,n,i,a,o,s){r(e.append("text").attr("x",n).attr("y",i).style("text-anchor","start").text(t),s)}function e(t,e,n,i,a,o,s,c){for(var u=c.actorFontSize,l=c.actorFontFamily,h=c.actorFontWeight,f=t.split(Um.lineBreakRegex),d=0;d<f.length;d++){var p=d*u-u*(f.length-1)/2,y=e.append("text").attr("x",n).attr("y",i).style("text-anchor","start").style("font-size",u).style("font-weight",h).style("font-family",l);y.append("tspan").attr("x",n).attr("dy",p).text(f[d]),y.attr("y",i+o/2).attr("dominant-baseline","central").attr("alignment-baseline","central"),r(y,s)}}function n(t,n,i,a,o,s,c,u){var l=n.append("switch"),h=l.append("foreignObject").attr("x",i).attr("y",a).attr("width",o).attr("height",s).append("xhtml:div").style("display","table").style("height","100%").style("width","100%");h.append("div").style("display","table-cell").style("text-align","center").style("vertical-align","middle").text(t),e(t,l,i,a,0,s,c,u),r(h,c)}function r(t,e){for(var n in e)e.hasOwnProperty(n)&&t.attr(n,e[n])}return function(r){return"fo"===r.textPlacement?n:"old"===r.textPlacement?t:e}}();const gT=nT,mT=function(t,e,n){switch(e.type){case"actor":return function(t,e,n){var r=e.x+e.width/2;0===e.y&&(lT++,t.append("line").attr("id","actor"+lT).attr("x1",r).attr("y1",80).attr("x2",r).attr("y2",2e3).attr("class","actor-line").attr("stroke-width","0.5px").attr("stroke","#999"));var i=t.append("g");i.attr("class","actor-man");var a={x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0};a.x=e.x,a.y=e.y,a.fill="#eaeaea",a.width=e.width,a.height=e.height,a.class="actor",a.rx=3,a.ry=3,i.append("line").attr("id","actor-man-torso"+lT).attr("x1",r).attr("y1",e.y+25).attr("x2",r).attr("y2",e.y+45),i.append("line").attr("id","actor-man-arms"+lT).attr("x1",r-18).attr("y1",e.y+33).attr("x2",r+18).attr("y2",e.y+33),i.append("line").attr("x1",r-18).attr("y1",e.y+60).attr("x2",r).attr("y2",e.y+45),i.append("line").attr("x1",r).attr("y1",e.y+45).attr("x2",r+16).attr("y2",e.y+60);var o=i.append("circle");o.attr("cx",e.x+e.width/2),o.attr("cy",e.y+10),o.attr("r",15),o.attr("width",e.width),o.attr("height",e.height);var s=i.node().getBBox();return e.height=s.height,pT(n)(e.description,i,a.x,a.y+35,a.width,a.height,{class:"actor"},n),e.height}(t,e,n);case"participant":return function(t,e,n){var r=e.x+e.width/2,i=t.append("g"),a=i;0===e.y&&(lT++,a.append("line").attr("id","actor"+lT).attr("x1",r).attr("y1",5).attr("x2",r).attr("y2",2e3).attr("class","actor-line").attr("stroke-width","0.5px").attr("stroke","#999"),a=i.append("g"),e.actorCnt=lT,null!=e.links&&(a.attr("id","root-"+lT),rT("#root-"+lT,lT)));var o={x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0},s="actor";null!=e.properties&&e.properties.class?s=e.properties.class:o.fill="#eaeaea",o.x=e.x,o.y=e.y,o.width=e.width,o.height=e.height,o.class=s,o.rx=3,o.ry=3;var c=nT(a,o);if(e.rectData=o,null!=e.properties&&e.properties.icon){var u=e.properties.icon.trim();"@"===u.charAt(0)?aT(a,o.x+o.width-20,o.y+10,u.substr(1)):iT(a,o.x+o.width-20,o.y+10,u)}pT(n)(e.description,a,o.x,o.y,o.width,o.height,{class:"actor"},n);var l=e.height;if(c.node){var h=c.node().getBBox();e.height=h.height,l=h.height}return l}(t,e,n)}},vT=function(t,e,n,r,i){if(void 0===e.links||null===e.links||0===Object.keys(e.links).length)return{height:0,width:0};var a=e.links,o=e.actorCnt,s=e.rectData,c="none";i&&(c="block !important");var u=t.append("g");u.attr("id","actor"+o+"_popup"),u.attr("class","actorPopupMenu"),u.attr("display",c),rT("#actor"+o+"_popup",o);var l="";void 0!==s.class&&(l=" "+s.class);var h=s.width>n?s.width:n,f=u.append("rect");if(f.attr("class","actorPopupMenuPanel"+l),f.attr("x",s.x),f.attr("y",s.height),f.attr("fill",s.fill),f.attr("stroke",s.stroke),f.attr("width",h),f.attr("height",s.height),f.attr("rx",s.rx),f.attr("ry",s.ry),null!=a){var d=20;for(var p in a){var y=u.append("a"),g=(0,Om.sanitizeUrl)(a[p]);y.attr("xlink:href",g),y.attr("target","_blank"),yT(r)(p,y,s.x+10,s.height+d,h,20,{class:"actor"},r),d+=30}}return f.attr("height",d),{height:s.height+d,width:h}},bT=function(t){return t.append("g")},_T=function(t,e,n,r,i){var a={x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0},o=e.anchored;a.x=e.startx,a.y=e.starty,a.class="activation"+i%3,a.width=e.stopx-e.startx,a.height=n-e.starty,nT(o,a)},xT=function(t,e,n,r){var i=r.boxMargin,a=r.boxTextMargin,o=r.labelBoxHeight,s=r.labelBoxWidth,c=r.messageFontFamily,u=r.messageFontSize,l=r.messageFontWeight,h=t.append("g"),f=function(t,e,n,r){return h.append("line").attr("x1",t).attr("y1",e).attr("x2",n).attr("y2",r).attr("class","loopLine")};f(e.startx,e.starty,e.stopx,e.starty),f(e.stopx,e.starty,e.stopx,e.stopy),f(e.startx,e.stopy,e.stopx,e.stopy),f(e.startx,e.starty,e.startx,e.stopy),void 0!==e.sections&&e.sections.forEach((function(t){f(e.startx,t.y,e.stopx,t.y).style("stroke-dasharray","3, 3")}));var d={x:0,y:0,fill:void 0,anchor:void 0,style:"#666",width:void 0,height:void 0,textMargin:0,rx:0,ry:0,tspan:!0,valign:void 0};d.text=n,d.x=e.startx,d.y=e.starty,d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.anchor="middle",d.valign="middle",d.tspan=!1,d.width=s||50,d.height=o||20,d.textMargin=a,d.class="labelText",uT(h,d),(d={x:0,y:0,fill:void 0,anchor:void 0,style:"#666",width:void 0,height:void 0,textMargin:0,rx:0,ry:0,tspan:!0,valign:void 0}).text=e.title,d.x=e.startx+s/2+(e.stopx-e.startx)/2,d.y=e.starty+i+a,d.anchor="middle",d.valign="middle",d.textMargin=a,d.class="loopText",d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.wrap=!0;var p=cT(h,d);return void 0!==e.sectionTitles&&e.sectionTitles.forEach((function(t,n){if(t.message){d.text=t.message,d.x=e.startx+(e.stopx-e.startx)/2,d.y=e.sections[n].y+i+a,d.class="loopText",d.anchor="middle",d.valign="middle",d.tspan=!1,d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.wrap=e.wrap,p=cT(h,d);var r=Math.round(p.map((function(t){return(t._groups||t)[0][0].getBBox().height})).reduce((function(t,e){return t+e})));e.sections[n].height+=r-(i+a)}})),e.height=Math.round(e.stopy-e.starty),h},wT=function(t,e){nT(t,{x:e.startx,y:e.starty,width:e.stopx-e.startx,height:e.stopy-e.starty,fill:e.fill,class:"rect"}).lower()},kT=function(t){t.append("defs").append("marker").attr("id","arrowhead").attr("refX",9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z")},TT=function(t){t.append("defs").append("marker").attr("id","filled-head").attr("refX",18).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},ET=function(t){t.append("defs").append("marker").attr("id","sequencenumber").attr("refX",15).attr("refY",15).attr("markerWidth",60).attr("markerHeight",40).attr("orient","auto").append("circle").attr("cx",15).attr("cy",15).attr("r",6)},CT=function(t){var e=t.append("defs").append("marker").attr("id","crosshead").attr("markerWidth",15).attr("markerHeight",8).attr("orient","auto").attr("refX",16).attr("refY",4);e.append("path").attr("fill","black").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 9,2 V 6 L16,4 Z"),e.append("path").attr("fill","none").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 0,1 L 6,7 M 6,1 L 0,7")},ST=function(t){t.append("defs").append("symbol").attr("id","database").attr("fill-rule","evenodd").attr("clip-rule","evenodd").append("path").attr("transform","scale(.5)").attr("d","M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z")},AT=function(t){t.append("defs").append("symbol").attr("id","computer").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z")},MT=function(t){t.append("defs").append("symbol").attr("id","clock").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z")},NT=fT,DT=dT;Om.sanitizeUrl;Dk.parser.yy=tT;var OT={},BT={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],activations:[],models:{getHeight:function(){return Math.max.apply(null,0===this.actors.length?[0]:this.actors.map((function(t){return t.height||0})))+(0===this.loops.length?0:this.loops.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))+(0===this.messages.length?0:this.messages.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))+(0===this.notes.length?0:this.notes.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))},clear:function(){this.actors=[],this.loops=[],this.messages=[],this.notes=[]},addActor:function(t){this.actors.push(t)},addLoop:function(t){this.loops.push(t)},addMessage:function(t){this.messages.push(t)},addNote:function(t){this.notes.push(t)},lastActor:function(){return this.actors[this.actors.length-1]},lastLoop:function(){return this.loops[this.loops.length-1]},lastMessage:function(){return this.messages[this.messages.length-1]},lastNote:function(){return this.notes[this.notes.length-1]},actors:[],loops:[],messages:[],notes:[]},init:function(){this.sequenceItems=[],this.activations=[],this.models.clear(),this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0,jT(Dk.parser.yy.getConfig())},updateVal:function(t,e,n,r){void 0===t[e]?t[e]=n:t[e]=r(n,t[e])},updateBounds:function(t,e,n,r){var i=this,a=0;function o(o){return function(s){a++;var c=i.sequenceItems.length-a+1;i.updateVal(s,"starty",e-c*OT.boxMargin,Math.min),i.updateVal(s,"stopy",r+c*OT.boxMargin,Math.max),i.updateVal(BT.data,"startx",t-c*OT.boxMargin,Math.min),i.updateVal(BT.data,"stopx",n+c*OT.boxMargin,Math.max),"activation"!==o&&(i.updateVal(s,"startx",t-c*OT.boxMargin,Math.min),i.updateVal(s,"stopx",n+c*OT.boxMargin,Math.max),i.updateVal(BT.data,"starty",e-c*OT.boxMargin,Math.min),i.updateVal(BT.data,"stopy",r+c*OT.boxMargin,Math.max))}}this.sequenceItems.forEach(o()),this.activations.forEach(o("activation"))},insert:function(t,e,n,r){var i=Math.min(t,n),a=Math.max(t,n),o=Math.min(e,r),s=Math.max(e,r);this.updateVal(BT.data,"startx",i,Math.min),this.updateVal(BT.data,"starty",o,Math.min),this.updateVal(BT.data,"stopx",a,Math.max),this.updateVal(BT.data,"stopy",s,Math.max),this.updateBounds(i,o,a,s)},newActivation:function(t,e,n){var r=n[t.from.actor],i=YT(t.from.actor).length||0,a=r.x+r.width/2+(i-1)*OT.activationWidth/2;this.activations.push({startx:a,starty:this.verticalPos+2,stopx:a+OT.activationWidth,stopy:void 0,actor:t.from.actor,anchored:bT(e)})},endActivation:function(t){var e=this.activations.map((function(t){return t.actor})).lastIndexOf(t.from.actor);return this.activations.splice(e,1)[0]},createLoop:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{message:void 0,wrap:!1,width:void 0},e=arguments.length>1?arguments[1]:void 0;return{startx:void 0,starty:this.verticalPos,stopx:void 0,stopy:void 0,title:t.message,wrap:t.wrap,width:t.width,height:0,fill:e}},newLoop:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{message:void 0,wrap:!1,width:void 0},e=arguments.length>1?arguments[1]:void 0;this.sequenceItems.push(this.createLoop(t,e))},endLoop:function(){return this.sequenceItems.pop()},addSectionToLoop:function(t){var e=this.sequenceItems.pop();e.sections=e.sections||[],e.sectionTitles=e.sectionTitles||[],e.sections.push({y:BT.getVerticalPos(),height:0}),e.sectionTitles.push(t),this.sequenceItems.push(e)},bumpVerticalPos:function(t){this.verticalPos=this.verticalPos+t,this.data.stopy=this.verticalPos},getVerticalPos:function(){return this.verticalPos},getBounds:function(){return{bounds:this.data,models:this.models}}},LT=function(t){return{fontFamily:t.messageFontFamily,fontSize:t.messageFontSize,fontWeight:t.messageFontWeight}},IT=function(t){return{fontFamily:t.noteFontFamily,fontSize:t.noteFontSize,fontWeight:t.noteFontWeight}},RT=function(t){return{fontFamily:t.actorFontFamily,fontSize:t.actorFontSize,fontWeight:t.actorFontWeight}},FT=function(t,e,n,r){for(var i=0,a=0,o=0,s=0;s<n.length;s++){var c=e[n[s]];c.width=c.width||OT.width,c.height=Math.max(c.height||OT.height,OT.height),c.margin=c.margin||OT.actorMargin,c.x=i+a,c.y=r;var u=mT(t,c,OT);o=Math.max(o,u),BT.insert(c.x,r,c.x+c.width,c.height),i+=c.width,a+=c.margin,BT.models.addActor(c)}BT.bumpVerticalPos(o)},PT=function(t,e,n){for(var r=0,i=0,a=0;a<n.length;a++){var o=e[n[a]],s=qT(o),c=vT(t,o,s,OT,OT.forceMenus);c.height>r&&(r=c.height),c.width+o.x>i&&(i=c.width+o.x)}return{maxHeight:r,maxWidth:i}},jT=function(t){Lv(OT,t),t.fontFamily&&(OT.actorFontFamily=OT.noteFontFamily=OT.messageFontFamily=t.fontFamily),t.fontSize&&(OT.actorFontSize=OT.noteFontSize=OT.messageFontSize=t.fontSize),t.fontWeight&&(OT.actorFontWeight=OT.noteFontWeight=OT.messageFontWeight=t.fontWeight)},YT=function(t){return BT.activations.filter((function(e){return e.actor===t}))},zT=function(t,e){var n=e[t],r=YT(t);return[r.reduce((function(t,e){return Math.min(t,e.startx)}),n.x+n.width/2),r.reduce((function(t,e){return Math.max(t,e.stopx)}),n.x+n.width/2)]};function UT(t,e,n,r,i){BT.bumpVerticalPos(n);var a=r;if(e.id&&e.message&&t[e.id]){var s=t[e.id].width,c=LT(OT);e.message=Hv.wrapLabel("[".concat(e.message,"]"),s-2*OT.wrapPadding,c),e.width=s,e.wrap=!0;var u=Hv.calculateTextDimensions(e.message,c),l=Math.max(u.height,OT.labelBoxHeight);a=r+l,o.debug("".concat(l," - ").concat(e.message))}i(e),BT.bumpVerticalPos(a)}var qT=function(t){var e=0,n=RT(OT);for(var r in t.links){var i=Hv.calculateTextDimensions(r,n).width+2*OT.wrapPadding+2*OT.boxMargin;e<i&&(e=i)}return e};const HT={bounds:BT,drawActors:FT,drawActorsPopup:PT,setConf:jT,draw:function(t,e){OT=Jv().sequence,Dk.parser.yy.clear(),Dk.parser.yy.setWrap(OT.wrap),Dk.parser.parse(t+"\n"),BT.init(),o.debug("C:".concat(JSON.stringify(OT,null,2)));var n=au('[id="'.concat(e,'"]')),r=Dk.parser.yy.getActors(),i=Dk.parser.yy.getActorKeys(),a=Dk.parser.yy.getMessages(),s=Dk.parser.yy.getTitle(),c=function(t,e){var n={};return e.forEach((function(e){if(t[e.to]&&t[e.from]){var r=t[e.to];if(e.placement===Dk.parser.yy.PLACEMENT.LEFTOF&&!r.prevActor)return;if(e.placement===Dk.parser.yy.PLACEMENT.RIGHTOF&&!r.nextActor)return;var i=void 0!==e.placement,a=!i,o=i?IT(OT):LT(OT),s=e.wrap?Hv.wrapLabel(e.message,OT.width-2*OT.wrapPadding,o):e.message,c=Hv.calculateTextDimensions(s,o).width+2*OT.wrapPadding;a&&e.from===r.nextActor?n[e.to]=Math.max(n[e.to]||0,c):a&&e.from===r.prevActor?n[e.from]=Math.max(n[e.from]||0,c):a&&e.from===e.to?(n[e.from]=Math.max(n[e.from]||0,c/2),n[e.to]=Math.max(n[e.to]||0,c/2)):e.placement===Dk.parser.yy.PLACEMENT.RIGHTOF?n[e.from]=Math.max(n[e.from]||0,c):e.placement===Dk.parser.yy.PLACEMENT.LEFTOF?n[r.prevActor]=Math.max(n[r.prevActor]||0,c):e.placement===Dk.parser.yy.PLACEMENT.OVER&&(r.prevActor&&(n[r.prevActor]=Math.max(n[r.prevActor]||0,c/2)),r.nextActor&&(n[e.from]=Math.max(n[e.from]||0,c/2)))}})),o.debug("maxMessageWidthPerActor:",n),n}(r,a);OT.height=function(t,e){var n=0;for(var r in Object.keys(t).forEach((function(e){var r=t[e];r.wrap&&(r.description=Hv.wrapLabel(r.description,OT.width-2*OT.wrapPadding,RT(OT)));var i=Hv.calculateTextDimensions(r.description,RT(OT));r.width=r.wrap?OT.width:Math.max(OT.width,i.width+2*OT.wrapPadding),r.height=r.wrap?Math.max(i.height,OT.height):OT.height,n=Math.max(n,r.height)})),e){var i=t[r];if(i){var a=t[i.nextActor];if(a){var o=e[r]+OT.actorMargin-i.width/2-a.width/2;i.margin=Math.max(o,OT.actorMargin)}}}return Math.max(n,OT.height)}(r,c),AT(n),ST(n),MT(n),FT(n,r,i,0);var u=function(t,e){var n,r,i,a={},s=[];return t.forEach((function(t){switch(t.id=Hv.random({length:10}),t.type){case Dk.parser.yy.LINETYPE.LOOP_START:case Dk.parser.yy.LINETYPE.ALT_START:case Dk.parser.yy.LINETYPE.OPT_START:case Dk.parser.yy.LINETYPE.PAR_START:s.push({id:t.id,msg:t.message,from:Number.MAX_SAFE_INTEGER,to:Number.MIN_SAFE_INTEGER,width:0});break;case Dk.parser.yy.LINETYPE.ALT_ELSE:case Dk.parser.yy.LINETYPE.PAR_AND:t.message&&(n=s.pop(),a[n.id]=n,a[t.id]=n,s.push(n));break;case Dk.parser.yy.LINETYPE.LOOP_END:case Dk.parser.yy.LINETYPE.ALT_END:case Dk.parser.yy.LINETYPE.OPT_END:case Dk.parser.yy.LINETYPE.PAR_END:n=s.pop(),a[n.id]=n;break;case Dk.parser.yy.LINETYPE.ACTIVE_START:var c=e[t.from?t.from.actor:t.to.actor],u=YT(t.from?t.from.actor:t.to.actor).length,l=c.x+c.width/2+(u-1)*OT.activationWidth/2,h={startx:l,stopx:l+OT.activationWidth,actor:t.from.actor,enabled:!0};BT.activations.push(h);break;case Dk.parser.yy.LINETYPE.ACTIVE_END:var f=BT.activations.map((function(t){return t.actor})).lastIndexOf(t.from.actor);delete BT.activations.splice(f,1)[0]}void 0!==t.placement?(r=function(t,e){var n=e[t.from].x,r=e[t.to].x,i=t.wrap&&t.message,a=Hv.calculateTextDimensions(i?Hv.wrapLabel(t.message,OT.width,IT(OT)):t.message,IT(OT)),s={width:i?OT.width:Math.max(OT.width,a.width+2*OT.noteMargin),height:0,startx:e[t.from].x,stopx:0,starty:0,stopy:0,message:t.message};return t.placement===Dk.parser.yy.PLACEMENT.RIGHTOF?(s.width=i?Math.max(OT.width,a.width):Math.max(e[t.from].width/2+e[t.to].width/2,a.width+2*OT.noteMargin),s.startx=n+(e[t.from].width+OT.actorMargin)/2):t.placement===Dk.parser.yy.PLACEMENT.LEFTOF?(s.width=i?Math.max(OT.width,a.width+2*OT.noteMargin):Math.max(e[t.from].width/2+e[t.to].width/2,a.width+2*OT.noteMargin),s.startx=n-s.width+(e[t.from].width-OT.actorMargin)/2):t.to===t.from?(a=Hv.calculateTextDimensions(i?Hv.wrapLabel(t.message,Math.max(OT.width,e[t.from].width),IT(OT)):t.message,IT(OT)),s.width=i?Math.max(OT.width,e[t.from].width):Math.max(e[t.from].width,OT.width,a.width+2*OT.noteMargin),s.startx=n+(e[t.from].width-s.width)/2):(s.width=Math.abs(n+e[t.from].width/2-(r+e[t.to].width/2))+OT.actorMargin,s.startx=n<r?n+e[t.from].width/2-OT.actorMargin/2:r+e[t.to].width/2-OT.actorMargin/2),i&&(s.message=Hv.wrapLabel(t.message,s.width-2*OT.wrapPadding,IT(OT))),o.debug("NM:[".concat(s.startx,",").concat(s.stopx,",").concat(s.starty,",").concat(s.stopy,":").concat(s.width,",").concat(s.height,"=").concat(t.message,"]")),s}(t,e),t.noteModel=r,s.forEach((function(t){(n=t).from=Math.min(n.from,r.startx),n.to=Math.max(n.to,r.startx+r.width),n.width=Math.max(n.width,Math.abs(n.from-n.to))-OT.labelBoxWidth}))):(i=function(t,e){var n=!1;if([Dk.parser.yy.LINETYPE.SOLID_OPEN,Dk.parser.yy.LINETYPE.DOTTED_OPEN,Dk.parser.yy.LINETYPE.SOLID,Dk.parser.yy.LINETYPE.DOTTED,Dk.parser.yy.LINETYPE.SOLID_CROSS,Dk.parser.yy.LINETYPE.DOTTED_CROSS,Dk.parser.yy.LINETYPE.SOLID_POINT,Dk.parser.yy.LINETYPE.DOTTED_POINT].includes(t.type)&&(n=!0),!n)return{};var r=zT(t.from,e),i=zT(t.to,e),a=r[0]<=i[0]?1:0,o=r[0]<i[0]?0:1,s=r.concat(i),c=Math.abs(i[o]-r[a]);t.wrap&&t.message&&(t.message=Hv.wrapLabel(t.message,Math.max(c+2*OT.wrapPadding,OT.width),LT(OT)));var u=Hv.calculateTextDimensions(t.message,LT(OT));return{width:Math.max(t.wrap?0:u.width+2*OT.wrapPadding,c+2*OT.wrapPadding,OT.width),height:0,startx:r[a],stopx:i[o],starty:0,stopy:0,message:t.message,type:t.type,wrap:t.wrap,fromBounds:Math.min.apply(null,s),toBounds:Math.max.apply(null,s)}}(t,e),t.msgModel=i,i.startx&&i.stopx&&s.length>0&&s.forEach((function(r){if(n=r,i.startx===i.stopx){var a=e[t.from],o=e[t.to];n.from=Math.min(a.x-i.width/2,a.x-a.width/2,n.from),n.to=Math.max(o.x+i.width/2,o.x+a.width/2,n.to),n.width=Math.max(n.width,Math.abs(n.to-n.from))-OT.labelBoxWidth}else n.from=Math.min(i.startx,n.from),n.to=Math.max(i.stopx,n.to),n.width=Math.max(n.width,i.width)-OT.labelBoxWidth})))})),BT.activations=[],o.debug("Loop type widths:",a),a}(a,r);kT(n),CT(n),TT(n),ET(n);var l=1;a.forEach((function(t){var e,i,a;switch(t.type){case Dk.parser.yy.LINETYPE.NOTE:i=t.noteModel,function(t,e){BT.bumpVerticalPos(OT.boxMargin),e.height=OT.boxMargin,e.starty=BT.getVerticalPos();var n=DT();n.x=e.startx,n.y=e.starty,n.width=e.width||OT.width,n.class="note";var r=t.append("g"),i=gT(r,n),a=NT();a.x=e.startx,a.y=e.starty,a.width=n.width,a.dy="1em",a.text=e.message,a.class="noteText",a.fontFamily=OT.noteFontFamily,a.fontSize=OT.noteFontSize,a.fontWeight=OT.noteFontWeight,a.anchor=OT.noteAlign,a.textMargin=OT.noteMargin,a.valign=OT.noteAlign;var o=cT(r,a),s=Math.round(o.map((function(t){return(t._groups||t)[0][0].getBBox().height})).reduce((function(t,e){return t+e})));i.attr("height",s+2*OT.noteMargin),e.height+=s+2*OT.noteMargin,BT.bumpVerticalPos(s+2*OT.noteMargin),e.stopy=e.starty+s+2*OT.noteMargin,e.stopx=e.startx+n.width,BT.insert(e.startx,e.starty,e.stopx,e.stopy),BT.models.addNote(e)}(n,i);break;case Dk.parser.yy.LINETYPE.ACTIVE_START:BT.newActivation(t,n,r);break;case Dk.parser.yy.LINETYPE.ACTIVE_END:!function(t,e){var r=BT.endActivation(t);r.starty+18>e&&(r.starty=e-6,e+=12),_T(n,r,e,OT,YT(t.from.actor).length),BT.insert(r.startx,e-10,r.stopx,e)}(t,BT.getVerticalPos());break;case Dk.parser.yy.LINETYPE.LOOP_START:UT(u,t,OT.boxMargin,OT.boxMargin+OT.boxTextMargin,(function(t){return BT.newLoop(t)}));break;case Dk.parser.yy.LINETYPE.LOOP_END:e=BT.endLoop(),xT(n,e,"loop",OT),BT.bumpVerticalPos(e.stopy-BT.getVerticalPos()),BT.models.addLoop(e);break;case Dk.parser.yy.LINETYPE.RECT_START:UT(u,t,OT.boxMargin,OT.boxMargin,(function(t){return BT.newLoop(void 0,t.message)}));break;case Dk.parser.yy.LINETYPE.RECT_END:e=BT.endLoop(),wT(n,e),BT.models.addLoop(e),BT.bumpVerticalPos(e.stopy-BT.getVerticalPos());break;case Dk.parser.yy.LINETYPE.OPT_START:UT(u,t,OT.boxMargin,OT.boxMargin+OT.boxTextMargin,(function(t){return BT.newLoop(t)}));break;case Dk.parser.yy.LINETYPE.OPT_END:e=BT.endLoop(),xT(n,e,"opt",OT),BT.bumpVerticalPos(e.stopy-BT.getVerticalPos()),BT.models.addLoop(e);break;case Dk.parser.yy.LINETYPE.ALT_START:UT(u,t,OT.boxMargin,OT.boxMargin+OT.boxTextMargin,(function(t){return BT.newLoop(t)}));break;case Dk.parser.yy.LINETYPE.ALT_ELSE:UT(u,t,OT.boxMargin+OT.boxTextMargin,OT.boxMargin,(function(t){return BT.addSectionToLoop(t)}));break;case Dk.parser.yy.LINETYPE.ALT_END:e=BT.endLoop(),xT(n,e,"alt",OT),BT.bumpVerticalPos(e.stopy-BT.getVerticalPos()),BT.models.addLoop(e);break;case Dk.parser.yy.LINETYPE.PAR_START:UT(u,t,OT.boxMargin,OT.boxMargin+OT.boxTextMargin,(function(t){return BT.newLoop(t)}));break;case Dk.parser.yy.LINETYPE.PAR_AND:UT(u,t,OT.boxMargin+OT.boxTextMargin,OT.boxMargin,(function(t){return BT.addSectionToLoop(t)}));break;case Dk.parser.yy.LINETYPE.PAR_END:e=BT.endLoop(),xT(n,e,"par",OT),BT.bumpVerticalPos(e.stopy-BT.getVerticalPos()),BT.models.addLoop(e);break;default:try{(a=t.msgModel).starty=BT.getVerticalPos(),a.sequenceIndex=l,function(t,e){BT.bumpVerticalPos(10);var n=e.startx,r=e.stopx,i=e.starty,a=e.message,o=e.type,s=e.sequenceIndex,c=Um.splitBreaks(a).length,u=Hv.calculateTextDimensions(a,LT(OT)),l=u.height/c;e.height+=l,BT.bumpVerticalPos(l);var h=NT();h.x=n,h.y=i+10,h.width=r-n,h.class="messageText",h.dy="1em",h.text=a,h.fontFamily=OT.messageFontFamily,h.fontSize=OT.messageFontSize,h.fontWeight=OT.messageFontWeight,h.anchor=OT.messageAlign,h.valign=OT.messageAlign,h.textMargin=OT.wrapPadding,h.tspan=!1,cT(t,h);var f,d,p=u.height-10,y=u.width;if(n===r){d=BT.getVerticalPos()+p,OT.rightAngles?f=t.append("path").attr("d","M ".concat(n,",").concat(d," H ").concat(n+Math.max(OT.width/2,y/2)," V ").concat(d+25," H ").concat(n)):(p+=OT.boxMargin,d=BT.getVerticalPos()+p,f=t.append("path").attr("d","M "+n+","+d+" C "+(n+60)+","+(d-10)+" "+(n+60)+","+(d+30)+" "+n+","+(d+20))),p+=30;var g=Math.max(y/2,OT.width/2);BT.insert(n-g,BT.getVerticalPos()-10+p,r+g,BT.getVerticalPos()+30+p)}else p+=OT.boxMargin,d=BT.getVerticalPos()+p,(f=t.append("line")).attr("x1",n),f.attr("y1",d),f.attr("x2",r),f.attr("y2",d),BT.insert(n,d-10,r,d);o===Dk.parser.yy.LINETYPE.DOTTED||o===Dk.parser.yy.LINETYPE.DOTTED_CROSS||o===Dk.parser.yy.LINETYPE.DOTTED_POINT||o===Dk.parser.yy.LINETYPE.DOTTED_OPEN?(f.style("stroke-dasharray","3, 3"),f.attr("class","messageLine1")):f.attr("class","messageLine0");var m="";OT.arrowMarkerAbsolute&&(m=(m=(m=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),f.attr("stroke-width",2),f.attr("stroke","none"),f.style("fill","none"),o!==Dk.parser.yy.LINETYPE.SOLID&&o!==Dk.parser.yy.LINETYPE.DOTTED||f.attr("marker-end","url("+m+"#arrowhead)"),o!==Dk.parser.yy.LINETYPE.SOLID_POINT&&o!==Dk.parser.yy.LINETYPE.DOTTED_POINT||f.attr("marker-end","url("+m+"#filled-head)"),o!==Dk.parser.yy.LINETYPE.SOLID_CROSS&&o!==Dk.parser.yy.LINETYPE.DOTTED_CROSS||f.attr("marker-end","url("+m+"#crosshead)"),(tT.showSequenceNumbers()||OT.showSequenceNumbers)&&(f.attr("marker-start","url("+m+"#sequencenumber)"),t.append("text").attr("x",n).attr("y",d+4).attr("font-family","sans-serif").attr("font-size","12px").attr("text-anchor","middle").attr("textLength","16px").attr("class","sequenceNumber").text(s)),BT.bumpVerticalPos(p),e.height+=p,e.stopy=e.starty+e.height,BT.insert(e.fromBounds,e.starty,e.toBounds,e.stopy)}(n,a),BT.models.addMessage(a)}catch(t){o.error("error while drawing message",t)}}[Dk.parser.yy.LINETYPE.SOLID_OPEN,Dk.parser.yy.LINETYPE.DOTTED_OPEN,Dk.parser.yy.LINETYPE.SOLID,Dk.parser.yy.LINETYPE.DOTTED,Dk.parser.yy.LINETYPE.SOLID_CROSS,Dk.parser.yy.LINETYPE.DOTTED_CROSS,Dk.parser.yy.LINETYPE.SOLID_POINT,Dk.parser.yy.LINETYPE.DOTTED_POINT].includes(t.type)&&l++})),OT.mirrorActors&&(BT.bumpVerticalPos(2*OT.boxMargin),FT(n,r,i,BT.getVerticalPos()),BT.bumpVerticalPos(OT.boxMargin),hT(n,BT.getVerticalPos()));var h=PT(n,r,i),f=BT.getBounds().bounds;o.debug("For line height fix Querying: #"+e+" .actor-line"),ou("#"+e+" .actor-line").attr("y2",f.stopy);var d=f.stopy-f.starty;d<h.maxHeight&&(d=h.maxHeight);var p=d+2*OT.diagramMarginY;OT.mirrorActors&&(p=p-OT.boxMargin+OT.bottomMarginAdj);var y=f.stopx-f.startx;y<h.maxWidth&&(y=h.maxWidth);var g=y+2*OT.diagramMarginX;s&&n.append("text").text(s).attr("x",(f.stopx-f.startx)/2-2*OT.diagramMarginX).attr("y",-25),zv(n,p,g,OT.useMaxWidth);var m=s?40:0;n.attr("viewBox",f.startx-OT.diagramMarginX+" -"+(OT.diagramMarginY+m)+" "+g+" "+(p+m)),o.debug("models:",BT.models)}};var $T=n(3584),WT=n.n($T);function VT(t){return VT="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},VT(t)}var GT=function(t){return JSON.parse(JSON.stringify(t))},XT=[],ZT=function t(e,n,r){if("relation"===n.stmt)t(e,n.state1,!0),t(e,n.state2,!1);else if("state"===n.stmt&&"[*]"===n.id&&(n.id=r?e.id+"_start":e.id+"_end",n.start=r),n.doc){var i=[],a=0,o=[];for(a=0;a<n.doc.length;a++)if("divider"===n.doc[a].type){var s=GT(n.doc[a]);s.doc=GT(o),i.push(s),o=[]}else o.push(n.doc[a]);if(i.length>0&&o.length>0){var c={stmt:"state",id:Ov(),type:"divider",doc:GT(o)};i.push(GT(c)),n.doc=i}n.doc.forEach((function(e){return t(n,e,!0)}))}},QT={root:{relations:[],states:{},documents:{}}},KT=QT.root,JT=0,tE=function(t,e,n,r,i){void 0===KT.states[t]?KT.states[t]={id:t,descriptions:[],type:e,doc:n,note:i}:(KT.states[t].doc||(KT.states[t].doc=n),KT.states[t].type||(KT.states[t].type=e)),r&&(o.info("Adding state ",t,r),"string"==typeof r&&rE(t,r.trim()),"object"===VT(r)&&r.forEach((function(e){return rE(t,e.trim())}))),i&&(KT.states[t].note=i,KT.states[t].note.text=Um.sanitizeText(KT.states[t].note.text,Jv()))},eE=function(){KT=(QT={root:{relations:[],states:{},documents:{}}}).root,KT=QT.root,JT=0,aE=[]},nE=function(t,e,n){var r=t,i=e,a="default",o="default";"[*]"===t&&(r="start"+ ++JT,a="start"),"[*]"===e&&(i="end"+JT,o="end"),tE(r,a),tE(i,o),KT.relations.push({id1:r,id2:i,title:Um.sanitizeText(n,Jv())})},rE=function(t,e){var n=KT.states[t],r=e;":"===r[0]&&(r=r.substr(1).trim()),n.descriptions.push(Um.sanitizeText(r,Jv()))},iE=0,aE=[],oE="TB";const sE={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().state},addState:tE,clear:eE,getState:function(t){return KT.states[t]},getStates:function(){return KT.states},getRelations:function(){return KT.relations},getClasses:function(){return aE},getDirection:function(){return oE},addRelation:nE,getDividerId:function(){return"divider-id-"+ ++iE},setDirection:function(t){oE=t},cleanupLabel:function(t){return":"===t.substring(0,1)?t.substr(2).trim():t.trim()},lineType:{LINE:0,DOTTED_LINE:1},relationType:{AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3},logDocuments:function(){o.info("Documents = ",QT)},getRootDoc:function(){return XT},setRootDoc:function(t){o.info("Setting root doc",t),XT=t},getRootDocV2:function(){return ZT({id:"root"},{id:"root",doc:XT},!0),{id:"root",doc:XT}},extract:function(t){var e;e=t.doc?t.doc:t,o.info(e),eE(),o.info("Extract",e),e.forEach((function(t){"state"===t.stmt&&tE(t.id,t.type,t.doc,t.description,t.note),"relation"===t.stmt&&nE(t.state1.id,t.state2.id,t.description)}))},trimColon:function(t){return t&&":"===t[0]?t.substr(1).trim():t.trim()}};var cE={};function uE(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var lE,hE=function(t,e,n){var r,i=Jv().state.padding,a=2*Jv().state.padding,o=t.node().getBBox(),s=o.width,c=o.x,u=t.append("text").attr("x",0).attr("y",Jv().state.titleShift).attr("font-size",Jv().state.fontSize).attr("class","state-title").text(e.id),l=u.node().getBBox().width+a,h=Math.max(l,s);h===s&&(h+=a);var f=t.node().getBBox();e.doc,r=c-i,l>s&&(r=(s-h)/2+i),Math.abs(c-f.x)<i&&l>s&&(r=c-(l-s)/2);var d=1-Jv().state.textHeight;return t.insert("rect",":first-child").attr("x",r).attr("y",d).attr("class",n?"alt-composit":"composit").attr("width",h).attr("height",f.height+Jv().state.textHeight+Jv().state.titleShift+1).attr("rx","0"),u.attr("x",r+i),l<=s&&u.attr("x",c+(h-a)/2-l/2+i),t.insert("rect",":first-child").attr("x",r).attr("y",Jv().state.titleShift-Jv().state.textHeight-Jv().state.padding).attr("width",h).attr("height",3*Jv().state.textHeight).attr("rx",Jv().state.radius),t.insert("rect",":first-child").attr("x",r).attr("y",Jv().state.titleShift-Jv().state.textHeight-Jv().state.padding).attr("width",h).attr("height",f.height+3+2*Jv().state.textHeight).attr("rx",Jv().state.radius),t},fE=function(t,e){e.attr("class","state-note");var n=e.append("rect").attr("x",0).attr("y",Jv().state.padding),r=function(t,e,n,r){var i=0,a=r.append("text");a.style("text-anchor","start"),a.attr("class","noteText");var o,s=t.replace(/\r\n/g,"<br/>"),c=(s=s.replace(/\n/g,"<br/>")).split(Um.lineBreakRegex),u=1.25*Jv().state.noteMargin,l=function(t,e){var n="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!n){if(Array.isArray(t)||(n=function(t,e){if(t){if("string"==typeof t)return uE(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?uE(t,e):void 0}}(t))||e&&t&&"number"==typeof t.length){n&&(t=n);var r=0,i=function(){};return{s:i,n:function(){return r>=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,o=!0,s=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return o=t.done,t},e:function(t){s=!0,a=t},f:function(){try{o||null==n.return||n.return()}finally{if(s)throw a}}}}(c);try{for(l.s();!(o=l.n()).done;){var h=o.value.trim();if(h.length>0){var f=a.append("tspan");f.text(h),0===u&&(u+=f.node().getBBox().height),i+=u,f.attr("x",0+Jv().state.noteMargin),f.attr("y",0+i+1.25*Jv().state.noteMargin)}}}catch(t){l.e(t)}finally{l.f()}return{textWidth:a.node().getBBox().width,textHeight:i}}(t,0,0,e.append("g")),i=r.textWidth,a=r.textHeight;return n.attr("height",a+2*Jv().state.noteMargin),n.attr("width",i+2*Jv().state.noteMargin),n},dE=function(t,e){var n=e.id,r={id:n,label:e.id,width:0,height:0},i=t.append("g").attr("id",n).attr("class","stateGroup");"start"===e.type&&function(t){t.append("circle").attr("class","start-state").attr("r",Jv().state.sizeUnit).attr("cx",Jv().state.padding+Jv().state.sizeUnit).attr("cy",Jv().state.padding+Jv().state.sizeUnit)}(i),"end"===e.type&&function(t){t.append("circle").attr("class","end-state-outer").attr("r",Jv().state.sizeUnit+Jv().state.miniPadding).attr("cx",Jv().state.padding+Jv().state.sizeUnit+Jv().state.miniPadding).attr("cy",Jv().state.padding+Jv().state.sizeUnit+Jv().state.miniPadding),t.append("circle").attr("class","end-state-inner").attr("r",Jv().state.sizeUnit).attr("cx",Jv().state.padding+Jv().state.sizeUnit+2).attr("cy",Jv().state.padding+Jv().state.sizeUnit+2)}(i),"fork"!==e.type&&"join"!==e.type||function(t,e){var n=Jv().state.forkWidth,r=Jv().state.forkHeight;if(e.parentId){var i=n;n=r,r=i}t.append("rect").style("stroke","black").style("fill","black").attr("width",n).attr("height",r).attr("x",Jv().state.padding).attr("y",Jv().state.padding)}(i,e),"note"===e.type&&fE(e.note.text,i),"divider"===e.type&&function(t){t.append("line").style("stroke","grey").style("stroke-dasharray","3").attr("x1",Jv().state.textHeight).attr("class","divider").attr("x2",2*Jv().state.textHeight).attr("y1",0).attr("y2",0)}(i),"default"===e.type&&0===e.descriptions.length&&function(t,e){var n=t.append("text").attr("x",2*Jv().state.padding).attr("y",Jv().state.textHeight+2*Jv().state.padding).attr("font-size",Jv().state.fontSize).attr("class","state-title").text(e.id).node().getBBox();t.insert("rect",":first-child").attr("x",Jv().state.padding).attr("y",Jv().state.padding).attr("width",n.width+2*Jv().state.padding).attr("height",n.height+2*Jv().state.padding).attr("rx",Jv().state.radius)}(i,e),"default"===e.type&&e.descriptions.length>0&&function(t,e){var n=t.append("text").attr("x",2*Jv().state.padding).attr("y",Jv().state.textHeight+1.3*Jv().state.padding).attr("font-size",Jv().state.fontSize).attr("class","state-title").text(e.descriptions[0]).node().getBBox(),r=n.height,i=t.append("text").attr("x",Jv().state.padding).attr("y",r+.4*Jv().state.padding+Jv().state.dividerMargin+Jv().state.textHeight).attr("class","state-description"),a=!0,o=!0;e.descriptions.forEach((function(t){a||(function(t,e,n){var r=t.append("tspan").attr("x",2*Jv().state.padding).text(e);n||r.attr("dy",Jv().state.textHeight)}(i,t,o),o=!1),a=!1}));var s=t.append("line").attr("x1",Jv().state.padding).attr("y1",Jv().state.padding+r+Jv().state.dividerMargin/2).attr("y2",Jv().state.padding+r+Jv().state.dividerMargin/2).attr("class","descr-divider"),c=i.node().getBBox(),u=Math.max(c.width,n.width);s.attr("x2",u+3*Jv().state.padding),t.insert("rect",":first-child").attr("x",Jv().state.padding).attr("y",Jv().state.padding).attr("width",u+2*Jv().state.padding).attr("height",c.height+r+2*Jv().state.padding).attr("rx",Jv().state.radius)}(i,e);var a,o=i.node().getBBox();return r.width=o.width+2*Jv().state.padding,r.height=o.height+2*Jv().state.padding,a=r,cE[n]=a,r},pE=0;$T.parser.yy=sE;var yE={},gE=function t(e,n,r,i){var a,s=new(kb().Graph)({compound:!0,multigraph:!0}),c=!0;for(a=0;a<e.length;a++)if("relation"===e[a].stmt){c=!1;break}r?s.setGraph({rankdir:"LR",multigraph:!0,compound:!0,ranker:"tight-tree",ranksep:c?1:lE.edgeLengthFactor,nodeSep:c?1:50,isMultiGraph:!0}):s.setGraph({rankdir:"TB",multigraph:!0,compound:!0,ranksep:c?1:lE.edgeLengthFactor,nodeSep:c?1:50,ranker:"tight-tree",isMultiGraph:!0}),s.setDefaultEdgeLabel((function(){return{}})),sE.extract(e);for(var u=sE.getStates(),l=sE.getRelations(),h=Object.keys(u),f=0;f<h.length;f++){var d=u[h[f]];r&&(d.parentId=r);var p=void 0;if(d.doc){var y=n.append("g").attr("id",d.id).attr("class","stateGroup");p=t(d.doc,y,d.id,!i);var g=(y=hE(y,d,i)).node().getBBox();p.width=g.width,p.height=g.height+lE.padding/2,yE[d.id]={y:lE.compositTitleSize}}else p=dE(n,d);if(d.note){var m={descriptions:[],id:d.id+"-note",note:d.note,type:"note"},v=dE(n,m);"left of"===d.note.position?(s.setNode(p.id+"-note",v),s.setNode(p.id,p)):(s.setNode(p.id,p),s.setNode(p.id+"-note",v)),s.setParent(p.id,p.id+"-group"),s.setParent(p.id+"-note",p.id+"-group")}else s.setNode(p.id,p)}o.debug("Count=",s.nodeCount(),s);var b=0;l.forEach((function(t){var e;b++,o.debug("Setting edge",t),s.setEdge(t.id1,t.id2,{relation:t,width:(e=t.title,e?e.length*lE.fontSizeFactor:1),height:lE.labelHeight*Um.getRows(t.title).length,labelpos:"c"},"id"+b)})),xb().layout(s),o.debug("Graph after layout",s.nodes());var _=n.node();s.nodes().forEach((function(t){void 0!==t&&void 0!==s.node(t)?(o.warn("Node "+t+": "+JSON.stringify(s.node(t))),au("#"+_.id+" #"+t).attr("transform","translate("+(s.node(t).x-s.node(t).width/2)+","+(s.node(t).y+(yE[t]?yE[t].y:0)-s.node(t).height/2)+" )"),au("#"+_.id+" #"+t).attr("data-x-shift",s.node(t).x-s.node(t).width/2),document.querySelectorAll("#"+_.id+" #"+t+" .divider").forEach((function(t){var e=t.parentElement,n=0,r=0;e&&(e.parentElement&&(n=e.parentElement.getBBox().width),r=parseInt(e.getAttribute("data-x-shift"),10),Number.isNaN(r)&&(r=0)),t.setAttribute("x1",0-r+8),t.setAttribute("x2",n-r-8)}))):o.debug("No Node "+t+": "+JSON.stringify(s.node(t)))}));var x=_.getBBox();s.edges().forEach((function(t){void 0!==t&&void 0!==s.edge(t)&&(o.debug("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(s.edge(t))),function(t,e,n){e.points=e.points.filter((function(t){return!Number.isNaN(t.y)}));var r=e.points,i=zu().x((function(t){return t.x})).y((function(t){return t.y})).curve(Vu),a=t.append("path").attr("d",i(r)).attr("id","edge"+pE).attr("class","transition"),s="";if(Jv().state.arrowMarkerAbsolute&&(s=(s=(s=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),a.attr("marker-end","url("+s+"#"+function(t){switch(t){case sE.relationType.AGGREGATION:return"aggregation";case sE.relationType.EXTENSION:return"extension";case sE.relationType.COMPOSITION:return"composition";case sE.relationType.DEPENDENCY:return"dependency"}}(sE.relationType.DEPENDENCY)+"End)"),void 0!==n.title){for(var c=t.append("g").attr("class","stateLabel"),u=Hv.calcLabelPosition(e.points),l=u.x,h=u.y,f=Um.getRows(n.title),d=0,p=[],y=0,g=0,m=0;m<=f.length;m++){var v=c.append("text").attr("text-anchor","middle").text(f[m]).attr("x",l).attr("y",h+d),b=v.node().getBBox();if(y=Math.max(y,b.width),g=Math.min(g,b.x),o.info(b.x,l,h+d),0===d){var _=v.node().getBBox();d=_.height,o.info("Title height",d,h)}p.push(v)}var x=d*f.length;if(f.length>1){var w=(f.length-1)*d*.5;p.forEach((function(t,e){return t.attr("y",h+e*d-w)})),x=d*f.length}var k=c.node().getBBox();c.insert("rect",":first-child").attr("class","box").attr("x",l-y/2-Jv().state.padding/2).attr("y",h-x/2-Jv().state.padding/2-3.5).attr("width",y+Jv().state.padding).attr("height",x+Jv().state.padding),o.info(k)}pE++}(n,s.edge(t),s.edge(t).relation))})),x=_.getBBox();var w={id:r||"root",label:r||"root",width:0,height:0};return w.width=x.width+2*lE.padding,w.height=x.height+2*lE.padding,o.debug("Doc rendered",w,s),w};const mE=function(t,e){lE=Jv().state,$T.parser.yy.clear(),$T.parser.parse(t),o.debug("Rendering diagram "+t);var n=au("[id='".concat(e,"']"));n.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z"),new(kb().Graph)({multigraph:!0,compound:!0,rankdir:"RL"}).setDefaultEdgeLabel((function(){return{}}));var r=sE.getRootDoc();gE(r,n,void 0,!1);var i=lE.padding,a=n.node().getBBox(),s=a.width+2*i,c=a.height+2*i;zv(n,c,1.75*s,lE.useMaxWidth),n.attr("viewBox","".concat(a.x-lE.padding," ").concat(a.y-lE.padding," ")+s+" "+c)};var vE={},bE={},_E=function(t,e,n,r){if("root"!==n.id){var i="rect";!0===n.start&&(i="start"),!1===n.start&&(i="end"),"default"!==n.type&&(i=n.type),bE[n.id]||(bE[n.id]={id:n.id,shape:i,description:Um.sanitizeText(n.id,Jv()),classes:"statediagram-state"}),n.description&&(Array.isArray(bE[n.id].description)?(bE[n.id].shape="rectWithTitle",bE[n.id].description.push(n.description)):bE[n.id].description.length>0?(bE[n.id].shape="rectWithTitle",bE[n.id].description===n.id?bE[n.id].description=[n.description]:bE[n.id].description=[bE[n.id].description,n.description]):(bE[n.id].shape="rect",bE[n.id].description=n.description),bE[n.id].description=Um.sanitizeTextOrArray(bE[n.id].description,Jv())),1===bE[n.id].description.length&&"rectWithTitle"===bE[n.id].shape&&(bE[n.id].shape="rect"),!bE[n.id].type&&n.doc&&(o.info("Setting cluster for ",n.id,kE(n)),bE[n.id].type="group",bE[n.id].dir=kE(n),bE[n.id].shape="divider"===n.type?"divider":"roundedWithTitle",bE[n.id].classes=bE[n.id].classes+" "+(r?"statediagram-cluster statediagram-cluster-alt":"statediagram-cluster"));var a={labelStyle:"",shape:bE[n.id].shape,labelText:bE[n.id].description,classes:bE[n.id].classes,style:"",id:n.id,dir:bE[n.id].dir,domId:"state-"+n.id+"-"+xE,type:bE[n.id].type,padding:15};if(n.note){var s={labelStyle:"",shape:"note",labelText:n.note.text,classes:"statediagram-note",style:"",id:n.id+"----note-"+xE,domId:"state-"+n.id+"----note-"+xE,type:bE[n.id].type,padding:15},c={labelStyle:"",shape:"noteGroup",labelText:n.note.text,classes:bE[n.id].classes,style:"",id:n.id+"----parent",domId:"state-"+n.id+"----parent-"+xE,type:"group",padding:0};xE++,t.setNode(n.id+"----parent",c),t.setNode(s.id,s),t.setNode(n.id,a),t.setParent(n.id,n.id+"----parent"),t.setParent(s.id,n.id+"----parent");var u=n.id,l=s.id;"left of"===n.note.position&&(u=s.id,l=n.id),t.setEdge(u,l,{arrowhead:"none",arrowType:"",style:"fill:none",labelStyle:"",classes:"transition note-edge",arrowheadStyle:"fill: #333",labelpos:"c",labelType:"text",thickness:"normal"})}else t.setNode(n.id,a)}e&&"root"!==e.id&&(o.trace("Setting node ",n.id," to be child of its parent ",e.id),t.setParent(n.id,e.id)),n.doc&&(o.trace("Adding nodes children "),wE(t,n,n.doc,!r))},xE=0,wE=function(t,e,n,r){o.trace("items",n),n.forEach((function(n){if("state"===n.stmt||"default"===n.stmt)_E(t,e,n,r);else if("relation"===n.stmt){_E(t,e,n.state1,r),_E(t,e,n.state2,r);var i={id:"edge"+xE,arrowhead:"normal",arrowTypeEnd:"arrow_barb",style:"fill:none",labelStyle:"",label:Um.sanitizeText(n.description,Jv()),arrowheadStyle:"fill: #333",labelpos:"c",labelType:"text",thickness:"normal",classes:"transition"},a=n.state1.id,o=n.state2.id;t.setEdge(a,o,i,xE),xE++}}))},kE=function(t,e){var n=e||"TB";if(t.doc)for(var r=0;r<t.doc.length;r++){var i=t.doc[r];"dir"===i.stmt&&(n=i.value)}return n};const TE=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)vE[e[n]]=t[e[n]]};function EE(t){return function(t){if(Array.isArray(t))return CE(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return CE(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?CE(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function CE(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var SE="",AE="",ME=[],NE=[],DE=[],OE=function(){for(var t=!0,e=0;e<DE.length;e++)DE[e].processed,t=t&&DE[e].processed;return t};const BE={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().journey},clear:function(){ME.length=0,NE.length=0,AE="",SE="",DE.length=0},setTitle:function(t){SE=t},getTitle:function(){return SE},addSection:function(t){AE=t,ME.push(t)},getSections:function(){return ME},getTasks:function(){for(var t=OE(),e=0;!t&&e<100;)t=OE(),e++;return NE.push.apply(NE,DE),NE},addTask:function(t,e){var n=e.substr(1).split(":"),r=0,i=[];1===n.length?(r=Number(n[0]),i=[]):(r=Number(n[0]),i=n[1].split(","));var a=i.map((function(t){return t.trim()})),o={section:AE,type:AE,people:a,task:t,score:r};DE.push(o)},addTaskOrg:function(t){var e={section:AE,type:AE,description:t,task:t,classes:[]};NE.push(e)},getActors:function(){return t=[],NE.forEach((function(e){e.people&&t.push.apply(t,EE(e.people))})),EE(new Set(t)).sort();var t}};var LE=n(9763),IE=n.n(LE),RE=function(t,e){var n=t.append("rect");return n.attr("x",e.x),n.attr("y",e.y),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("width",e.width),n.attr("height",e.height),n.attr("rx",e.rx),n.attr("ry",e.ry),void 0!==e.class&&n.attr("class",e.class),n},FE=function(t,e){var n=t.append("circle");return n.attr("cx",e.cx),n.attr("cy",e.cy),n.attr("class","actor-"+e.pos),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("r",e.r),void 0!==n.class&&n.attr("class",n.class),void 0!==e.title&&n.append("title").text(e.title),n},PE=-1,jE=function(){function t(t,e,n,i,a,o,s,c){r(e.append("text").attr("x",n+a/2).attr("y",i+o/2+5).style("font-color",c).style("text-anchor","middle").text(t),s)}function e(t,e,n,i,a,o,s,c,u){for(var l=c.taskFontSize,h=c.taskFontFamily,f=t.split(/<br\s*\/?>/gi),d=0;d<f.length;d++){var p=d*l-l*(f.length-1)/2,y=e.append("text").attr("x",n+a/2).attr("y",i).attr("fill",u).style("text-anchor","middle").style("font-size",l).style("font-family",h);y.append("tspan").attr("x",n+a/2).attr("dy",p).text(f[d]),y.attr("y",i+o/2).attr("dominant-baseline","central").attr("alignment-baseline","central"),r(y,s)}}function n(t,n,i,a,o,s,c,u){var l=n.append("switch"),h=l.append("foreignObject").attr("x",i).attr("y",a).attr("width",o).attr("height",s).attr("position","fixed").append("xhtml:div").style("display","table").style("height","100%").style("width","100%");h.append("div").attr("class","label").style("display","table-cell").style("text-align","center").style("vertical-align","middle").text(t),e(t,l,i,a,o,s,c,u),r(h,c)}function r(t,e){for(var n in e)n in e&&t.attr(n,e[n])}return function(r){return"fo"===r.textPlacement?n:"old"===r.textPlacement?t:e}}();const YE=FE,zE=function(t,e,n){var r=t.append("g"),i={x:0,y:0,width:100,anchor:"start",height:100,rx:0,ry:0};i.x=e.x,i.y=e.y,i.fill=e.fill,i.width=n.width,i.height=n.height,i.class="journey-section section-type-"+e.num,i.rx=3,i.ry=3,RE(r,i),jE(n)(e.text,r,i.x,i.y,i.width,i.height,{class:"journey-section section-type-"+e.num},n,e.colour)},UE=function(t,e){var n=e.text.replace(/<br\s*\/?>/gi," "),r=t.append("text");r.attr("x",e.x),r.attr("y",e.y),r.attr("class","legend"),r.style("text-anchor",e.anchor),void 0!==e.class&&r.attr("class",e.class);var i=r.append("tspan");return i.attr("x",e.x+2*e.textMargin),i.text(n),r},qE=function(t,e,n){var r,i,a,o=e.x+n.width/2,s=t.append("g");PE++,s.append("line").attr("id","task"+PE).attr("x1",o).attr("y1",e.y).attr("x2",o).attr("y2",450).attr("class","task-line").attr("stroke-width","1px").attr("stroke-dasharray","4 2").attr("stroke","#666"),r=s,i={cx:o,cy:300+30*(5-e.score),score:e.score},r.append("circle").attr("cx",i.cx).attr("cy",i.cy).attr("class","face").attr("r",15).attr("stroke-width",2).attr("overflow","visible"),(a=r.append("g")).append("circle").attr("cx",i.cx-5).attr("cy",i.cy-5).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),a.append("circle").attr("cx",i.cx+5).attr("cy",i.cy-5).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),i.score>3?function(t){var e=Iu().startAngle(Math.PI/2).endAngle(Math.PI/2*3).innerRadius(7.5).outerRadius(15/2.2);t.append("path").attr("class","mouth").attr("d",e).attr("transform","translate("+i.cx+","+(i.cy+2)+")")}(a):i.score<3?function(t){var e=Iu().startAngle(3*Math.PI/2).endAngle(Math.PI/2*5).innerRadius(7.5).outerRadius(15/2.2);t.append("path").attr("class","mouth").attr("d",e).attr("transform","translate("+i.cx+","+(i.cy+7)+")")}(a):function(t){t.append("line").attr("class","mouth").attr("stroke",2).attr("x1",i.cx-5).attr("y1",i.cy+7).attr("x2",i.cx+5).attr("y2",i.cy+7).attr("class","mouth").attr("stroke-width","1px").attr("stroke","#666")}(a);var c={x:0,y:0,width:100,anchor:"start",height:100,rx:0,ry:0};c.x=e.x,c.y=e.y,c.fill=e.fill,c.width=n.width,c.height=n.height,c.class="task task-type-"+e.num,c.rx=3,c.ry=3,RE(s,c);var u=e.x+14;e.people.forEach((function(t){var n=e.actors[t].color,r={cx:u,cy:e.y,r:7,fill:n,stroke:"#000",title:t,pos:e.actors[t].position};FE(s,r),u+=10})),jE(n)(e.task,s,c.x,c.y,c.width,c.height,{class:"task"},n,e.colour)};LE.parser.yy=BE;var HE={},$E=Jv().journey,WE=Jv().journey.leftMargin,VE={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],init:function(){this.sequenceItems=[],this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0},updateVal:function(t,e,n,r){void 0===t[e]?t[e]=n:t[e]=r(n,t[e])},updateBounds:function(t,e,n,r){var i=Jv().journey,a=this,o=0;this.sequenceItems.forEach((function(s){o++;var c=a.sequenceItems.length-o+1;a.updateVal(s,"starty",e-c*i.boxMargin,Math.min),a.updateVal(s,"stopy",r+c*i.boxMargin,Math.max),a.updateVal(VE.data,"startx",t-c*i.boxMargin,Math.min),a.updateVal(VE.data,"stopx",n+c*i.boxMargin,Math.max),a.updateVal(s,"startx",t-c*i.boxMargin,Math.min),a.updateVal(s,"stopx",n+c*i.boxMargin,Math.max),a.updateVal(VE.data,"starty",e-c*i.boxMargin,Math.min),a.updateVal(VE.data,"stopy",r+c*i.boxMargin,Math.max)}))},insert:function(t,e,n,r){var i=Math.min(t,n),a=Math.max(t,n),o=Math.min(e,r),s=Math.max(e,r);this.updateVal(VE.data,"startx",i,Math.min),this.updateVal(VE.data,"starty",o,Math.min),this.updateVal(VE.data,"stopx",a,Math.max),this.updateVal(VE.data,"stopy",s,Math.max),this.updateBounds(i,o,a,s)},bumpVerticalPos:function(t){this.verticalPos=this.verticalPos+t,this.data.stopy=this.verticalPos},getVerticalPos:function(){return this.verticalPos},getBounds:function(){return this.data}},GE=$E.sectionFills,XE=$E.sectionColours;const ZE=function(t){Object.keys(t).forEach((function(e){$E[e]=t[e]}))},QE=function(t,e){var n=Jv().journey;LE.parser.yy.clear(),LE.parser.parse(t+"\n"),VE.init();var r=au("#"+e);r.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),r.append("defs").append("marker").attr("id","arrowhead").attr("refX",5).attr("refY",2).attr("markerWidth",6).attr("markerHeight",4).attr("orient","auto").append("path").attr("d","M 0,0 V 4 L6,2 Z");var i=LE.parser.yy.getTasks(),a=LE.parser.yy.getTitle(),o=LE.parser.yy.getActors();for(var s in HE)delete HE[s];var c=0;o.forEach((function(t){HE[t]={color:n.actorColours[c%n.actorColours.length],position:c},c++})),function(t){var e=Jv().journey,n=60;Object.keys(HE).forEach((function(r){var i=HE[r].color,a={cx:20,cy:n,r:7,fill:i,stroke:"#000",pos:HE[r].position};YE(t,a);var o={x:40,y:n+7,fill:"#666",text:r,textMargin:5|e.boxTextMargin};UE(t,o),n+=20}))}(r),VE.insert(0,0,WE,50*Object.keys(HE).length),function(t,e,n){for(var r=Jv().journey,i="",a=n+(2*r.height+r.diagramMarginY),o=0,s="#CCC",c="black",u=0,l=0;l<e.length;l++){var h=e[l];if(i!==h.section){s=GE[o%GE.length],u=o%GE.length,c=XE[o%XE.length];var f={x:l*r.taskMargin+l*r.width+WE,y:50,text:h.section,fill:s,num:u,colour:c};zE(t,f,r),i=h.section,o++}var d=h.people.reduce((function(t,e){return HE[e]&&(t[e]=HE[e]),t}),{});h.x=l*r.taskMargin+l*r.width+WE,h.y=a,h.width=r.diagramMarginX,h.height=r.diagramMarginY,h.colour=c,h.fill=s,h.num=u,h.actors=d,qE(t,h,r),VE.insert(h.x,h.y,h.x+h.width+r.taskMargin,450)}}(r,i,0);var u=VE.getBounds();a&&r.append("text").text(a).attr("x",WE).attr("font-size","4ex").attr("font-weight","bold").attr("y",25);var l=u.stopy-u.starty+2*n.diagramMarginY,h=WE+u.stopx+2*n.diagramMarginX;zv(r,l,h,n.useMaxWidth),r.append("line").attr("x1",WE).attr("y1",4*n.height).attr("x2",h-WE-4).attr("y2",4*n.height).attr("stroke-width",4).attr("stroke","black").attr("marker-end","url(#arrowhead)");var f=a?70:0;r.attr("viewBox","".concat(u.startx," -25 ").concat(h," ").concat(l+f)),r.attr("preserveAspectRatio","xMinYMin meet"),r.attr("height",l+f+25)};var KE={};const JE=function(t){return"g.classGroup text {\n fill: ".concat(t.nodeBorder,";\n fill: ").concat(t.classText,";\n stroke: none;\n font-family: ").concat(t.fontFamily,";\n font-size: 10px;\n\n .title {\n font-weight: bolder;\n }\n\n}\n\n.nodeLabel, .edgeLabel {\n color: ").concat(t.classText,";\n}\n.edgeLabel .label rect {\n fill: ").concat(t.mainBkg,";\n}\n.label text {\n fill: ").concat(t.classText,";\n}\n.edgeLabel .label span {\n background: ").concat(t.mainBkg,";\n}\n\n.classTitle {\n font-weight: bolder;\n}\n.node rect,\n .node circle,\n .node ellipse,\n .node polygon,\n .node path {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n }\n\n\n.divider {\n stroke: ").concat(t.nodeBorder,";\n stroke: 1;\n}\n\ng.clickable {\n cursor: pointer;\n}\n\ng.classGroup rect {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n}\n\ng.classGroup line {\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1;\n}\n\n.classLabel .box {\n stroke: none;\n stroke-width: 0;\n fill: ").concat(t.mainBkg,";\n opacity: 0.5;\n}\n\n.classLabel .label {\n fill: ").concat(t.nodeBorder,";\n font-size: 10px;\n}\n\n.relation {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n fill: none;\n}\n\n.dashed-line{\n stroke-dasharray: 3;\n}\n\n#compositionStart, .composition {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#compositionEnd, .composition {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#dependencyStart, .dependency {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#dependencyStart, .dependency {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#extensionStart, .extension {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#extensionEnd, .extension {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#aggregationStart, .aggregation {\n fill: ").concat(t.mainBkg," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#aggregationEnd, .aggregation {\n fill: ").concat(t.mainBkg," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n.edgeTerminals {\n font-size: 11px;\n}\n\n")},tC=function(t){return".label {\n font-family: ".concat(t.fontFamily,";\n color: ").concat(t.nodeTextColor||t.textColor,";\n }\n .cluster-label text {\n fill: ").concat(t.titleColor,";\n }\n .cluster-label span {\n color: ").concat(t.titleColor,";\n }\n\n .label text,span {\n fill: ").concat(t.nodeTextColor||t.textColor,";\n color: ").concat(t.nodeTextColor||t.textColor,";\n }\n\n .node rect,\n .node circle,\n .node ellipse,\n .node polygon,\n .node path {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n }\n\n .node .label {\n text-align: center;\n }\n .node.clickable {\n cursor: pointer;\n }\n\n .arrowheadPath {\n fill: ").concat(t.arrowheadColor,";\n }\n\n .edgePath .path {\n stroke: ").concat(t.lineColor,";\n stroke-width: 2.0px;\n }\n\n .flowchart-link {\n stroke: ").concat(t.lineColor,";\n fill: none;\n }\n\n .edgeLabel {\n background-color: ").concat(t.edgeLabelBackground,";\n rect {\n opacity: 0.5;\n background-color: ").concat(t.edgeLabelBackground,";\n fill: ").concat(t.edgeLabelBackground,";\n }\n text-align: center;\n }\n\n .cluster rect {\n fill: ").concat(t.clusterBkg,";\n stroke: ").concat(t.clusterBorder,";\n stroke-width: 1px;\n }\n\n .cluster text {\n fill: ").concat(t.titleColor,";\n }\n\n .cluster span {\n color: ").concat(t.titleColor,";\n }\n /* .cluster div {\n color: ").concat(t.titleColor,";\n } */\n\n div.mermaidTooltip {\n position: absolute;\n text-align: center;\n max-width: 200px;\n padding: 2px;\n font-family: ").concat(t.fontFamily,";\n font-size: 12px;\n background: ").concat(t.tertiaryColor,";\n border: 1px solid ").concat(t.border2,";\n border-radius: 2px;\n pointer-events: none;\n z-index: 100;\n }\n")},eC=function(t){return"\ndefs #statediagram-barbEnd {\n fill: ".concat(t.transitionColor,";\n stroke: ").concat(t.transitionColor,";\n }\ng.stateGroup text {\n fill: ").concat(t.nodeBorder,";\n stroke: none;\n font-size: 10px;\n}\ng.stateGroup text {\n fill: ").concat(t.textColor,";\n stroke: none;\n font-size: 10px;\n\n}\ng.stateGroup .state-title {\n font-weight: bolder;\n fill: ").concat(t.stateLabelColor,";\n}\n\ng.stateGroup rect {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n}\n\ng.stateGroup line {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n}\n\n.transition {\n stroke: ").concat(t.transitionColor,";\n stroke-width: 1;\n fill: none;\n}\n\n.stateGroup .composit {\n fill: ").concat(t.background,";\n border-bottom: 1px\n}\n\n.stateGroup .alt-composit {\n fill: #e0e0e0;\n border-bottom: 1px\n}\n\n.state-note {\n stroke: ").concat(t.noteBorderColor,";\n fill: ").concat(t.noteBkgColor,";\n\n text {\n fill: ").concat(t.noteTextColor,";\n stroke: none;\n font-size: 10px;\n }\n}\n\n.stateLabel .box {\n stroke: none;\n stroke-width: 0;\n fill: ").concat(t.mainBkg,";\n opacity: 0.5;\n}\n\n.edgeLabel .label rect {\n fill: ").concat(t.labelBackgroundColor,";\n opacity: 0.5;\n}\n.edgeLabel .label text {\n fill: ").concat(t.transitionLabelColor||t.tertiaryTextColor,";\n}\n.label div .edgeLabel {\n color: ").concat(t.transitionLabelColor||t.tertiaryTextColor,";\n}\n\n.stateLabel text {\n fill: ").concat(t.stateLabelColor,";\n font-size: 10px;\n font-weight: bold;\n}\n\n.node circle.state-start {\n fill: ").concat(t.specialStateColor,";\n stroke: ").concat(t.specialStateColor,";\n}\n\n.node .fork-join {\n fill: ").concat(t.specialStateColor,";\n stroke: ").concat(t.specialStateColor,";\n}\n\n.node circle.state-end {\n fill: ").concat(t.innerEndBackground,";\n stroke: ").concat(t.background,";\n stroke-width: 1.5\n}\n.end-state-inner {\n fill: ").concat(t.compositeBackground||t.background,";\n // stroke: ").concat(t.background,";\n stroke-width: 1.5\n}\n\n.node rect {\n fill: ").concat(t.stateBkg||t.mainBkg,";\n stroke: ").concat(t.stateBorder||t.nodeBorder,";\n stroke-width: 1px;\n}\n.node polygon {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.stateBorder||t.nodeBorder,";;\n stroke-width: 1px;\n}\n#statediagram-barbEnd {\n fill: ").concat(t.lineColor,";\n}\n\n.statediagram-cluster rect {\n fill: ").concat(t.compositeTitleBackground,";\n stroke: ").concat(t.stateBorder||t.nodeBorder,";\n stroke-width: 1px;\n}\n\n.cluster-label, .nodeLabel {\n color: ").concat(t.stateLabelColor,";\n}\n\n.statediagram-cluster rect.outer {\n rx: 5px;\n ry: 5px;\n}\n.statediagram-state .divider {\n stroke: ").concat(t.stateBorder||t.nodeBorder,";\n}\n\n.statediagram-state .title-state {\n rx: 5px;\n ry: 5px;\n}\n.statediagram-cluster.statediagram-cluster .inner {\n fill: ").concat(t.compositeBackground||t.background,";\n}\n.statediagram-cluster.statediagram-cluster-alt .inner {\n fill: ").concat(t.altBackground?t.altBackground:"#efefef",";\n}\n\n.statediagram-cluster .inner {\n rx:0;\n ry:0;\n}\n\n.statediagram-state rect.basic {\n rx: 5px;\n ry: 5px;\n}\n.statediagram-state rect.divider {\n stroke-dasharray: 10,10;\n fill: ").concat(t.altBackground?t.altBackground:"#efefef",";\n}\n\n.note-edge {\n stroke-dasharray: 5;\n}\n\n.statediagram-note rect {\n fill: ").concat(t.noteBkgColor,";\n stroke: ").concat(t.noteBorderColor,";\n stroke-width: 1px;\n rx: 0;\n ry: 0;\n}\n.statediagram-note rect {\n fill: ").concat(t.noteBkgColor,";\n stroke: ").concat(t.noteBorderColor,";\n stroke-width: 1px;\n rx: 0;\n ry: 0;\n}\n\n.statediagram-note text {\n fill: ").concat(t.noteTextColor,";\n}\n\n.statediagram-note .nodeLabel {\n color: ").concat(t.noteTextColor,";\n}\n.statediagram .edgeLabel {\n color: red; // ").concat(t.noteTextColor,";\n}\n\n#dependencyStart, #dependencyEnd {\n fill: ").concat(t.lineColor,";\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n}\n")};var nC={flowchart:tC,"flowchart-v2":tC,sequence:function(t){return".actor {\n stroke: ".concat(t.actorBorder,";\n fill: ").concat(t.actorBkg,";\n }\n\n text.actor > tspan {\n fill: ").concat(t.actorTextColor,";\n stroke: none;\n }\n\n .actor-line {\n stroke: ").concat(t.actorLineColor,";\n }\n\n .messageLine0 {\n stroke-width: 1.5;\n stroke-dasharray: none;\n stroke: ").concat(t.signalColor,";\n }\n\n .messageLine1 {\n stroke-width: 1.5;\n stroke-dasharray: 2, 2;\n stroke: ").concat(t.signalColor,";\n }\n\n #arrowhead path {\n fill: ").concat(t.signalColor,";\n stroke: ").concat(t.signalColor,";\n }\n\n .sequenceNumber {\n fill: ").concat(t.sequenceNumberColor,";\n }\n\n #sequencenumber {\n fill: ").concat(t.signalColor,";\n }\n\n #crosshead path {\n fill: ").concat(t.signalColor,";\n stroke: ").concat(t.signalColor,";\n }\n\n .messageText {\n fill: ").concat(t.signalTextColor,";\n stroke: ").concat(t.signalTextColor,";\n }\n\n .labelBox {\n stroke: ").concat(t.labelBoxBorderColor,";\n fill: ").concat(t.labelBoxBkgColor,";\n }\n\n .labelText, .labelText > tspan {\n fill: ").concat(t.labelTextColor,";\n stroke: none;\n }\n\n .loopText, .loopText > tspan {\n fill: ").concat(t.loopTextColor,";\n stroke: none;\n }\n\n .loopLine {\n stroke-width: 2px;\n stroke-dasharray: 2, 2;\n stroke: ").concat(t.labelBoxBorderColor,";\n fill: ").concat(t.labelBoxBorderColor,";\n }\n\n .note {\n //stroke: #decc93;\n stroke: ").concat(t.noteBorderColor,";\n fill: ").concat(t.noteBkgColor,";\n }\n\n .noteText, .noteText > tspan {\n fill: ").concat(t.noteTextColor,";\n stroke: none;\n }\n\n .activation0 {\n fill: ").concat(t.activationBkgColor,";\n stroke: ").concat(t.activationBorderColor,";\n }\n\n .activation1 {\n fill: ").concat(t.activationBkgColor,";\n stroke: ").concat(t.activationBorderColor,";\n }\n\n .activation2 {\n fill: ").concat(t.activationBkgColor,";\n stroke: ").concat(t.activationBorderColor,";\n }\n\n .actorPopupMenu {\n position: absolute;\n }\n\n .actorPopupMenuPanel {\n position: absolute;\n fill: ").concat(t.actorBkg,";\n box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);\n filter: drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));\n}\n .actor-man line {\n stroke: ").concat(t.actorBorder,";\n fill: ").concat(t.actorBkg,";\n }\n .actor-man circle, line {\n stroke: ").concat(t.actorBorder,";\n fill: ").concat(t.actorBkg,";\n stroke-width: 2px;\n }\n")},gantt:function(t){return'\n .mermaid-main-font {\n font-family: "trebuchet ms", verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n .exclude-range {\n fill: '.concat(t.excludeBkgColor,";\n }\n\n .section {\n stroke: none;\n opacity: 0.2;\n }\n\n .section0 {\n fill: ").concat(t.sectionBkgColor,";\n }\n\n .section2 {\n fill: ").concat(t.sectionBkgColor2,";\n }\n\n .section1,\n .section3 {\n fill: ").concat(t.altSectionBkgColor,";\n opacity: 0.2;\n }\n\n .sectionTitle0 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle1 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle2 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle3 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle {\n text-anchor: start;\n // font-size: ").concat(t.ganttFontSize,";\n // text-height: 14px;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n\n }\n\n\n /* Grid and axis */\n\n .grid .tick {\n stroke: ").concat(t.gridColor,";\n opacity: 0.8;\n shape-rendering: crispEdges;\n text {\n font-family: ").concat(t.fontFamily,";\n fill: ").concat(t.textColor,";\n }\n }\n\n .grid path {\n stroke-width: 0;\n }\n\n\n /* Today line */\n\n .today {\n fill: none;\n stroke: ").concat(t.todayLineColor,";\n stroke-width: 2px;\n }\n\n\n /* Task styling */\n\n /* Default task */\n\n .task {\n stroke-width: 2;\n }\n\n .taskText {\n text-anchor: middle;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n\n // .taskText:not([font-size]) {\n // font-size: ").concat(t.ganttFontSize,";\n // }\n\n .taskTextOutsideRight {\n fill: ").concat(t.taskTextDarkColor,";\n text-anchor: start;\n // font-size: ").concat(t.ganttFontSize,";\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n\n }\n\n .taskTextOutsideLeft {\n fill: ").concat(t.taskTextDarkColor,";\n text-anchor: end;\n // font-size: ").concat(t.ganttFontSize,";\n }\n\n /* Special case clickable */\n .task.clickable {\n cursor: pointer;\n }\n .taskText.clickable {\n cursor: pointer;\n fill: ").concat(t.taskTextClickableColor," !important;\n font-weight: bold;\n }\n\n .taskTextOutsideLeft.clickable {\n cursor: pointer;\n fill: ").concat(t.taskTextClickableColor," !important;\n font-weight: bold;\n }\n\n .taskTextOutsideRight.clickable {\n cursor: pointer;\n fill: ").concat(t.taskTextClickableColor," !important;\n font-weight: bold;\n }\n\n /* Specific task settings for the sections*/\n\n .taskText0,\n .taskText1,\n .taskText2,\n .taskText3 {\n fill: ").concat(t.taskTextColor,";\n }\n\n .task0,\n .task1,\n .task2,\n .task3 {\n fill: ").concat(t.taskBkgColor,";\n stroke: ").concat(t.taskBorderColor,";\n }\n\n .taskTextOutside0,\n .taskTextOutside2\n {\n fill: ").concat(t.taskTextOutsideColor,";\n }\n\n .taskTextOutside1,\n .taskTextOutside3 {\n fill: ").concat(t.taskTextOutsideColor,";\n }\n\n\n /* Active task */\n\n .active0,\n .active1,\n .active2,\n .active3 {\n fill: ").concat(t.activeTaskBkgColor,";\n stroke: ").concat(t.activeTaskBorderColor,";\n }\n\n .activeText0,\n .activeText1,\n .activeText2,\n .activeText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n\n /* Completed task */\n\n .done0,\n .done1,\n .done2,\n .done3 {\n stroke: ").concat(t.doneTaskBorderColor,";\n fill: ").concat(t.doneTaskBkgColor,";\n stroke-width: 2;\n }\n\n .doneText0,\n .doneText1,\n .doneText2,\n .doneText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n\n /* Tasks on the critical line */\n\n .crit0,\n .crit1,\n .crit2,\n .crit3 {\n stroke: ").concat(t.critBorderColor,";\n fill: ").concat(t.critBkgColor,";\n stroke-width: 2;\n }\n\n .activeCrit0,\n .activeCrit1,\n .activeCrit2,\n .activeCrit3 {\n stroke: ").concat(t.critBorderColor,";\n fill: ").concat(t.activeTaskBkgColor,";\n stroke-width: 2;\n }\n\n .doneCrit0,\n .doneCrit1,\n .doneCrit2,\n .doneCrit3 {\n stroke: ").concat(t.critBorderColor,";\n fill: ").concat(t.doneTaskBkgColor,";\n stroke-width: 2;\n cursor: pointer;\n shape-rendering: crispEdges;\n }\n\n .milestone {\n transform: rotate(45deg) scale(0.8,0.8);\n }\n\n .milestoneText {\n font-style: italic;\n }\n .doneCritText0,\n .doneCritText1,\n .doneCritText2,\n .doneCritText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n .activeCritText0,\n .activeCritText1,\n .activeCritText2,\n .activeCritText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n .titleText {\n text-anchor: middle;\n font-size: 18px;\n fill: ").concat(t.textColor," ;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n")},classDiagram:JE,"classDiagram-v2":JE,class:JE,stateDiagram:eC,state:eC,git:function(){return"\n .commit-id,\n .commit-msg,\n .branch-label {\n fill: lightgrey;\n color: lightgrey;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n"},info:function(){return""},pie:function(t){return"\n .pieCircle{\n stroke: ".concat(t.pieStrokeColor,";\n stroke-width : ").concat(t.pieStrokeWidth,";\n opacity : ").concat(t.pieOpacity,";\n }\n .pieTitleText {\n text-anchor: middle;\n font-size: ").concat(t.pieTitleTextSize,";\n fill: ").concat(t.pieTitleTextColor,";\n font-family: ").concat(t.fontFamily,";\n }\n .slice {\n font-family: ").concat(t.fontFamily,";\n fill: ").concat(t.pieSectionTextColor,";\n font-size:").concat(t.pieSectionTextSize,";\n // fill: white;\n }\n .legend text {\n fill: ").concat(t.pieLegendTextColor,";\n font-family: ").concat(t.fontFamily,";\n font-size: ").concat(t.pieLegendTextSize,";\n }\n")},er:function(t){return"\n .entityBox {\n fill: ".concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n }\n\n .attributeBoxOdd {\n fill: #ffffff;\n stroke: ").concat(t.nodeBorder,";\n }\n\n .attributeBoxEven {\n fill: #f2f2f2;\n stroke: ").concat(t.nodeBorder,";\n }\n\n .relationshipLabelBox {\n fill: ").concat(t.tertiaryColor,";\n opacity: 0.7;\n background-color: ").concat(t.tertiaryColor,";\n rect {\n opacity: 0.5;\n }\n }\n\n .relationshipLine {\n stroke: ").concat(t.lineColor,";\n }\n")},journey:function(t){return".label {\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n color: ".concat(t.textColor,";\n }\n .mouth {\n stroke: #666;\n }\n\n line {\n stroke: ").concat(t.textColor,"\n }\n\n .legend {\n fill: ").concat(t.textColor,";\n }\n\n .label text {\n fill: #333;\n }\n .label {\n color: ").concat(t.textColor,"\n }\n\n .face {\n ").concat(t.faceColor?"fill: ".concat(t.faceColor):"fill: #FFF8DC",";\n stroke: #999;\n }\n\n .node rect,\n .node circle,\n .node ellipse,\n .node polygon,\n .node path {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n }\n\n .node .label {\n text-align: center;\n }\n .node.clickable {\n cursor: pointer;\n }\n\n .arrowheadPath {\n fill: ").concat(t.arrowheadColor,";\n }\n\n .edgePath .path {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1.5px;\n }\n\n .flowchart-link {\n stroke: ").concat(t.lineColor,";\n fill: none;\n }\n\n .edgeLabel {\n background-color: ").concat(t.edgeLabelBackground,";\n rect {\n opacity: 0.5;\n }\n text-align: center;\n }\n\n .cluster rect {\n }\n\n .cluster text {\n fill: ").concat(t.titleColor,";\n }\n\n div.mermaidTooltip {\n position: absolute;\n text-align: center;\n max-width: 200px;\n padding: 2px;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n font-size: 12px;\n background: ").concat(t.tertiaryColor,";\n border: 1px solid ").concat(t.border2,";\n border-radius: 2px;\n pointer-events: none;\n z-index: 100;\n }\n\n .task-type-0, .section-type-0 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType0):"",";\n }\n .task-type-1, .section-type-1 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType1):"",";\n }\n .task-type-2, .section-type-2 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType2):"",";\n }\n .task-type-3, .section-type-3 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType3):"",";\n }\n .task-type-4, .section-type-4 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType4):"",";\n }\n .task-type-5, .section-type-5 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType5):"",";\n }\n .task-type-6, .section-type-6 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType6):"",";\n }\n .task-type-7, .section-type-7 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType7):"",";\n }\n\n .actor-0 {\n ").concat(t.actor0?"fill: ".concat(t.actor0):"",";\n }\n .actor-1 {\n ").concat(t.actor1?"fill: ".concat(t.actor1):"",";\n }\n .actor-2 {\n ").concat(t.actor2?"fill: ".concat(t.actor2):"",";\n }\n .actor-3 {\n ").concat(t.actor3?"fill: ".concat(t.actor3):"",";\n }\n .actor-4 {\n ").concat(t.actor4?"fill: ".concat(t.actor4):"",";\n }\n .actor-5 {\n ").concat(t.actor5?"fill: ".concat(t.actor5):"",";\n }\n\n }\n")},requirement:function(t){return"\n\n marker {\n fill: ".concat(t.relationColor,";\n stroke: ").concat(t.relationColor,";\n }\n\n marker.cross {\n stroke: ").concat(t.lineColor,";\n }\n\n svg {\n font-family: ").concat(t.fontFamily,";\n font-size: ").concat(t.fontSize,";\n }\n\n .reqBox {\n fill: ").concat(t.requirementBackground,";\n fill-opacity: 100%;\n stroke: ").concat(t.requirementBorderColor,";\n stroke-width: ").concat(t.requirementBorderSize,";\n }\n \n .reqTitle, .reqLabel{\n fill: ").concat(t.requirementTextColor,";\n }\n .reqLabelBox {\n fill: ").concat(t.relationLabelBackground,";\n fill-opacity: 100%;\n }\n\n .req-title-line {\n stroke: ").concat(t.requirementBorderColor,";\n stroke-width: ").concat(t.requirementBorderSize,";\n }\n .relationshipLine {\n stroke: ").concat(t.relationColor,";\n stroke-width: 1;\n }\n .relationshipLabel {\n fill: ").concat(t.relationLabelColor,";\n }\n\n")}};function rC(t){return rC="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},rC(t)}var iC=function(t){var e=t;return(e=(e=e.replace(/fl°°/g,(function(){return"&#"}))).replace(/fl°/g,(function(){return"&"}))).replace(/¶ß/g,(function(){return";"}))},aC={};function oC(t){var e;Zw(t.git),Fx(t.flowchart),Yx(t.flowchart),void 0!==t.sequenceDiagram&&HT.setConf(Lv(t.sequence,t.sequenceDiagram)),HT.setConf(t.sequence),t.gantt,Pb(t.class),t.state,TE(t.state),rk(t.class),U_(t.er),ZE(t.journey),Mk(t.requirement),e=t.class,Object.keys(e).forEach((function(t){KE[t]=e[t]}))}var sC=Object.freeze({render:function(t,e,n,r){nb();var i=e,a=Hv.detectInit(i);a&&(Uv(a),eb(a));var s=Jv();if(e.length>s.maxTextSize&&(i="graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa"),void 0!==r)r.innerHTML="",au(r).append("div").attr("id","d"+t).attr("style","font-family: "+s.fontFamily).append("svg").attr("id",t).attr("width","100%").attr("xmlns","http://www.w3.org/2000/svg").append("g");else{var c=document.getElementById(t);c&&c.remove();var u=document.querySelector("#d"+t);u&&u.remove(),au("body").append("div").attr("id","d"+t).append("svg").attr("id",t).attr("width","100%").attr("xmlns","http://www.w3.org/2000/svg").append("g")}window.txt=i,i=i.replace(/style.*:\S*#.*;/g,(function(t){return t.substring(0,t.length-1)})).replace(/classDef.*:\S*#.*;/g,(function(t){return t.substring(0,t.length-1)})).replace(/#\w+;/g,(function(t){var e=t.substring(1,t.length-1);return/^\+?\d+$/.test(e)?"fl°°"+e+"¶ß":"fl°"+e+"¶ß"}));var l=au("#d"+t).node(),h=Hv.detectType(i,s),f=l.firstChild,d=f.firstChild,p="";if(void 0!==s.themeCSS&&(p+="\n".concat(s.themeCSS)),void 0!==s.fontFamily&&(p+="\n:root { --mermaid-font-family: ".concat(s.fontFamily,"}")),void 0!==s.altFontFamily&&(p+="\n:root { --mermaid-alt-font-family: ".concat(s.altFontFamily,"}")),"flowchart"===h||"flowchart-v2"===h||"graph"===h){var y=function(t){o.info("Extracting classes"),gx.clear();try{var e=vx().parser;return e.yy=gx,e.parse(t),gx.getClasses()}catch(t){return}}(i),g=s.htmlLabels||s.flowchart.htmlLabels;for(var m in y)g?(p+="\n.".concat(m," > * { ").concat(y[m].styles.join(" !important; ")," !important; }"),p+="\n.".concat(m," span { ").concat(y[m].styles.join(" !important; ")," !important; }")):(p+="\n.".concat(m," path { ").concat(y[m].styles.join(" !important; ")," !important; }"),p+="\n.".concat(m," rect { ").concat(y[m].styles.join(" !important; ")," !important; }"),p+="\n.".concat(m," polygon { ").concat(y[m].styles.join(" !important; ")," !important; }"),p+="\n.".concat(m," ellipse { ").concat(y[m].styles.join(" !important; ")," !important; }"),p+="\n.".concat(m," circle { ").concat(y[m].styles.join(" !important; ")," !important; }"),y[m].textStyles&&(p+="\n.".concat(m," tspan { ").concat(y[m].textStyles.join(" !important; ")," !important; }")))}var v=function(t,e){return am(Cm("".concat(t,"{").concat(e,"}")),om)}("#".concat(t),function(t,e,n){return" {\n font-family: ".concat(n.fontFamily,";\n font-size: ").concat(n.fontSize,";\n fill: ").concat(n.textColor,"\n }\n\n /* Classes common for multiple diagrams */\n\n .error-icon {\n fill: ").concat(n.errorBkgColor,";\n }\n .error-text {\n fill: ").concat(n.errorTextColor,";\n stroke: ").concat(n.errorTextColor,";\n }\n\n .edge-thickness-normal {\n stroke-width: 2px;\n }\n .edge-thickness-thick {\n stroke-width: 3.5px\n }\n .edge-pattern-solid {\n stroke-dasharray: 0;\n }\n\n .edge-pattern-dashed{\n stroke-dasharray: 3;\n }\n .edge-pattern-dotted {\n stroke-dasharray: 2;\n }\n\n .marker {\n fill: ").concat(n.lineColor,";\n stroke: ").concat(n.lineColor,";\n }\n .marker.cross {\n stroke: ").concat(n.lineColor,";\n }\n\n svg {\n font-family: ").concat(n.fontFamily,";\n font-size: ").concat(n.fontSize,";\n }\n\n ").concat(nC[t](n),"\n\n ").concat(e,"\n")}(h,p,s.themeVariables)),b=document.createElement("style");b.innerHTML="#".concat(t," ")+v,f.insertBefore(b,d);try{switch(h){case"git":s.flowchart.arrowMarkerAbsolute=s.arrowMarkerAbsolute,Zw(s.git),function(t,e,n){try{var r=Yw().parser;r.yy=Fw,r.yy.clear(),o.debug("in gitgraph renderer",t+"\n","id:",e,n),r.parse(t+"\n"),Uw=Object.assign(Uw,qw,Fw.getOptions()),o.debug("effective options",Uw);var i=Fw.getDirection();zw=Fw.getCommits();var a=Fw.getBranchesAsObjArray();"BT"===i&&(Uw.nodeLabel.x=a.length*Uw.branchOffset,Uw.nodeLabel.width="100%",Uw.nodeLabel.y=-2*Uw.nodeRadius);var s=au('[id="'.concat(e,'"]'));for(var c in function(t){t.append("defs").append("g").attr("id","def-commit").append("circle").attr("r",Uw.nodeRadius).attr("cx",0).attr("cy",0),t.select("#def-commit").append("foreignObject").attr("width",Uw.nodeLabel.width).attr("height",Uw.nodeLabel.height).attr("x",Uw.nodeLabel.x).attr("y",Uw.nodeLabel.y).attr("class","node-label").attr("requiredFeatures","http://www.w3.org/TR/SVG11/feature#Extensibility").append("p").html("")}(s),Pw=1,a){var u=a[c];Gw(s,u.commit.id,a,i),Xw(s,u.commit,i),Pw++}s.attr("height",(function(){return"BT"===i?Object.keys(zw).length*Uw.nodeSpacing:(a.length+1)*Uw.branchOffset}))}catch(t){o.error("Error while rendering gitgraph"),o.error(t.message)}}(i,t,!1);break;case"flowchart":s.flowchart.arrowMarkerAbsolute=s.arrowMarkerAbsolute,Fx(s.flowchart),Px(i,t);break;case"flowchart-v2":s.flowchart.arrowMarkerAbsolute=s.arrowMarkerAbsolute,Yx(s.flowchart),zx(i,t);break;case"sequence":s.sequence.arrowMarkerAbsolute=s.arrowMarkerAbsolute,s.sequenceDiagram?(HT.setConf(Object.assign(s.sequence,s.sequenceDiagram)),console.error("`mermaid config.sequenceDiagram` has been renamed to `config.sequence`. Please update your mermaid config.")):HT.setConf(s.sequence),HT.draw(i,t);break;case"gantt":s.gantt.arrowMarkerAbsolute=s.arrowMarkerAbsolute,s.gantt,Tw(i,t);break;case"class":s.class.arrowMarkerAbsolute=s.arrowMarkerAbsolute,Pb(s.class),jb(i,t);break;case"classDiagram":s.class.arrowMarkerAbsolute=s.arrowMarkerAbsolute,function(t){Object.keys(t).forEach((function(e){A_[e]=t[e]}))}(s.class),function(t,e){o.info("Drawing class"),bb.clear(),Tb.parser.parse(t);var n=Jv().flowchart;o.info("config:",n);var r=n.nodeSpacing||50,i=n.rankSpacing||50,a=new(kb().Graph)({multigraph:!0,compound:!0}).setGraph({rankdir:bb.getDirection(),nodesep:r,ranksep:i,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),s=bb.getClasses(),c=bb.getRelations();o.info(c),function(t,e){var n=Object.keys(t);o.info("keys:",n),o.info(t),n.forEach((function(n){var r=t[n],i="";r.cssClasses.length>0&&(i=i+" "+r.cssClasses.join(" "));var a,s,c={labelStyle:""},u=void 0!==r.text?r.text:r.id;r.type,s="class_box",e.setNode(r.id,{labelStyle:c.labelStyle,shape:s,labelText:(a=u,Um.sanitizeText(a,Jv())),classData:r,rx:0,ry:0,class:i,style:c.style,id:r.id,domId:r.domId,haveCallback:r.haveCallback,link:r.link,width:"group"===r.type?500:void 0,type:r.type,padding:Jv().flowchart.padding}),o.info("setNode",{labelStyle:c.labelStyle,shape:s,labelText:u,rx:0,ry:0,class:i,style:c.style,id:r.id,width:"group"===r.type?500:void 0,type:r.type,padding:Jv().flowchart.padding})}))}(s,a),function(t,e){var n=0;t.forEach((function(r){n++;var i={classes:"relation"};i.pattern=1==r.relation.lineType?"dashed":"solid",i.id="id"+n,"arrow_open"===r.type?i.arrowhead="none":i.arrowhead="normal",o.info(i,r),i.startLabelRight="none"===r.relationTitle1?"":r.relationTitle1,i.endLabelLeft="none"===r.relationTitle2?"":r.relationTitle2,i.arrowTypeStart=M_(r.relation.type1),i.arrowTypeEnd=M_(r.relation.type2);var a="",s="";if(void 0!==r.style){var c=Nv(r.style);a=c.style,s=c.labelStyle}else a="fill:none";i.style=a,i.labelStyle=s,void 0!==r.interpolate?i.curve=Av(r.interpolate,Pu):void 0!==t.defaultInterpolate?i.curve=Av(t.defaultInterpolate,Pu):i.curve=Av(A_.curve,Pu),r.text=r.title,void 0===r.text?void 0!==r.style&&(i.arrowheadStyle="fill: #333"):(i.arrowheadStyle="fill: #333",i.labelpos="c",Jv().flowchart.htmlLabels?(i.labelType="html",i.label='<span class="edgeLabel">'+r.text+"</span>"):(i.labelType="text",i.label=r.text.replace(Um.lineBreakRegex,"\n"),void 0===r.style&&(i.style=i.style||"stroke: #333; stroke-width: 1.5px;fill:none"),i.labelStyle=i.labelStyle.replace("color:","fill:"))),e.setEdge(r.id1,r.id2,i,n)}))}(c,a);var u=au('[id="'.concat(e,'"]'));u.attr("xmlns:xlink","http://www.w3.org/1999/xlink");var l=au("#"+e+" g");S_(l,a,["aggregation","extension","composition","dependency"],"classDiagram",e);var h=u.node().getBBox(),f=h.width+16,d=h.height+16;if(o.debug("new ViewBox 0 0 ".concat(f," ").concat(d),"translate(".concat(8-a._label.marginx,", ").concat(8-a._label.marginy,")")),zv(u,d,f,n.useMaxWidth),u.attr("viewBox","0 0 ".concat(f," ").concat(d)),u.select("g").attr("transform","translate(".concat(8-a._label.marginx,", ").concat(8-h.y,")")),!n.htmlLabels)for(var p=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),y=0;y<p.length;y++){var g=p[y],m=g.getBBox(),v=document.createElementNS("http://www.w3.org/2000/svg","rect");v.setAttribute("rx",0),v.setAttribute("ry",0),v.setAttribute("width",m.width),v.setAttribute("height",m.height),g.insertBefore(v,g.firstChild)}}(i,t);break;case"state":s.class.arrowMarkerAbsolute=s.arrowMarkerAbsolute,s.state,mE(i,t);break;case"stateDiagram":s.class.arrowMarkerAbsolute=s.arrowMarkerAbsolute,TE(s.state),function(t,e){o.info("Drawing state diagram (v2)",e),sE.clear(),bE={};var n=WT().parser;n.yy=sE,n.parse(t);var r=sE.getDirection();void 0===r&&(r="LR");var i=Jv().state,a=i.nodeSpacing||50,s=i.rankSpacing||50;o.info(sE.getRootDocV2()),sE.extract(sE.getRootDocV2()),o.info(sE.getRootDocV2());var c=new(kb().Graph)({multigraph:!0,compound:!0}).setGraph({rankdir:kE(sE.getRootDocV2()),nodesep:a,ranksep:s,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}}));_E(c,void 0,sE.getRootDocV2(),!0);var u=au('[id="'.concat(e,'"]')),l=au("#"+e+" g");S_(l,c,["barb"],"statediagram",e);var h=u.node().getBBox(),f=h.width+16,d=h.height+16;u.attr("class","statediagram");var p=u.node().getBBox();zv(u,d,1.75*f,i.useMaxWidth);var y="".concat(p.x-8," ").concat(p.y-8," ").concat(f," ").concat(d);o.debug("viewBox ".concat(y)),u.attr("viewBox",y);for(var g=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),m=0;m<g.length;m++){var v=g[m],b=v.getBBox(),_=document.createElementNS("http://www.w3.org/2000/svg","rect");_.setAttribute("rx",0),_.setAttribute("ry",0),_.setAttribute("width",b.width),_.setAttribute("height",b.height),v.insertBefore(_,v.firstChild)}}(i,t);break;case"info":s.class.arrowMarkerAbsolute=s.arrowMarkerAbsolute,rk(s.class),function(t,e,n){try{var r=ek().parser;r.yy=Jw,o.debug("Renering info diagram\n"+t),r.parse(t),o.debug("Parsed info diagram");var i=au("#"+e);i.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size","32px").style("text-anchor","middle").text("v "+n),i.attr("height",100),i.attr("width",400)}catch(t){o.error("Error while rendering info diagram"),o.error(t.message)}}(i,t,Dm);break;case"pie":fk(i,t);break;case"er":U_(s.er),q_(i,t);break;case"journey":ZE(s.journey),QE(i,t);break;case"requirement":Mk(s.requirement),Nk(i,t)}}catch(e){throw function(t,e){try{o.debug("Renering svg for syntax error\n");var n=au("#"+t),r=n.append("g");r.append("path").attr("class","error-icon").attr("d","m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z"),r.append("path").attr("class","error-icon").attr("d","m459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z"),r.append("path").attr("class","error-icon").attr("d","m340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z"),r.append("path").attr("class","error-icon").attr("d","m400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z"),r.append("path").attr("class","error-icon").attr("d","m496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z"),r.append("path").attr("class","error-icon").attr("d","m436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z"),r.append("text").attr("class","error-text").attr("x",1240).attr("y",250).attr("font-size","150px").style("text-anchor","middle").text("Syntax error in graph"),r.append("text").attr("class","error-text").attr("x",1050).attr("y",400).attr("font-size","100px").style("text-anchor","middle").text("mermaid version "+e),n.attr("height",100),n.attr("width",400),n.attr("viewBox","768 0 512 512")}catch(t){o.error("Error while rendering info diagram"),o.error(t.message)}}(t,Dm),e}au('[id="'.concat(t,'"]')).selectAll("foreignobject > *").attr("xmlns","http://www.w3.org/1999/xhtml");var _=au("#d"+t).node().innerHTML;if(o.debug("cnf.arrowMarkerAbsolute",s.arrowMarkerAbsolute),s.arrowMarkerAbsolute&&"false"!==s.arrowMarkerAbsolute||(_=_.replace(/marker-end="url\(.*?#/g,'marker-end="url(#',"g")),_=(_=iC(_)).replace(/<br>/g,"<br/>"),void 0!==n)switch(h){case"flowchart":case"flowchart-v2":n(_,gx.bindFunctions);break;case"gantt":n(_,bw.bindFunctions);break;case"class":case"classDiagram":n(_,bb.bindFunctions);break;default:n(_)}else o.debug("CB = undefined!");eT.forEach((function(t){t()})),eT=[];var x=au("#d"+t).node();return null!==x&&"function"==typeof x.remove&&au("#d"+t).node().remove(),_},parse:function(t){var e=Jv(),n=Hv.detectInit(t,e);n&&o.debug("reinit ",n);var r,i=Hv.detectType(t,e);switch(o.debug("Type "+i),i){case"git":(r=Yw()).parser.yy=Fw;break;case"flowchart":case"flowchart-v2":gx.clear(),(r=vx()).parser.yy=gx;break;case"sequence":(r=Ok()).parser.yy=tT;break;case"gantt":(r=kw()).parser.yy=bw;break;case"class":case"classDiagram":(r=Eb()).parser.yy=bb;break;case"state":case"stateDiagram":(r=WT()).parser.yy=sE;break;case"info":o.debug("info info info"),(r=ek()).parser.yy=Jw;break;case"pie":o.debug("pie"),(r=ak()).parser.yy=uk;break;case"er":o.debug("er"),(r=R_()).parser.yy=L_;break;case"journey":o.debug("Journey"),(r=IE()).parser.yy=BE;break;case"requirement":case"requirementDiagram":o.debug("RequirementDiagram"),(r=pk()).parser.yy=_k}return r.parser.yy.graphType=i,r.parser.yy.parseError=function(t,e){throw{str:t,hash:e}},r.parse(t),r},parseDirective:function(t,e,n,r){try{if(void 0!==e)switch(e=e.trim(),n){case"open_directive":aC={};break;case"type_directive":aC.type=e.toLowerCase();break;case"arg_directive":aC.args=JSON.parse(e);break;case"close_directive":(function(t,e,n){switch(o.debug("Directive type=".concat(e.type," with args:"),e.args),e.type){case"init":case"initialize":["config"].forEach((function(t){void 0!==e.args[t]&&("flowchart-v2"===n&&(n="flowchart"),e.args[n]=e.args[t],delete e.args[t])})),o.debug("sanitize in handleDirective",e.args),Uv(e.args),o.debug("sanitize in handleDirective (done)",e.args),e.args,eb(e.args);break;case"wrap":case"nowrap":t&&t.setWrap&&t.setWrap("wrap"===e.type);break;case"themeCss":o.warn("themeCss encountered");break;default:o.warn("Unhandled directive: source: '%%{".concat(e.type,": ").concat(JSON.stringify(e.args?e.args:{}),"}%%"),e)}})(t,aC,r),aC=null}}catch(t){o.error("Error while rendering sequenceDiagram directive: ".concat(e," jison context: ").concat(n)),o.error(t.message)}},initialize:function(t){t&&t.fontFamily&&(t.themeVariables&&t.themeVariables.fontFamily||(t.themeVariables={fontFamily:t.fontFamily})),function(t){Wv=Lv({},t)}(t),t&&t.theme&&ov[t.theme]?t.themeVariables=ov[t.theme].getThemeVariables(t.themeVariables):t&&(t.themeVariables=ov.default.getThemeVariables(t.themeVariables));var e="object"===rC(t)?function(t){return Gv=Lv({},Vv),Gv=Lv(Gv,t),t.theme&&(Gv.themeVariables=ov[t.theme].getThemeVariables(t.themeVariables)),Zv=Qv(Gv,Xv),Gv}(t):Kv();oC(e),s(e.logLevel)},reinitialize:function(){},getConfig:Jv,setConfig:function(t){return Lv(Zv,t),Jv()},getSiteConfig:Kv,updateSiteConfig:function(t){return Gv=Lv(Gv,t),Qv(Gv,Xv),Gv},reset:function(){nb()},globalReset:function(){nb(),oC(Jv())},defaultConfig:Vv});s(Jv().logLevel),nb(Jv());const cC=sC;var uC=function(){lC.startOnLoad?cC.getConfig().startOnLoad&&lC.init():void 0===lC.startOnLoad&&(o.debug("In start, no config"),cC.getConfig().startOnLoad&&lC.init())};"undefined"!=typeof document&&window.addEventListener("load",(function(){uC()}),!1);var lC={startOnLoad:!0,htmlLabels:!0,mermaidAPI:cC,parse:cC.parse,render:cC.render,init:function(){var t,e,n=this,r=cC.getConfig();arguments.length>=2?(void 0!==arguments[0]&&(lC.sequenceConfig=arguments[0]),t=arguments[1]):t=arguments[0],"function"==typeof arguments[arguments.length-1]?(e=arguments[arguments.length-1],o.debug("Callback function found")):void 0!==r.mermaid&&("function"==typeof r.mermaid.callback?(e=r.mermaid.callback,o.debug("Callback function found")):o.debug("No Callback function found")),t=void 0===t?document.querySelectorAll(".mermaid"):"string"==typeof t?document.querySelectorAll(t):t instanceof window.Node?[t]:t,o.debug("Start On Load before: "+lC.startOnLoad),void 0!==lC.startOnLoad&&(o.debug("Start On Load inner: "+lC.startOnLoad),cC.updateSiteConfig({startOnLoad:lC.startOnLoad})),void 0!==lC.ganttConfig&&cC.updateSiteConfig({gantt:lC.ganttConfig});for(var i,a=new Hv.initIdGeneratior(r.deterministicIds,r.deterministicIDSeed),s=function(r){var s=t[r];if(s.getAttribute("data-processed"))return"continue";s.setAttribute("data-processed",!0);var c="mermaid-".concat(a.next());i=s.innerHTML,i=Hv.entityDecode(i).trim().replace(/<br\s*\/?>/gi,"<br/>");var u=Hv.detectInit(i);u&&o.debug("Detected early reinit: ",u);try{cC.render(c,i,(function(t,n){s.innerHTML=t,void 0!==e&&e(c),n&&n(s)}),s)}catch(t){o.warn("Syntax Error rendering"),o.warn(t),n.parseError&&n.parseError(t)}},c=0;c<t.length;c++)s(c)},initialize:function(t){void 0!==t.mermaid&&(void 0!==t.mermaid.startOnLoad&&(lC.startOnLoad=t.mermaid.startOnLoad),void 0!==t.mermaid.htmlLabels&&(lC.htmlLabels="false"!==t.mermaid.htmlLabels&&!1!==t.mermaid.htmlLabels)),cC.initialize(t)},contentLoaded:uC};const hC=lC},4949:(t,e,n)=>{t.exports={graphlib:n(6614),dagre:n(1463),intersect:n(8114),render:n(5787),util:n(8355),version:n(5689)}},9144:(t,e,n)=>{var r=n(8355);function i(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])}t.exports={default:i,normal:i,vee:function(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 L 4 5 z").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])},undirected:function(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 5 L 10 5").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])}}},5632:(t,e,n)=>{var r=n(8355),i=n(4322),a=n(1322);t.exports=function(t,e){var n,o=e.nodes().filter((function(t){return r.isSubgraph(e,t)})),s=t.selectAll("g.cluster").data(o,(function(t){return t}));return s.selectAll("*").remove(),s.enter().append("g").attr("class","cluster").attr("id",(function(t){return e.node(t).id})).style("opacity",0),s=t.selectAll("g.cluster"),r.applyTransition(s,e).style("opacity",1),s.each((function(t){var n=e.node(t),r=i.select(this);i.select(this).append("rect");var o=r.append("g").attr("class","label");a(o,n,n.clusterLabelPos)})),s.selectAll("rect").each((function(t){var n=e.node(t),a=i.select(this);r.applyStyle(a,n.style)})),n=s.exit?s.exit():s.selectAll(null),r.applyTransition(n,e).style("opacity",0).remove(),s}},6315:(t,e,n)=>{"use strict";var r=n(1034),i=n(1322),a=n(8355),o=n(4322);t.exports=function(t,e){var n,s=t.selectAll("g.edgeLabel").data(e.edges(),(function(t){return a.edgeToId(t)})).classed("update",!0);return s.exit().remove(),s.enter().append("g").classed("edgeLabel",!0).style("opacity",0),(s=t.selectAll("g.edgeLabel")).each((function(t){var n=o.select(this);n.select(".label").remove();var a=e.edge(t),s=i(n,e.edge(t),0,0).classed("label",!0),c=s.node().getBBox();a.labelId&&s.attr("id",a.labelId),r.has(a,"width")||(a.width=c.width),r.has(a,"height")||(a.height=c.height)})),n=s.exit?s.exit():s.selectAll(null),a.applyTransition(n,e).style("opacity",0).remove(),s}},940:(t,e,n)=>{"use strict";var r=n(1034),i=n(7584),a=n(8355),o=n(4322);function s(t,e){var n=(o.line||o.svg.line)().x((function(t){return t.x})).y((function(t){return t.y}));return(n.curve||n.interpolate)(t.curve),n(e)}t.exports=function(t,e,n){var c=t.selectAll("g.edgePath").data(e.edges(),(function(t){return a.edgeToId(t)})).classed("update",!0),u=function(t,e){var n=t.enter().append("g").attr("class","edgePath").style("opacity",0);return n.append("path").attr("class","path").attr("d",(function(t){var n=e.edge(t),i=e.node(t.v).elem;return s(n,r.range(n.points.length).map((function(){return e=(t=i).getBBox(),{x:(n=t.ownerSVGElement.getScreenCTM().inverse().multiply(t.getScreenCTM()).translate(e.width/2,e.height/2)).e,y:n.f};var t,e,n})))})),n.append("defs"),n}(c,e);!function(t,e){var n=t.exit();a.applyTransition(n,e).style("opacity",0).remove()}(c,e);var l=void 0!==c.merge?c.merge(u):c;return a.applyTransition(l,e).style("opacity",1),l.each((function(t){var n=o.select(this),r=e.edge(t);r.elem=this,r.id&&n.attr("id",r.id),a.applyClass(n,r.class,(n.classed("update")?"update ":"")+"edgePath")})),l.selectAll("path.path").each((function(t){var n=e.edge(t);n.arrowheadId=r.uniqueId("arrowhead");var c=o.select(this).attr("marker-end",(function(){return"url("+(t=location.href,e=n.arrowheadId,t.split("#")[0]+"#"+e+")");var t,e})).style("fill","none");a.applyTransition(c,e).attr("d",(function(t){return function(t,e){var n=t.edge(e),r=t.node(e.v),a=t.node(e.w),o=n.points.slice(1,n.points.length-1);return o.unshift(i(r,o[0])),o.push(i(a,o[o.length-1])),s(n,o)}(e,t)})),a.applyStyle(c,n.style)})),l.selectAll("defs *").remove(),l.selectAll("defs").each((function(t){var r=e.edge(t);(0,n[r.arrowhead])(o.select(this),r.arrowheadId,r,"arrowhead")})),l}},607:(t,e,n)=>{"use strict";var r=n(1034),i=n(1322),a=n(8355),o=n(4322);t.exports=function(t,e,n){var s,c=e.nodes().filter((function(t){return!a.isSubgraph(e,t)})),u=t.selectAll("g.node").data(c,(function(t){return t})).classed("update",!0);return u.exit().remove(),u.enter().append("g").attr("class","node").style("opacity",0),(u=t.selectAll("g.node")).each((function(t){var s=e.node(t),c=o.select(this);a.applyClass(c,s.class,(c.classed("update")?"update ":"")+"node"),c.select("g.label").remove();var u=c.append("g").attr("class","label"),l=i(u,s),h=n[s.shape],f=r.pick(l.node().getBBox(),"width","height");s.elem=this,s.id&&c.attr("id",s.id),s.labelId&&u.attr("id",s.labelId),r.has(s,"width")&&(f.width=s.width),r.has(s,"height")&&(f.height=s.height),f.width+=s.paddingLeft+s.paddingRight,f.height+=s.paddingTop+s.paddingBottom,u.attr("transform","translate("+(s.paddingLeft-s.paddingRight)/2+","+(s.paddingTop-s.paddingBottom)/2+")");var d=o.select(this);d.select(".label-container").remove();var p=h(d,f,s).classed("label-container",!0);a.applyStyle(p,s.style);var y=p.node().getBBox();s.width=y.width,s.height=y.height})),s=u.exit?u.exit():u.selectAll(null),a.applyTransition(s,e).style("opacity",0).remove(),u}},4322:(t,e,n)=>{var r;if(!r)try{r=n(7188)}catch(t){}r||(r=window.d3),t.exports=r},1463:(t,e,n)=>{var r;try{r=n(681)}catch(t){}r||(r=window.dagre),t.exports=r},6614:(t,e,n)=>{var r;try{r=n(8282)}catch(t){}r||(r=window.graphlib),t.exports=r},8114:(t,e,n)=>{t.exports={node:n(7584),circle:n(6587),ellipse:n(3260),polygon:n(5337),rect:n(8049)}},6587:(t,e,n)=>{var r=n(3260);t.exports=function(t,e,n){return r(t,e,e,n)}},3260:t=>{t.exports=function(t,e,n,r){var i=t.x,a=t.y,o=i-r.x,s=a-r.y,c=Math.sqrt(e*e*s*s+n*n*o*o),u=Math.abs(e*n*o/c);r.x<i&&(u=-u);var l=Math.abs(e*n*s/c);return r.y<a&&(l=-l),{x:i+u,y:a+l}}},6808:t=>{function e(t,e){return t*e>0}t.exports=function(t,n,r,i){var a,o,s,c,u,l,h,f,d,p,y,g,m;if(!(a=n.y-t.y,s=t.x-n.x,u=n.x*t.y-t.x*n.y,d=a*r.x+s*r.y+u,p=a*i.x+s*i.y+u,0!==d&&0!==p&&e(d,p)||(o=i.y-r.y,c=r.x-i.x,l=i.x*r.y-r.x*i.y,h=o*t.x+c*t.y+l,f=o*n.x+c*n.y+l,0!==h&&0!==f&&e(h,f)||0==(y=a*c-o*s))))return g=Math.abs(y/2),{x:(m=s*l-c*u)<0?(m-g)/y:(m+g)/y,y:(m=o*u-a*l)<0?(m-g)/y:(m+g)/y}}},7584:t=>{t.exports=function(t,e){return t.intersect(e)}},5337:(t,e,n)=>{var r=n(6808);t.exports=function(t,e,n){var i=t.x,a=t.y,o=[],s=Number.POSITIVE_INFINITY,c=Number.POSITIVE_INFINITY;e.forEach((function(t){s=Math.min(s,t.x),c=Math.min(c,t.y)}));for(var u=i-t.width/2-s,l=a-t.height/2-c,h=0;h<e.length;h++){var f=e[h],d=e[h<e.length-1?h+1:0],p=r(t,n,{x:u+f.x,y:l+f.y},{x:u+d.x,y:l+d.y});p&&o.push(p)}return o.length?(o.length>1&&o.sort((function(t,e){var r=t.x-n.x,i=t.y-n.y,a=Math.sqrt(r*r+i*i),o=e.x-n.x,s=e.y-n.y,c=Math.sqrt(o*o+s*s);return a<c?-1:a===c?0:1})),o[0]):(console.log("NO INTERSECTION FOUND, RETURN NODE CENTER",t),t)}},8049:t=>{t.exports=function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;return Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=0===s?0:u*o/s,r=u):(o<0&&(c=-c),n=c,r=0===o?0:c*s/o),{x:i+n,y:a+r}}},8284:(t,e,n)=>{var r=n(8355);t.exports=function(t,e){var n=t.append("foreignObject").attr("width","100000"),i=n.append("xhtml:div");i.attr("xmlns","http://www.w3.org/1999/xhtml");var a=e.label;switch(typeof a){case"function":i.insert(a);break;case"object":i.insert((function(){return a}));break;default:i.html(a)}r.applyStyle(i,e.labelStyle),i.style("display","inline-block"),i.style("white-space","nowrap");var o=i.node().getBoundingClientRect();return n.attr("width",o.width).attr("height",o.height),n}},1322:(t,e,n)=>{var r=n(7318),i=n(8284),a=n(8287);t.exports=function(t,e,n){var o=e.label,s=t.append("g");"svg"===e.labelType?a(s,e):"string"!=typeof o||"html"===e.labelType?i(s,e):r(s,e);var c,u=s.node().getBBox();switch(n){case"top":c=-e.height/2;break;case"bottom":c=e.height/2-u.height;break;default:c=-u.height/2}return s.attr("transform","translate("+-u.width/2+","+c+")"),s}},8287:(t,e,n)=>{var r=n(8355);t.exports=function(t,e){var n=t;return n.node().appendChild(e.label),r.applyStyle(n,e.labelStyle),n}},7318:(t,e,n)=>{var r=n(8355);t.exports=function(t,e){for(var n=t.append("text"),i=function(t){for(var e,n="",r=!1,i=0;i<t.length;++i)e=t[i],r?(n+="n"===e?"\n":e,r=!1):"\\"===e?r=!0:n+=e;return n}(e.label).split("\n"),a=0;a<i.length;a++)n.append("tspan").attr("xml:space","preserve").attr("dy","1em").attr("x","1").text(i[a]);return r.applyStyle(n,e.labelStyle),n}},1034:(t,e,n)=>{var r;try{r={defaults:n(1747),each:n(6073),isFunction:n(3560),isPlainObject:n(8630),pick:n(9722),has:n(8721),range:n(6026),uniqueId:n(3955)}}catch(t){}r||(r=window._),t.exports=r},6381:(t,e,n)=>{"use strict";var r=n(8355),i=n(4322);t.exports=function(t,e){var n=t.filter((function(){return!i.select(this).classed("update")}));function a(t){var n=e.node(t);return"translate("+n.x+","+n.y+")"}n.attr("transform",a),r.applyTransition(t,e).style("opacity",1).attr("transform",a),r.applyTransition(n.selectAll("rect"),e).attr("width",(function(t){return e.node(t).width})).attr("height",(function(t){return e.node(t).height})).attr("x",(function(t){return-e.node(t).width/2})).attr("y",(function(t){return-e.node(t).height/2}))}},4577:(t,e,n)=>{"use strict";var r=n(8355),i=n(4322),a=n(1034);t.exports=function(t,e){function n(t){var n=e.edge(t);return a.has(n,"x")?"translate("+n.x+","+n.y+")":""}t.filter((function(){return!i.select(this).classed("update")})).attr("transform",n),r.applyTransition(t,e).style("opacity",1).attr("transform",n)}},4849:(t,e,n)=>{"use strict";var r=n(8355),i=n(4322);t.exports=function(t,e){function n(t){var n=e.node(t);return"translate("+n.x+","+n.y+")"}t.filter((function(){return!i.select(this).classed("update")})).attr("transform",n),r.applyTransition(t,e).style("opacity",1).attr("transform",n)}},5787:(t,e,n)=>{var r=n(1034),i=n(4322),a=n(1463).layout;t.exports=function(){var t=n(607),e=n(5632),i=n(6315),u=n(940),l=n(4849),h=n(4577),f=n(6381),d=n(4418),p=n(9144),y=function(n,y){!function(t){t.nodes().forEach((function(e){var n=t.node(e);r.has(n,"label")||t.children(e).length||(n.label=e),r.has(n,"paddingX")&&r.defaults(n,{paddingLeft:n.paddingX,paddingRight:n.paddingX}),r.has(n,"paddingY")&&r.defaults(n,{paddingTop:n.paddingY,paddingBottom:n.paddingY}),r.has(n,"padding")&&r.defaults(n,{paddingLeft:n.padding,paddingRight:n.padding,paddingTop:n.padding,paddingBottom:n.padding}),r.defaults(n,o),r.each(["paddingLeft","paddingRight","paddingTop","paddingBottom"],(function(t){n[t]=Number(n[t])})),r.has(n,"width")&&(n._prevWidth=n.width),r.has(n,"height")&&(n._prevHeight=n.height)})),t.edges().forEach((function(e){var n=t.edge(e);r.has(n,"label")||(n.label=""),r.defaults(n,s)}))}(y);var g=c(n,"output"),m=c(g,"clusters"),v=c(g,"edgePaths"),b=i(c(g,"edgeLabels"),y),_=t(c(g,"nodes"),y,d);a(y),l(_,y),h(b,y),u(v,y,p);var x=e(m,y);f(x,y),function(t){r.each(t.nodes(),(function(e){var n=t.node(e);r.has(n,"_prevWidth")?n.width=n._prevWidth:delete n.width,r.has(n,"_prevHeight")?n.height=n._prevHeight:delete n.height,delete n._prevWidth,delete n._prevHeight}))}(y)};return y.createNodes=function(e){return arguments.length?(t=e,y):t},y.createClusters=function(t){return arguments.length?(e=t,y):e},y.createEdgeLabels=function(t){return arguments.length?(i=t,y):i},y.createEdgePaths=function(t){return arguments.length?(u=t,y):u},y.shapes=function(t){return arguments.length?(d=t,y):d},y.arrows=function(t){return arguments.length?(p=t,y):p},y};var o={paddingLeft:10,paddingRight:10,paddingTop:10,paddingBottom:10,rx:0,ry:0,shape:"rect"},s={arrowhead:"normal",curve:i.curveLinear};function c(t,e){var n=t.select("g."+e);return n.empty()&&(n=t.append("g").attr("class",e)),n}},4418:(t,e,n)=>{"use strict";var r=n(8049),i=n(3260),a=n(6587),o=n(5337);t.exports={rect:function(t,e,n){var i=t.insert("rect",":first-child").attr("rx",n.rx).attr("ry",n.ry).attr("x",-e.width/2).attr("y",-e.height/2).attr("width",e.width).attr("height",e.height);return n.intersect=function(t){return r(n,t)},i},ellipse:function(t,e,n){var r=e.width/2,a=e.height/2,o=t.insert("ellipse",":first-child").attr("x",-e.width/2).attr("y",-e.height/2).attr("rx",r).attr("ry",a);return n.intersect=function(t){return i(n,r,a,t)},o},circle:function(t,e,n){var r=Math.max(e.width,e.height)/2,i=t.insert("circle",":first-child").attr("x",-e.width/2).attr("y",-e.height/2).attr("r",r);return n.intersect=function(t){return a(n,r,t)},i},diamond:function(t,e,n){var r=e.width*Math.SQRT2/2,i=e.height*Math.SQRT2/2,a=[{x:0,y:-i},{x:-r,y:0},{x:0,y:i},{x:r,y:0}],s=t.insert("polygon",":first-child").attr("points",a.map((function(t){return t.x+","+t.y})).join(" "));return n.intersect=function(t){return o(n,a,t)},s}}},8355:(t,e,n)=>{var r=n(1034);t.exports={isSubgraph:function(t,e){return!!t.children(e).length},edgeToId:function(t){return a(t.v)+":"+a(t.w)+":"+a(t.name)},applyStyle:function(t,e){e&&t.attr("style",e)},applyClass:function(t,e,n){e&&t.attr("class",e).attr("class",n+" "+t.attr("class"))},applyTransition:function(t,e){var n=e.graph();if(r.isPlainObject(n)){var i=n.transition;if(r.isFunction(i))return i(t)}return t}};var i=/:/g;function a(t){return t?String(t).replace(i,"\\:"):""}},5689:t=>{t.exports="0.6.4"},7188:(t,e,n)=>{"use strict";n.r(e),n.d(e,{FormatSpecifier:()=>uc,active:()=>Jr,arc:()=>fx,area:()=>vx,areaRadial:()=>Sx,ascending:()=>i,autoType:()=>Fo,axisBottom:()=>it,axisLeft:()=>at,axisRight:()=>rt,axisTop:()=>nt,bisect:()=>u,bisectLeft:()=>c,bisectRight:()=>s,bisector:()=>a,blob:()=>ms,brush:()=>Ai,brushSelection:()=>Ei,brushX:()=>Ci,brushY:()=>Si,buffer:()=>bs,chord:()=>Fi,clientPoint:()=>Dn,cluster:()=>Sd,color:()=>Ve,contourDensity:()=>oo,contours:()=>to,create:()=>j_,creator:()=>ie,cross:()=>f,csv:()=>Ts,csvFormat:()=>To,csvFormatBody:()=>Eo,csvFormatRow:()=>So,csvFormatRows:()=>Co,csvFormatValue:()=>Ao,csvParse:()=>wo,csvParseRows:()=>ko,cubehelix:()=>Ha,curveBasis:()=>sw,curveBasisClosed:()=>uw,curveBasisOpen:()=>hw,curveBundle:()=>dw,curveCardinal:()=>gw,curveCardinalClosed:()=>vw,curveCardinalOpen:()=>_w,curveCatmullRom:()=>kw,curveCatmullRomClosed:()=>Ew,curveCatmullRomOpen:()=>Sw,curveLinear:()=>px,curveLinearClosed:()=>Mw,curveMonotoneX:()=>Fw,curveMonotoneY:()=>Pw,curveNatural:()=>zw,curveStep:()=>qw,curveStepAfter:()=>$w,curveStepBefore:()=>Hw,customEvent:()=>ge,descending:()=>d,deviation:()=>g,dispatch:()=>ft,drag:()=>po,dragDisable:()=>Se,dragEnable:()=>Ae,dsv:()=>ks,dsvFormat:()=>_o,easeBack:()=>hs,easeBackIn:()=>us,easeBackInOut:()=>hs,easeBackOut:()=>ls,easeBounce:()=>os,easeBounceIn:()=>as,easeBounceInOut:()=>ss,easeBounceOut:()=>os,easeCircle:()=>rs,easeCircleIn:()=>es,easeCircleInOut:()=>rs,easeCircleOut:()=>ns,easeCubic:()=>Xr,easeCubicIn:()=>Vr,easeCubicInOut:()=>Xr,easeCubicOut:()=>Gr,easeElastic:()=>ps,easeElasticIn:()=>ds,easeElasticInOut:()=>ys,easeElasticOut:()=>ps,easeExp:()=>ts,easeExpIn:()=>Ko,easeExpInOut:()=>ts,easeExpOut:()=>Jo,easeLinear:()=>jo,easePoly:()=>$o,easePolyIn:()=>qo,easePolyInOut:()=>$o,easePolyOut:()=>Ho,easeQuad:()=>Uo,easeQuadIn:()=>Yo,easeQuadInOut:()=>Uo,easeQuadOut:()=>zo,easeSin:()=>Zo,easeSinIn:()=>Go,easeSinInOut:()=>Zo,easeSinOut:()=>Xo,entries:()=>pa,event:()=>le,extent:()=>m,forceCenter:()=>Bs,forceCollide:()=>Ws,forceLink:()=>Xs,forceManyBody:()=>tc,forceRadial:()=>ec,forceSimulation:()=>Js,forceX:()=>nc,forceY:()=>rc,format:()=>pc,formatDefaultLocale:()=>bc,formatLocale:()=>vc,formatPrefix:()=>yc,formatSpecifier:()=>cc,geoAlbers:()=>Uf,geoAlbersUsa:()=>qf,geoArea:()=>yu,geoAzimuthalEqualArea:()=>Vf,geoAzimuthalEqualAreaRaw:()=>Wf,geoAzimuthalEquidistant:()=>Xf,geoAzimuthalEquidistantRaw:()=>Gf,geoBounds:()=>sl,geoCentroid:()=>bl,geoCircle:()=>Nl,geoClipAntimeridian:()=>Ul,geoClipCircle:()=>ql,geoClipExtent:()=>Vl,geoClipRectangle:()=>Wl,geoConicConformal:()=>ed,geoConicConformalRaw:()=>td,geoConicEqualArea:()=>zf,geoConicEqualAreaRaw:()=>Yf,geoConicEquidistant:()=>ad,geoConicEquidistantRaw:()=>id,geoContains:()=>ph,geoDistance:()=>ah,geoEqualEarth:()=>fd,geoEqualEarthRaw:()=>hd,geoEquirectangular:()=>rd,geoEquirectangularRaw:()=>nd,geoGnomonic:()=>pd,geoGnomonicRaw:()=>dd,geoGraticule:()=>mh,geoGraticule10:()=>vh,geoIdentity:()=>yd,geoInterpolate:()=>bh,geoLength:()=>nh,geoMercator:()=>Qf,geoMercatorRaw:()=>Zf,geoNaturalEarth1:()=>md,geoNaturalEarth1Raw:()=>gd,geoOrthographic:()=>bd,geoOrthographicRaw:()=>vd,geoPath:()=>kf,geoProjection:()=>Ff,geoProjectionMutator:()=>Pf,geoRotation:()=>Sl,geoStereographic:()=>xd,geoStereographicRaw:()=>_d,geoStream:()=>nu,geoTransform:()=>Tf,geoTransverseMercator:()=>kd,geoTransverseMercatorRaw:()=>wd,gray:()=>ka,hcl:()=>Oa,hierarchy:()=>Md,histogram:()=>D,hsl:()=>an,html:()=>Ds,image:()=>Cs,interpolate:()=>Mn,interpolateArray:()=>xn,interpolateBasis:()=>un,interpolateBasisClosed:()=>ln,interpolateBlues:()=>f_,interpolateBrBG:()=>Tb,interpolateBuGn:()=>Ub,interpolateBuPu:()=>Hb,interpolateCividis:()=>k_,interpolateCool:()=>C_,interpolateCubehelix:()=>zp,interpolateCubehelixDefault:()=>T_,interpolateCubehelixLong:()=>Up,interpolateDate:()=>kn,interpolateDiscrete:()=>Sp,interpolateGnBu:()=>Wb,interpolateGreens:()=>p_,interpolateGreys:()=>g_,interpolateHcl:()=>Pp,interpolateHclLong:()=>jp,interpolateHsl:()=>Lp,interpolateHslLong:()=>Ip,interpolateHue:()=>Ap,interpolateInferno:()=>F_,interpolateLab:()=>Rp,interpolateMagma:()=>R_,interpolateNumber:()=>Tn,interpolateNumberArray:()=>bn,interpolateObject:()=>En,interpolateOrRd:()=>Gb,interpolateOranges:()=>w_,interpolatePRGn:()=>Cb,interpolatePiYG:()=>Ab,interpolatePlasma:()=>P_,interpolatePuBu:()=>Kb,interpolatePuBuGn:()=>Zb,interpolatePuOr:()=>Nb,interpolatePuRd:()=>t_,interpolatePurples:()=>v_,interpolateRainbow:()=>A_,interpolateRdBu:()=>Ob,interpolateRdGy:()=>Lb,interpolateRdPu:()=>n_,interpolateRdYlBu:()=>Rb,interpolateRdYlGn:()=>Pb,interpolateReds:()=>__,interpolateRgb:()=>yn,interpolateRgbBasis:()=>mn,interpolateRgbBasisClosed:()=>vn,interpolateRound:()=>Mp,interpolateSinebow:()=>O_,interpolateSpectral:()=>Yb,interpolateString:()=>An,interpolateTransformCss:()=>pr,interpolateTransformSvg:()=>yr,interpolateTurbo:()=>B_,interpolateViridis:()=>I_,interpolateWarm:()=>E_,interpolateYlGn:()=>o_,interpolateYlGnBu:()=>i_,interpolateYlOrBr:()=>c_,interpolateYlOrRd:()=>l_,interpolateZoom:()=>Op,interrupt:()=>ar,interval:()=>fk,isoFormat:()=>uk,isoParse:()=>hk,json:()=>As,keys:()=>fa,lab:()=>Ta,lch:()=>Da,line:()=>mx,lineRadial:()=>Cx,linkHorizontal:()=>Rx,linkRadial:()=>Px,linkVertical:()=>Fx,local:()=>z_,map:()=>na,matcher:()=>mt,max:()=>I,mean:()=>R,median:()=>F,merge:()=>P,min:()=>j,mouse:()=>Bn,namespace:()=>Et,namespaces:()=>Tt,nest:()=>ra,now:()=>Hn,pack:()=>tp,packEnclose:()=>Id,packSiblings:()=>Gd,pairs:()=>l,partition:()=>op,path:()=>Wi,permute:()=>Y,pie:()=>xx,piecewise:()=>qp,pointRadial:()=>Ax,polygonArea:()=>$p,polygonCentroid:()=>Wp,polygonContains:()=>Qp,polygonHull:()=>Zp,polygonLength:()=>Kp,precisionFixed:()=>_c,precisionPrefix:()=>xc,precisionRound:()=>wc,quadtree:()=>Ys,quantile:()=>O,quantize:()=>Hp,radialArea:()=>Sx,radialLine:()=>Cx,randomBates:()=>iy,randomExponential:()=>ay,randomIrwinHall:()=>ry,randomLogNormal:()=>ny,randomNormal:()=>ey,randomUniform:()=>ty,range:()=>k,rgb:()=>Qe,ribbon:()=>Ki,scaleBand:()=>dy,scaleDiverging:()=>ob,scaleDivergingLog:()=>sb,scaleDivergingPow:()=>ub,scaleDivergingSqrt:()=>lb,scaleDivergingSymlog:()=>cb,scaleIdentity:()=>My,scaleImplicit:()=>hy,scaleLinear:()=>Ay,scaleLog:()=>Py,scaleOrdinal:()=>fy,scalePoint:()=>yy,scalePow:()=>Vy,scaleQuantile:()=>Xy,scaleQuantize:()=>Zy,scaleSequential:()=>Jv,scaleSequentialLog:()=>tb,scaleSequentialPow:()=>nb,scaleSequentialQuantile:()=>ib,scaleSequentialSqrt:()=>rb,scaleSequentialSymlog:()=>eb,scaleSqrt:()=>Gy,scaleSymlog:()=>Uy,scaleThreshold:()=>Qy,scaleTime:()=>Yv,scaleUtc:()=>Zv,scan:()=>z,schemeAccent:()=>db,schemeBlues:()=>h_,schemeBrBG:()=>kb,schemeBuGn:()=>zb,schemeBuPu:()=>qb,schemeCategory10:()=>fb,schemeDark2:()=>pb,schemeGnBu:()=>$b,schemeGreens:()=>d_,schemeGreys:()=>y_,schemeOrRd:()=>Vb,schemeOranges:()=>x_,schemePRGn:()=>Eb,schemePaired:()=>yb,schemePastel1:()=>gb,schemePastel2:()=>mb,schemePiYG:()=>Sb,schemePuBu:()=>Qb,schemePuBuGn:()=>Xb,schemePuOr:()=>Mb,schemePuRd:()=>Jb,schemePurples:()=>m_,schemeRdBu:()=>Db,schemeRdGy:()=>Bb,schemeRdPu:()=>e_,schemeRdYlBu:()=>Ib,schemeRdYlGn:()=>Fb,schemeReds:()=>b_,schemeSet1:()=>vb,schemeSet2:()=>bb,schemeSet3:()=>_b,schemeSpectral:()=>jb,schemeTableau10:()=>xb,schemeYlGn:()=>a_,schemeYlGnBu:()=>r_,schemeYlOrBr:()=>s_,schemeYlOrRd:()=>u_,select:()=>Te,selectAll:()=>q_,selection:()=>ke,selector:()=>pt,selectorAll:()=>gt,set:()=>ha,shuffle:()=>U,stack:()=>Xw,stackOffsetDiverging:()=>Qw,stackOffsetExpand:()=>Zw,stackOffsetNone:()=>Ww,stackOffsetSilhouette:()=>Kw,stackOffsetWiggle:()=>Jw,stackOrderAppearance:()=>tk,stackOrderAscending:()=>nk,stackOrderDescending:()=>ik,stackOrderInsideOut:()=>ak,stackOrderNone:()=>Vw,stackOrderReverse:()=>ok,stratify:()=>hp,style:()=>Rt,sum:()=>q,svg:()=>Os,symbol:()=>rw,symbolCircle:()=>jx,symbolCross:()=>Yx,symbolDiamond:()=>qx,symbolSquare:()=>Gx,symbolStar:()=>Vx,symbolTriangle:()=>Zx,symbolWye:()=>ew,symbols:()=>nw,text:()=>xs,thresholdFreedmanDiaconis:()=>B,thresholdScott:()=>L,thresholdSturges:()=>N,tickFormat:()=>Cy,tickIncrement:()=>A,tickStep:()=>M,ticks:()=>S,timeDay:()=>Ag,timeDays:()=>Mg,timeFormat:()=>pm,timeFormatDefaultLocale:()=>Iv,timeFormatLocale:()=>fm,timeFriday:()=>vg,timeFridays:()=>Eg,timeHour:()=>Dg,timeHours:()=>Og,timeInterval:()=>tg,timeMillisecond:()=>Yg,timeMilliseconds:()=>zg,timeMinute:()=>Lg,timeMinutes:()=>Ig,timeMonday:()=>pg,timeMondays:()=>xg,timeMonth:()=>ag,timeMonths:()=>og,timeParse:()=>ym,timeSaturday:()=>bg,timeSaturdays:()=>Cg,timeSecond:()=>Fg,timeSeconds:()=>Pg,timeSunday:()=>dg,timeSundays:()=>_g,timeThursday:()=>mg,timeThursdays:()=>Tg,timeTuesday:()=>yg,timeTuesdays:()=>wg,timeWednesday:()=>gg,timeWednesdays:()=>kg,timeWeek:()=>dg,timeWeeks:()=>_g,timeYear:()=>ng,timeYears:()=>rg,timeout:()=>Kn,timer:()=>Vn,timerFlush:()=>Gn,touch:()=>On,touches:()=>H_,transition:()=>Hr,transpose:()=>H,tree:()=>vp,treemap:()=>kp,treemapBinary:()=>Tp,treemapDice:()=>ap,treemapResquarify:()=>Cp,treemapSlice:()=>bp,treemapSliceDice:()=>Ep,treemapSquarify:()=>wp,tsv:()=>Es,tsvFormat:()=>Oo,tsvFormatBody:()=>Bo,tsvFormatRow:()=>Io,tsvFormatRows:()=>Lo,tsvFormatValue:()=>Ro,tsvParse:()=>No,tsvParseRows:()=>Do,utcDay:()=>im,utcDays:()=>am,utcFormat:()=>gm,utcFriday:()=>Gg,utcFridays:()=>em,utcHour:()=>$v,utcHours:()=>Wv,utcMillisecond:()=>Yg,utcMilliseconds:()=>zg,utcMinute:()=>Gv,utcMinutes:()=>Xv,utcMonday:()=>Hg,utcMondays:()=>Qg,utcMonth:()=>Uv,utcMonths:()=>qv,utcParse:()=>mm,utcSaturday:()=>Xg,utcSaturdays:()=>nm,utcSecond:()=>Fg,utcSeconds:()=>Pg,utcSunday:()=>qg,utcSundays:()=>Zg,utcThursday:()=>Vg,utcThursdays:()=>tm,utcTuesday:()=>$g,utcTuesdays:()=>Kg,utcWednesday:()=>Wg,utcWednesdays:()=>Jg,utcWeek:()=>qg,utcWeeks:()=>Zg,utcYear:()=>sm,utcYears:()=>cm,values:()=>da,variance:()=>y,version:()=>r,voronoi:()=>Kk,window:()=>Ot,xml:()=>Ns,zip:()=>W,zoom:()=>fT,zoomIdentity:()=>nT,zoomTransform:()=>rT});var r="5.16.0";function i(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}function a(t){var e;return 1===t.length&&(e=t,t=function(t,n){return i(e(t),n)}),{left:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r<i;){var a=r+i>>>1;t(e[a],n)<0?r=a+1:i=a}return r},right:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r<i;){var a=r+i>>>1;t(e[a],n)>0?i=a:r=a+1}return r}}}var o=a(i),s=o.right,c=o.left;const u=s;function l(t,e){null==e&&(e=h);for(var n=0,r=t.length-1,i=t[0],a=new Array(r<0?0:r);n<r;)a[n]=e(i,i=t[++n]);return a}function h(t,e){return[t,e]}function f(t,e,n){var r,i,a,o,s=t.length,c=e.length,u=new Array(s*c);for(null==n&&(n=h),r=a=0;r<s;++r)for(o=t[r],i=0;i<c;++i,++a)u[a]=n(o,e[i]);return u}function d(t,e){return e<t?-1:e>t?1:e>=t?0:NaN}function p(t){return null===t?NaN:+t}function y(t,e){var n,r,i=t.length,a=0,o=-1,s=0,c=0;if(null==e)for(;++o<i;)isNaN(n=p(t[o]))||(c+=(r=n-s)*(n-(s+=r/++a)));else for(;++o<i;)isNaN(n=p(e(t[o],o,t)))||(c+=(r=n-s)*(n-(s+=r/++a)));if(a>1)return c/(a-1)}function g(t,e){var n=y(t,e);return n?Math.sqrt(n):n}function m(t,e){var n,r,i,a=t.length,o=-1;if(null==e){for(;++o<a;)if(null!=(n=t[o])&&n>=n)for(r=i=n;++o<a;)null!=(n=t[o])&&(r>n&&(r=n),i<n&&(i=n))}else for(;++o<a;)if(null!=(n=e(t[o],o,t))&&n>=n)for(r=i=n;++o<a;)null!=(n=e(t[o],o,t))&&(r>n&&(r=n),i<n&&(i=n));return[r,i]}var v=Array.prototype,b=v.slice,_=v.map;function x(t){return function(){return t}}function w(t){return t}function k(t,e,n){t=+t,e=+e,n=(i=arguments.length)<2?(e=t,t=0,1):i<3?1:+n;for(var r=-1,i=0|Math.max(0,Math.ceil((e-t)/n)),a=new Array(i);++r<i;)a[r]=t+r*n;return a}var T=Math.sqrt(50),E=Math.sqrt(10),C=Math.sqrt(2);function S(t,e,n){var r,i,a,o,s=-1;if(n=+n,(t=+t)==(e=+e)&&n>0)return[t];if((r=e<t)&&(i=t,t=e,e=i),0===(o=A(t,e,n))||!isFinite(o))return[];if(o>0)for(t=Math.ceil(t/o),e=Math.floor(e/o),a=new Array(i=Math.ceil(e-t+1));++s<i;)a[s]=(t+s)*o;else for(t=Math.floor(t*o),e=Math.ceil(e*o),a=new Array(i=Math.ceil(t-e+1));++s<i;)a[s]=(t-s)/o;return r&&a.reverse(),a}function A(t,e,n){var r=(e-t)/Math.max(0,n),i=Math.floor(Math.log(r)/Math.LN10),a=r/Math.pow(10,i);return i>=0?(a>=T?10:a>=E?5:a>=C?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(a>=T?10:a>=E?5:a>=C?2:1)}function M(t,e,n){var r=Math.abs(e-t)/Math.max(0,n),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),a=r/i;return a>=T?i*=10:a>=E?i*=5:a>=C&&(i*=2),e<t?-i:i}function N(t){return Math.ceil(Math.log(t.length)/Math.LN2)+1}function D(){var t=w,e=m,n=N;function r(r){var i,a,o=r.length,s=new Array(o);for(i=0;i<o;++i)s[i]=t(r[i],i,r);var c=e(s),l=c[0],h=c[1],f=n(s,l,h);Array.isArray(f)||(f=M(l,h,f),f=k(Math.ceil(l/f)*f,h,f));for(var d=f.length;f[0]<=l;)f.shift(),--d;for(;f[d-1]>h;)f.pop(),--d;var p,y=new Array(d+1);for(i=0;i<=d;++i)(p=y[i]=[]).x0=i>0?f[i-1]:l,p.x1=i<d?f[i]:h;for(i=0;i<o;++i)l<=(a=s[i])&&a<=h&&y[u(f,a,0,d)].push(r[i]);return y}return r.value=function(e){return arguments.length?(t="function"==typeof e?e:x(e),r):t},r.domain=function(t){return arguments.length?(e="function"==typeof t?t:x([t[0],t[1]]),r):e},r.thresholds=function(t){return arguments.length?(n="function"==typeof t?t:Array.isArray(t)?x(b.call(t)):x(t),r):n},r}function O(t,e,n){if(null==n&&(n=p),r=t.length){if((e=+e)<=0||r<2)return+n(t[0],0,t);if(e>=1)return+n(t[r-1],r-1,t);var r,i=(r-1)*e,a=Math.floor(i),o=+n(t[a],a,t);return o+(+n(t[a+1],a+1,t)-o)*(i-a)}}function B(t,e,n){return t=_.call(t,p).sort(i),Math.ceil((n-e)/(2*(O(t,.75)-O(t,.25))*Math.pow(t.length,-1/3)))}function L(t,e,n){return Math.ceil((n-e)/(3.5*g(t)*Math.pow(t.length,-1/3)))}function I(t,e){var n,r,i=t.length,a=-1;if(null==e){for(;++a<i;)if(null!=(n=t[a])&&n>=n)for(r=n;++a<i;)null!=(n=t[a])&&n>r&&(r=n)}else for(;++a<i;)if(null!=(n=e(t[a],a,t))&&n>=n)for(r=n;++a<i;)null!=(n=e(t[a],a,t))&&n>r&&(r=n);return r}function R(t,e){var n,r=t.length,i=r,a=-1,o=0;if(null==e)for(;++a<r;)isNaN(n=p(t[a]))?--i:o+=n;else for(;++a<r;)isNaN(n=p(e(t[a],a,t)))?--i:o+=n;if(i)return o/i}function F(t,e){var n,r=t.length,a=-1,o=[];if(null==e)for(;++a<r;)isNaN(n=p(t[a]))||o.push(n);else for(;++a<r;)isNaN(n=p(e(t[a],a,t)))||o.push(n);return O(o.sort(i),.5)}function P(t){for(var e,n,r,i=t.length,a=-1,o=0;++a<i;)o+=t[a].length;for(n=new Array(o);--i>=0;)for(e=(r=t[i]).length;--e>=0;)n[--o]=r[e];return n}function j(t,e){var n,r,i=t.length,a=-1;if(null==e){for(;++a<i;)if(null!=(n=t[a])&&n>=n)for(r=n;++a<i;)null!=(n=t[a])&&r>n&&(r=n)}else for(;++a<i;)if(null!=(n=e(t[a],a,t))&&n>=n)for(r=n;++a<i;)null!=(n=e(t[a],a,t))&&r>n&&(r=n);return r}function Y(t,e){for(var n=e.length,r=new Array(n);n--;)r[n]=t[e[n]];return r}function z(t,e){if(n=t.length){var n,r,a=0,o=0,s=t[o];for(null==e&&(e=i);++a<n;)(e(r=t[a],s)<0||0!==e(s,s))&&(s=r,o=a);return 0===e(s,s)?o:void 0}}function U(t,e,n){for(var r,i,a=(null==n?t.length:n)-(e=null==e?0:+e);a;)i=Math.random()*a--|0,r=t[a+e],t[a+e]=t[i+e],t[i+e]=r;return t}function q(t,e){var n,r=t.length,i=-1,a=0;if(null==e)for(;++i<r;)(n=+t[i])&&(a+=n);else for(;++i<r;)(n=+e(t[i],i,t))&&(a+=n);return a}function H(t){if(!(i=t.length))return[];for(var e=-1,n=j(t,$),r=new Array(n);++e<n;)for(var i,a=-1,o=r[e]=new Array(i);++a<i;)o[a]=t[a][e];return r}function $(t){return t.length}function W(){return H(arguments)}var V=Array.prototype.slice;function G(t){return t}var X=1e-6;function Z(t){return"translate("+(t+.5)+",0)"}function Q(t){return"translate(0,"+(t+.5)+")"}function K(t){return function(e){return+t(e)}}function J(t){var e=Math.max(0,t.bandwidth()-1)/2;return t.round()&&(e=Math.round(e)),function(n){return+t(n)+e}}function tt(){return!this.__axis}function et(t,e){var n=[],r=null,i=null,a=6,o=6,s=3,c=1===t||4===t?-1:1,u=4===t||2===t?"x":"y",l=1===t||3===t?Z:Q;function h(h){var f=null==r?e.ticks?e.ticks.apply(e,n):e.domain():r,d=null==i?e.tickFormat?e.tickFormat.apply(e,n):G:i,p=Math.max(a,0)+s,y=e.range(),g=+y[0]+.5,m=+y[y.length-1]+.5,v=(e.bandwidth?J:K)(e.copy()),b=h.selection?h.selection():h,_=b.selectAll(".domain").data([null]),x=b.selectAll(".tick").data(f,e).order(),w=x.exit(),k=x.enter().append("g").attr("class","tick"),T=x.select("line"),E=x.select("text");_=_.merge(_.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),x=x.merge(k),T=T.merge(k.append("line").attr("stroke","currentColor").attr(u+"2",c*a)),E=E.merge(k.append("text").attr("fill","currentColor").attr(u,c*p).attr("dy",1===t?"0em":3===t?"0.71em":"0.32em")),h!==b&&(_=_.transition(h),x=x.transition(h),T=T.transition(h),E=E.transition(h),w=w.transition(h).attr("opacity",X).attr("transform",(function(t){return isFinite(t=v(t))?l(t):this.getAttribute("transform")})),k.attr("opacity",X).attr("transform",(function(t){var e=this.parentNode.__axis;return l(e&&isFinite(e=e(t))?e:v(t))}))),w.remove(),_.attr("d",4===t||2==t?o?"M"+c*o+","+g+"H0.5V"+m+"H"+c*o:"M0.5,"+g+"V"+m:o?"M"+g+","+c*o+"V0.5H"+m+"V"+c*o:"M"+g+",0.5H"+m),x.attr("opacity",1).attr("transform",(function(t){return l(v(t))})),T.attr(u+"2",c*a),E.attr(u,c*p).text(d),b.filter(tt).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",2===t?"start":4===t?"end":"middle"),b.each((function(){this.__axis=v}))}return h.scale=function(t){return arguments.length?(e=t,h):e},h.ticks=function(){return n=V.call(arguments),h},h.tickArguments=function(t){return arguments.length?(n=null==t?[]:V.call(t),h):n.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:V.call(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(a=o=+t,h):a},h.tickSizeInner=function(t){return arguments.length?(a=+t,h):a},h.tickSizeOuter=function(t){return arguments.length?(o=+t,h):o},h.tickPadding=function(t){return arguments.length?(s=+t,h):s},h}function nt(t){return et(1,t)}function rt(t){return et(2,t)}function it(t){return et(3,t)}function at(t){return et(4,t)}var ot={value:function(){}};function st(){for(var t,e=0,n=arguments.length,r={};e<n;++e){if(!(t=arguments[e]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new ct(r)}function ct(t){this._=t}function ut(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function lt(t,e){for(var n,r=0,i=t.length;r<i;++r)if((n=t[r]).name===e)return n.value}function ht(t,e,n){for(var r=0,i=t.length;r<i;++r)if(t[r].name===e){t[r]=ot,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=n&&t.push({name:e,value:n}),t}ct.prototype=st.prototype={constructor:ct,on:function(t,e){var n,r=this._,i=ut(t+"",r),a=-1,o=i.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++a<o;)if(n=(t=i[a]).type)r[n]=ht(r[n],t.name,e);else if(null==e)for(n in r)r[n]=ht(r[n],t.name,null);return this}for(;++a<o;)if((n=(t=i[a]).type)&&(n=lt(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new ct(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,i=new Array(n),a=0;a<n;++a)i[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,i)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,a=r.length;i<a;++i)r[i].value.apply(e,n)}};const ft=st;function dt(){}function pt(t){return null==t?dt:function(){return this.querySelector(t)}}function yt(){return[]}function gt(t){return null==t?yt:function(){return this.querySelectorAll(t)}}function mt(t){return function(){return this.matches(t)}}function vt(t){return new Array(t.length)}function bt(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}function _t(t,e,n,r,i,a){for(var o,s=0,c=e.length,u=a.length;s<u;++s)(o=e[s])?(o.__data__=a[s],r[s]=o):n[s]=new bt(t,a[s]);for(;s<c;++s)(o=e[s])&&(i[s]=o)}function xt(t,e,n,r,i,a,o){var s,c,u,l={},h=e.length,f=a.length,d=new Array(h);for(s=0;s<h;++s)(c=e[s])&&(d[s]=u="$"+o.call(c,c.__data__,s,e),u in l?i[s]=c:l[u]=c);for(s=0;s<f;++s)(c=l[u="$"+o.call(t,a[s],s,a)])?(r[s]=c,c.__data__=a[s],l[u]=null):n[s]=new bt(t,a[s]);for(s=0;s<h;++s)(c=e[s])&&l[d[s]]===c&&(i[s]=c)}function wt(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}bt.prototype={constructor:bt,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,e){return this._parent.insertBefore(t,e)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var kt="http://www.w3.org/1999/xhtml";const Tt={svg:"http://www.w3.org/2000/svg",xhtml:kt,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function Et(t){var e=t+="",n=e.indexOf(":");return n>=0&&"xmlns"!==(e=t.slice(0,n))&&(t=t.slice(n+1)),Tt.hasOwnProperty(e)?{space:Tt[e],local:t}:t}function Ct(t){return function(){this.removeAttribute(t)}}function St(t){return function(){this.removeAttributeNS(t.space,t.local)}}function At(t,e){return function(){this.setAttribute(t,e)}}function Mt(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function Nt(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}}function Dt(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}function Ot(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function Bt(t){return function(){this.style.removeProperty(t)}}function Lt(t,e,n){return function(){this.style.setProperty(t,e,n)}}function It(t,e,n){return function(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}}function Rt(t,e){return t.style.getPropertyValue(e)||Ot(t).getComputedStyle(t,null).getPropertyValue(e)}function Ft(t){return function(){delete this[t]}}function Pt(t,e){return function(){this[t]=e}}function jt(t,e){return function(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}}function Yt(t){return t.trim().split(/^|\s+/)}function zt(t){return t.classList||new Ut(t)}function Ut(t){this._node=t,this._names=Yt(t.getAttribute("class")||"")}function qt(t,e){for(var n=zt(t),r=-1,i=e.length;++r<i;)n.add(e[r])}function Ht(t,e){for(var n=zt(t),r=-1,i=e.length;++r<i;)n.remove(e[r])}function $t(t){return function(){qt(this,t)}}function Wt(t){return function(){Ht(this,t)}}function Vt(t,e){return function(){(e.apply(this,arguments)?qt:Ht)(this,t)}}function Gt(){this.textContent=""}function Xt(t){return function(){this.textContent=t}}function Zt(t){return function(){var e=t.apply(this,arguments);this.textContent=null==e?"":e}}function Qt(){this.innerHTML=""}function Kt(t){return function(){this.innerHTML=t}}function Jt(t){return function(){var e=t.apply(this,arguments);this.innerHTML=null==e?"":e}}function te(){this.nextSibling&&this.parentNode.appendChild(this)}function ee(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function ne(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===kt&&e.documentElement.namespaceURI===kt?e.createElement(t):e.createElementNS(n,t)}}function re(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function ie(t){var e=Et(t);return(e.local?re:ne)(e)}function ae(){return null}function oe(){var t=this.parentNode;t&&t.removeChild(this)}function se(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function ce(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}Ut.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var ue={},le=null;function he(t,e,n){return t=fe(t,e,n),function(e){var n=e.relatedTarget;n&&(n===this||8&n.compareDocumentPosition(this))||t.call(this,e)}}function fe(t,e,n){return function(r){var i=le;le=r;try{t.call(this,this.__data__,e,n)}finally{le=i}}}function de(t){return t.trim().split(/^|\s+/).map((function(t){var e="",n=t.indexOf(".");return n>=0&&(e=t.slice(n+1),t=t.slice(0,n)),{type:t,name:e}}))}function pe(t){return function(){var e=this.__on;if(e){for(var n,r=0,i=-1,a=e.length;r<a;++r)n=e[r],t.type&&n.type!==t.type||n.name!==t.name?e[++i]=n:this.removeEventListener(n.type,n.listener,n.capture);++i?e.length=i:delete this.__on}}}function ye(t,e,n){var r=ue.hasOwnProperty(t.type)?he:fe;return function(i,a,o){var s,c=this.__on,u=r(e,a,o);if(c)for(var l=0,h=c.length;l<h;++l)if((s=c[l]).type===t.type&&s.name===t.name)return this.removeEventListener(s.type,s.listener,s.capture),this.addEventListener(s.type,s.listener=u,s.capture=n),void(s.value=e);this.addEventListener(t.type,u,n),s={type:t.type,name:t.name,value:e,listener:u,capture:n},c?c.push(s):this.__on=[s]}}function ge(t,e,n,r){var i=le;t.sourceEvent=le,le=t;try{return e.apply(n,r)}finally{le=i}}function me(t,e,n){var r=Ot(t),i=r.CustomEvent;"function"==typeof i?i=new i(e,n):(i=r.document.createEvent("Event"),n?(i.initEvent(e,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function ve(t,e){return function(){return me(this,t,e)}}function be(t,e){return function(){return me(this,t,e.apply(this,arguments))}}"undefined"!=typeof document&&("onmouseenter"in document.documentElement||(ue={mouseenter:"mouseover",mouseleave:"mouseout"}));var _e=[null];function xe(t,e){this._groups=t,this._parents=e}function we(){return new xe([[document.documentElement]],_e)}xe.prototype=we.prototype={constructor:xe,select:function(t){"function"!=typeof t&&(t=pt(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o,s=e[i],c=s.length,u=r[i]=new Array(c),l=0;l<c;++l)(a=s[l])&&(o=t.call(a,a.__data__,l,s))&&("__data__"in a&&(o.__data__=a.__data__),u[l]=o);return new xe(r,this._parents)},selectAll:function(t){"function"!=typeof t&&(t=gt(t));for(var e=this._groups,n=e.length,r=[],i=[],a=0;a<n;++a)for(var o,s=e[a],c=s.length,u=0;u<c;++u)(o=s[u])&&(r.push(t.call(o,o.__data__,u,s)),i.push(o));return new xe(r,i)},filter:function(t){"function"!=typeof t&&(t=mt(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new xe(r,this._parents)},data:function(t,e){if(!t)return p=new Array(this.size()),l=-1,this.each((function(t){p[++l]=t})),p;var n,r=e?xt:_t,i=this._parents,a=this._groups;"function"!=typeof t&&(n=t,t=function(){return n});for(var o=a.length,s=new Array(o),c=new Array(o),u=new Array(o),l=0;l<o;++l){var h=i[l],f=a[l],d=f.length,p=t.call(h,h&&h.__data__,l,i),y=p.length,g=c[l]=new Array(y),m=s[l]=new Array(y);r(h,f,g,m,u[l]=new Array(d),p,e);for(var v,b,_=0,x=0;_<y;++_)if(v=g[_]){for(_>=x&&(x=_+1);!(b=m[x])&&++x<y;);v._next=b||null}}return(s=new xe(s,i))._enter=c,s._exit=u,s},enter:function(){return new xe(this._enter||this._groups.map(vt),this._parents)},exit:function(){return new xe(this._exit||this._groups.map(vt),this._parents)},join:function(t,e,n){var r=this.enter(),i=this,a=this.exit();return r="function"==typeof t?t(r):r.append(t+""),null!=e&&(i=e(i)),null==n?a.remove():n(a),r&&i?r.merge(i).order():i},merge:function(t){for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new xe(o,this._parents)},order:function(){for(var t=this._groups,e=-1,n=t.length;++e<n;)for(var r,i=t[e],a=i.length-1,o=i[a];--a>=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function e(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}t||(t=wt);for(var n=this._groups,r=n.length,i=new Array(r),a=0;a<r;++a){for(var o,s=n[a],c=s.length,u=i[a]=new Array(c),l=0;l<c;++l)(o=s[l])&&(u[l]=o);u.sort(e)}return new xe(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){var t=new Array(this.size()),e=-1;return this.each((function(){t[++e]=this})),t},node:function(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r=t[e],i=0,a=r.length;i<a;++i){var o=r[i];if(o)return o}return null},size:function(){var t=0;return this.each((function(){++t})),t},empty:function(){return!this.node()},each:function(t){for(var e=this._groups,n=0,r=e.length;n<r;++n)for(var i,a=e[n],o=0,s=a.length;o<s;++o)(i=a[o])&&t.call(i,i.__data__,o,a);return this},attr:function(t,e){var n=Et(t);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((null==e?n.local?St:Ct:"function"==typeof e?n.local?Dt:Nt:n.local?Mt:At)(n,e))},style:function(t,e,n){return arguments.length>1?this.each((null==e?Bt:"function"==typeof e?It:Lt)(t,e,null==n?"":n)):Rt(this.node(),t)},property:function(t,e){return arguments.length>1?this.each((null==e?Ft:"function"==typeof e?jt:Pt)(t,e)):this.node()[t]},classed:function(t,e){var n=Yt(t+"");if(arguments.length<2){for(var r=zt(this.node()),i=-1,a=n.length;++i<a;)if(!r.contains(n[i]))return!1;return!0}return this.each(("function"==typeof e?Vt:e?$t:Wt)(n,e))},text:function(t){return arguments.length?this.each(null==t?Gt:("function"==typeof t?Zt:Xt)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?Qt:("function"==typeof t?Jt:Kt)(t)):this.node().innerHTML},raise:function(){return this.each(te)},lower:function(){return this.each(ee)},append:function(t){var e="function"==typeof t?t:ie(t);return this.select((function(){return this.appendChild(e.apply(this,arguments))}))},insert:function(t,e){var n="function"==typeof t?t:ie(t),r=null==e?ae:"function"==typeof e?e:pt(e);return this.select((function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(oe)},clone:function(t){return this.select(t?ce:se)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,e,n){var r,i,a=de(t+""),o=a.length;if(!(arguments.length<2)){for(s=e?ye:pe,null==n&&(n=!1),r=0;r<o;++r)this.each(s(a[r],e,n));return this}var s=this.node().__on;if(s)for(var c,u=0,l=s.length;u<l;++u)for(r=0,c=s[u];r<o;++r)if((i=a[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,e){return this.each(("function"==typeof e?be:ve)(t,e))}};const ke=we;function Te(t){return"string"==typeof t?new xe([[document.querySelector(t)]],[document.documentElement]):new xe([[t]],_e)}function Ee(){le.stopImmediatePropagation()}function Ce(){le.preventDefault(),le.stopImmediatePropagation()}function Se(t){var e=t.document.documentElement,n=Te(t).on("dragstart.drag",Ce,!0);"onselectstart"in e?n.on("selectstart.drag",Ce,!0):(e.__noselect=e.style.MozUserSelect,e.style.MozUserSelect="none")}function Ae(t,e){var n=t.document.documentElement,r=Te(t).on("dragstart.drag",null);e&&(r.on("click.drag",Ce,!0),setTimeout((function(){r.on("click.drag",null)}),0)),"onselectstart"in n?r.on("selectstart.drag",null):(n.style.MozUserSelect=n.__noselect,delete n.__noselect)}function Me(t,e,n){t.prototype=e.prototype=n,n.constructor=t}function Ne(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function De(){}var Oe=.7,Be=1/Oe,Le="\\s*([+-]?\\d+)\\s*",Ie="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",Re="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",Fe=/^#([0-9a-f]{3,8})$/,Pe=new RegExp("^rgb\\("+[Le,Le,Le]+"\\)$"),je=new RegExp("^rgb\\("+[Re,Re,Re]+"\\)$"),Ye=new RegExp("^rgba\\("+[Le,Le,Le,Ie]+"\\)$"),ze=new RegExp("^rgba\\("+[Re,Re,Re,Ie]+"\\)$"),Ue=new RegExp("^hsl\\("+[Ie,Re,Re]+"\\)$"),qe=new RegExp("^hsla\\("+[Ie,Re,Re,Ie]+"\\)$"),He={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function $e(){return this.rgb().formatHex()}function We(){return this.rgb().formatRgb()}function Ve(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=Fe.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?Ge(e):3===n?new Ke(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?Xe(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?Xe(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=Pe.exec(t))?new Ke(e[1],e[2],e[3],1):(e=je.exec(t))?new Ke(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=Ye.exec(t))?Xe(e[1],e[2],e[3],e[4]):(e=ze.exec(t))?Xe(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=Ue.exec(t))?nn(e[1],e[2]/100,e[3]/100,1):(e=qe.exec(t))?nn(e[1],e[2]/100,e[3]/100,e[4]):He.hasOwnProperty(t)?Ge(He[t]):"transparent"===t?new Ke(NaN,NaN,NaN,0):null}function Ge(t){return new Ke(t>>16&255,t>>8&255,255&t,1)}function Xe(t,e,n,r){return r<=0&&(t=e=n=NaN),new Ke(t,e,n,r)}function Ze(t){return t instanceof De||(t=Ve(t)),t?new Ke((t=t.rgb()).r,t.g,t.b,t.opacity):new Ke}function Qe(t,e,n,r){return 1===arguments.length?Ze(t):new Ke(t,e,n,null==r?1:r)}function Ke(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function Je(){return"#"+en(this.r)+en(this.g)+en(this.b)}function tn(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function en(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function nn(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new on(t,e,n,r)}function rn(t){if(t instanceof on)return new on(t.h,t.s,t.l,t.opacity);if(t instanceof De||(t=Ve(t)),!t)return new on;if(t instanceof on)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new on(o,s,c,t.opacity)}function an(t,e,n,r){return 1===arguments.length?rn(t):new on(t,e,n,null==r?1:r)}function on(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function sn(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}function cn(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}function un(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return cn((n-r/e)*e,o,i,a,s)}}function ln(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return cn((n-r/e)*e,i,a,o,s)}}function hn(t){return function(){return t}}function fn(t,e){return function(n){return t+n*e}}function dn(t,e){var n=e-t;return n?fn(t,n>180||n<-180?n-360*Math.round(n/360):n):hn(isNaN(t)?e:t)}function pn(t,e){var n=e-t;return n?fn(t,n):hn(isNaN(t)?e:t)}Me(De,Ve,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:$e,formatHex:$e,formatHsl:function(){return rn(this).formatHsl()},formatRgb:We,toString:We}),Me(Ke,Qe,Ne(De,{brighter:function(t){return t=null==t?Be:Math.pow(Be,t),new Ke(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Oe:Math.pow(Oe,t),new Ke(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Je,formatHex:Je,formatRgb:tn,toString:tn})),Me(on,an,Ne(De,{brighter:function(t){return t=null==t?Be:Math.pow(Be,t),new on(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Oe:Math.pow(Oe,t),new on(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new Ke(sn(t>=240?t-240:t+120,i,r),sn(t,i,r),sn(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const yn=function t(e){var n=function(t){return 1==(t=+t)?pn:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):hn(isNaN(e)?n:e)}}(e);function r(t,e){var r=n((t=Qe(t)).r,(e=Qe(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=pn(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function gn(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=Qe(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}var mn=gn(un),vn=gn(ln);function bn(t,e){e||(e=[]);var n,r=t?Math.min(e.length,t.length):0,i=e.slice();return function(a){for(n=0;n<r;++n)i[n]=t[n]*(1-a)+e[n]*a;return i}}function _n(t){return ArrayBuffer.isView(t)&&!(t instanceof DataView)}function xn(t,e){return(_n(e)?bn:wn)(t,e)}function wn(t,e){var n,r=e?e.length:0,i=t?Math.min(r,t.length):0,a=new Array(i),o=new Array(r);for(n=0;n<i;++n)a[n]=Mn(t[n],e[n]);for(;n<r;++n)o[n]=e[n];return function(t){for(n=0;n<i;++n)o[n]=a[n](t);return o}}function kn(t,e){var n=new Date;return t=+t,e=+e,function(r){return n.setTime(t*(1-r)+e*r),n}}function Tn(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}function En(t,e){var n,r={},i={};for(n in null!==t&&"object"==typeof t||(t={}),null!==e&&"object"==typeof e||(e={}),e)n in t?r[n]=Mn(t[n],e[n]):i[n]=e[n];return function(t){for(n in r)i[n]=r[n](t);return i}}var Cn=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Sn=new RegExp(Cn.source,"g");function An(t,e){var n,r,i,a=Cn.lastIndex=Sn.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=Cn.exec(t))&&(r=Sn.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:Tn(n,r)})),a=Sn.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})}function Mn(t,e){var n,r=typeof e;return null==e||"boolean"===r?hn(e):("number"===r?Tn:"string"===r?(n=Ve(e))?(e=n,yn):An:e instanceof Ve?yn:e instanceof Date?kn:_n(e)?bn:Array.isArray(e)?wn:"function"!=typeof e.valueOf&&"function"!=typeof e.toString||isNaN(e)?En:Tn)(t,e)}function Nn(){for(var t,e=le;t=e.sourceEvent;)e=t;return e}function Dn(t,e){var n=t.ownerSVGElement||t;if(n.createSVGPoint){var r=n.createSVGPoint();return r.x=e.clientX,r.y=e.clientY,[(r=r.matrixTransform(t.getScreenCTM().inverse())).x,r.y]}var i=t.getBoundingClientRect();return[e.clientX-i.left-t.clientLeft,e.clientY-i.top-t.clientTop]}function On(t,e,n){arguments.length<3&&(n=e,e=Nn().changedTouches);for(var r,i=0,a=e?e.length:0;i<a;++i)if((r=e[i]).identifier===n)return Dn(t,r);return null}function Bn(t){var e=Nn();return e.changedTouches&&(e=e.changedTouches[0]),Dn(t,e)}var Ln,In,Rn=0,Fn=0,Pn=0,jn=0,Yn=0,zn=0,Un="object"==typeof performance&&performance.now?performance:Date,qn="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function Hn(){return Yn||(qn($n),Yn=Un.now()+zn)}function $n(){Yn=0}function Wn(){this._call=this._time=this._next=null}function Vn(t,e,n){var r=new Wn;return r.restart(t,e,n),r}function Gn(){Hn(),++Rn;for(var t,e=Ln;e;)(t=Yn-e._time)>=0&&e._call.call(null,t),e=e._next;--Rn}function Xn(){Yn=(jn=Un.now())+zn,Rn=Fn=0;try{Gn()}finally{Rn=0,function(){for(var t,e,n=Ln,r=1/0;n;)n._call?(r>n._time&&(r=n._time),t=n,n=n._next):(e=n._next,n._next=null,n=t?t._next=e:Ln=e);In=t,Qn(r)}(),Yn=0}}function Zn(){var t=Un.now(),e=t-jn;e>1e3&&(zn-=e,jn=t)}function Qn(t){Rn||(Fn&&(Fn=clearTimeout(Fn)),t-Yn>24?(t<1/0&&(Fn=setTimeout(Xn,t-Un.now()-zn)),Pn&&(Pn=clearInterval(Pn))):(Pn||(jn=Un.now(),Pn=setInterval(Zn,1e3)),Rn=1,qn(Xn)))}function Kn(t,e,n){var r=new Wn;return e=null==e?0:+e,r.restart((function(n){r.stop(),t(n+e)}),e,n),r}Wn.prototype=Vn.prototype={constructor:Wn,restart:function(t,e,n){if("function"!=typeof t)throw new TypeError("callback is not a function");n=(null==n?Hn():+n)+(null==e?0:+e),this._next||In===this||(In?In._next=this:Ln=this,In=this),this._call=t,this._time=n,Qn()},stop:function(){this._call&&(this._call=null,this._time=1/0,Qn())}};var Jn=ft("start","end","cancel","interrupt"),tr=[];function er(t,e,n,r,i,a){var o=t.__transition;if(o){if(n in o)return}else t.__transition={};!function(t,e,n){var r,i=t.__transition;function a(c){var u,l,h,f;if(1!==n.state)return s();for(u in i)if((f=i[u]).name===n.name){if(3===f.state)return Kn(a);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[u]):+u<e&&(f.state=6,f.timer.stop(),f.on.call("cancel",t,t.__data__,f.index,f.group),delete i[u])}if(Kn((function(){3===n.state&&(n.state=4,n.timer.restart(o,n.delay,n.time),o(c))})),n.state=2,n.on.call("start",t,t.__data__,n.index,n.group),2===n.state){for(n.state=3,r=new Array(h=n.tween.length),u=0,l=-1;u<h;++u)(f=n.tween[u].value.call(t,t.__data__,n.index,n.group))&&(r[++l]=f);r.length=l+1}}function o(e){for(var i=e<n.duration?n.ease.call(null,e/n.duration):(n.timer.restart(s),n.state=5,1),a=-1,o=r.length;++a<o;)r[a].call(t,i);5===n.state&&(n.on.call("end",t,t.__data__,n.index,n.group),s())}function s(){for(var r in n.state=6,n.timer.stop(),delete i[e],i)return;delete t.__transition}i[e]=n,n.timer=Vn((function(t){n.state=1,n.timer.restart(a,n.delay,n.time),n.delay<=t&&a(t-n.delay)}),0,n.time)}(t,n,{name:e,index:r,group:i,on:Jn,tween:tr,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:0})}function nr(t,e){var n=ir(t,e);if(n.state>0)throw new Error("too late; already scheduled");return n}function rr(t,e){var n=ir(t,e);if(n.state>3)throw new Error("too late; already running");return n}function ir(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function ar(t,e){var n,r,i,a=t.__transition,o=!0;if(a){for(i in e=null==e?null:e+"",a)(n=a[i]).name===e?(r=n.state>2&&n.state<5,n.state=6,n.timer.stop(),n.on.call(r?"interrupt":"cancel",t,t.__data__,n.index,n.group),delete a[i]):o=!1;o&&delete t.__transition}}var or,sr,cr,ur,lr=180/Math.PI,hr={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function fr(t,e,n,r,i,a){var o,s,c;return(o=Math.sqrt(t*t+e*e))&&(t/=o,e/=o),(c=t*n+e*r)&&(n-=t*c,r-=e*c),(s=Math.sqrt(n*n+r*r))&&(n/=s,r/=s,c/=s),t*r<e*n&&(t=-t,e=-e,c=-c,o=-o),{translateX:i,translateY:a,rotate:Math.atan2(e,t)*lr,skewX:Math.atan(c)*lr,scaleX:o,scaleY:s}}function dr(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return function(a,o){var s=[],c=[];return a=t(a),o=t(o),function(t,r,i,a,o,s){if(t!==i||r!==a){var c=o.push("translate(",null,e,null,n);s.push({i:c-4,x:Tn(t,i)},{i:c-2,x:Tn(r,a)})}else(i||a)&&o.push("translate("+i+e+a+n)}(a.translateX,a.translateY,o.translateX,o.translateY,s,c),function(t,e,n,a){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:Tn(t,e)})):e&&n.push(i(n)+"rotate("+e+r)}(a.rotate,o.rotate,s,c),function(t,e,n,a){t!==e?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:Tn(t,e)}):e&&n.push(i(n)+"skewX("+e+r)}(a.skewX,o.skewX,s,c),function(t,e,n,r,a,o){if(t!==n||e!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:Tn(t,n)},{i:s-2,x:Tn(e,r)})}else 1===n&&1===r||a.push(i(a)+"scale("+n+","+r+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,c),a=o=null,function(t){for(var e,n=-1,r=c.length;++n<r;)s[(e=c[n]).i]=e.x(t);return s.join("")}}}var pr=dr((function(t){return"none"===t?hr:(or||(or=document.createElement("DIV"),sr=document.documentElement,cr=document.defaultView),or.style.transform=t,t=cr.getComputedStyle(sr.appendChild(or),null).getPropertyValue("transform"),sr.removeChild(or),fr(+(t=t.slice(7,-1).split(","))[0],+t[1],+t[2],+t[3],+t[4],+t[5]))}),"px, ","px)","deg)"),yr=dr((function(t){return null==t?hr:(ur||(ur=document.createElementNS("http://www.w3.org/2000/svg","g")),ur.setAttribute("transform",t),(t=ur.transform.baseVal.consolidate())?fr((t=t.matrix).a,t.b,t.c,t.d,t.e,t.f):hr)}),", ",")",")");function gr(t,e){var n,r;return function(){var i=rr(this,t),a=i.tween;if(a!==n)for(var o=0,s=(r=n=a).length;o<s;++o)if(r[o].name===e){(r=r.slice()).splice(o,1);break}i.tween=r}}function mr(t,e,n){var r,i;if("function"!=typeof n)throw new Error;return function(){var a=rr(this,t),o=a.tween;if(o!==r){i=(r=o).slice();for(var s={name:e,value:n},c=0,u=i.length;c<u;++c)if(i[c].name===e){i[c]=s;break}c===u&&i.push(s)}a.tween=i}}function vr(t,e,n){var r=t._id;return t.each((function(){var t=rr(this,r);(t.value||(t.value={}))[e]=n.apply(this,arguments)})),function(t){return ir(t,r).value[e]}}function br(t,e){var n;return("number"==typeof e?Tn:e instanceof Ve?yn:(n=Ve(e))?(e=n,yn):An)(t,e)}function _r(t){return function(){this.removeAttribute(t)}}function xr(t){return function(){this.removeAttributeNS(t.space,t.local)}}function wr(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttribute(t);return o===a?null:o===r?i:i=e(r=o,n)}}function kr(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttributeNS(t.space,t.local);return o===a?null:o===r?i:i=e(r=o,n)}}function Tr(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttribute(t))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttribute(t)}}function Er(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttributeNS(t.space,t.local))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttributeNS(t.space,t.local)}}function Cr(t,e){return function(n){this.setAttribute(t,e.call(this,n))}}function Sr(t,e){return function(n){this.setAttributeNS(t.space,t.local,e.call(this,n))}}function Ar(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&Sr(t,i)),n}return i._value=e,i}function Mr(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&Cr(t,i)),n}return i._value=e,i}function Nr(t,e){return function(){nr(this,t).delay=+e.apply(this,arguments)}}function Dr(t,e){return e=+e,function(){nr(this,t).delay=e}}function Or(t,e){return function(){rr(this,t).duration=+e.apply(this,arguments)}}function Br(t,e){return e=+e,function(){rr(this,t).duration=e}}function Lr(t,e){if("function"!=typeof e)throw new Error;return function(){rr(this,t).ease=e}}function Ir(t,e,n){var r,i,a=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var e=t.indexOf(".");return e>=0&&(t=t.slice(0,e)),!t||"start"===t}))}(e)?nr:rr;return function(){var o=a(this,t),s=o.on;s!==r&&(i=(r=s).copy()).on(e,n),o.on=i}}var Rr=ke.prototype.constructor;function Fr(t){return function(){this.style.removeProperty(t)}}function Pr(t,e,n){return function(r){this.style.setProperty(t,e.call(this,r),n)}}function jr(t,e,n){var r,i;function a(){var a=e.apply(this,arguments);return a!==i&&(r=(i=a)&&Pr(t,a,n)),r}return a._value=e,a}function Yr(t){return function(e){this.textContent=t.call(this,e)}}function zr(t){var e,n;function r(){var r=t.apply(this,arguments);return r!==n&&(e=(n=r)&&Yr(r)),e}return r._value=t,r}var Ur=0;function qr(t,e,n,r){this._groups=t,this._parents=e,this._name=n,this._id=r}function Hr(t){return ke().transition(t)}function $r(){return++Ur}var Wr=ke.prototype;function Vr(t){return t*t*t}function Gr(t){return--t*t*t+1}function Xr(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}qr.prototype=Hr.prototype={constructor:qr,select:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=pt(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;o<i;++o)for(var s,c,u=r[o],l=u.length,h=a[o]=new Array(l),f=0;f<l;++f)(s=u[f])&&(c=t.call(s,s.__data__,f,u))&&("__data__"in s&&(c.__data__=s.__data__),h[f]=c,er(h[f],e,n,f,h,ir(s,n)));return new qr(a,this._parents,e,n)},selectAll:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=gt(t));for(var r=this._groups,i=r.length,a=[],o=[],s=0;s<i;++s)for(var c,u=r[s],l=u.length,h=0;h<l;++h)if(c=u[h]){for(var f,d=t.call(c,c.__data__,h,u),p=ir(c,n),y=0,g=d.length;y<g;++y)(f=d[y])&&er(f,e,n,y,d,p);a.push(d),o.push(c)}return new qr(a,o,e,n)},filter:function(t){"function"!=typeof t&&(t=mt(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new qr(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new qr(o,this._parents,this._name,this._id)},selection:function(){return new Rr(this._groups,this._parents)},transition:function(){for(var t=this._name,e=this._id,n=$r(),r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)if(o=s[u]){var l=ir(o,e);er(o,t,n,u,s,{time:l.time+l.delay+l.duration,delay:0,duration:l.duration,ease:l.ease})}return new qr(r,this._parents,t,n)},call:Wr.call,nodes:Wr.nodes,node:Wr.node,size:Wr.size,empty:Wr.empty,each:Wr.each,on:function(t,e){var n=this._id;return arguments.length<2?ir(this.node(),n).on.on(t):this.each(Ir(n,t,e))},attr:function(t,e){var n=Et(t),r="transform"===n?yr:br;return this.attrTween(t,"function"==typeof e?(n.local?Er:Tr)(n,r,vr(this,"attr."+t,e)):null==e?(n.local?xr:_r)(n):(n.local?kr:wr)(n,r,e))},attrTween:function(t,e){var n="attr."+t;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(null==e)return this.tween(n,null);if("function"!=typeof e)throw new Error;var r=Et(t);return this.tween(n,(r.local?Ar:Mr)(r,e))},style:function(t,e,n){var r="transform"==(t+="")?pr:br;return null==e?this.styleTween(t,function(t,e){var n,r,i;return function(){var a=Rt(this,t),o=(this.style.removeProperty(t),Rt(this,t));return a===o?null:a===n&&o===r?i:i=e(n=a,r=o)}}(t,r)).on("end.style."+t,Fr(t)):"function"==typeof e?this.styleTween(t,function(t,e,n){var r,i,a;return function(){var o=Rt(this,t),s=n(this),c=s+"";return null==s&&(this.style.removeProperty(t),c=s=Rt(this,t)),o===c?null:o===r&&c===i?a:(i=c,a=e(r=o,s))}}(t,r,vr(this,"style."+t,e))).each(function(t,e){var n,r,i,a,o="style."+e,s="end."+o;return function(){var c=rr(this,t),u=c.on,l=null==c.value[o]?a||(a=Fr(e)):void 0;u===n&&i===l||(r=(n=u).copy()).on(s,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,e,n){var r,i,a=n+"";return function(){var o=Rt(this,t);return o===a?null:o===r?i:i=e(r=o,n)}}(t,r,e),n).on("end.style."+t,null)},styleTween:function(t,e,n){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==e)return this.tween(r,null);if("function"!=typeof e)throw new Error;return this.tween(r,jr(t,e,null==n?"":n))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var e=t(this);this.textContent=null==e?"":e}}(vr(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(null==t)return this.tween(e,null);if("function"!=typeof t)throw new Error;return this.tween(e,zr(t))},remove:function(){return this.on("end.remove",function(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}(this._id))},tween:function(t,e){var n=this._id;if(t+="",arguments.length<2){for(var r,i=ir(this.node(),n).tween,a=0,o=i.length;a<o;++a)if((r=i[a]).name===t)return r.value;return null}return this.each((null==e?gr:mr)(n,t,e))},delay:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Nr:Dr)(e,t)):ir(this.node(),e).delay},duration:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Or:Br)(e,t)):ir(this.node(),e).duration},ease:function(t){var e=this._id;return arguments.length?this.each(Lr(e,t)):ir(this.node(),e).ease},end:function(){var t,e,n=this,r=n._id,i=n.size();return new Promise((function(a,o){var s={value:o},c={value:function(){0==--i&&a()}};n.each((function(){var n=rr(this,r),i=n.on;i!==t&&((e=(t=i).copy())._.cancel.push(s),e._.interrupt.push(s),e._.end.push(c)),n.on=e}))}))}};var Zr={time:null,delay:0,duration:250,ease:Xr};function Qr(t,e){for(var n;!(n=t.__transition)||!(n=n[e]);)if(!(t=t.parentNode))return Zr.time=Hn(),Zr;return n}ke.prototype.interrupt=function(t){return this.each((function(){ar(this,t)}))},ke.prototype.transition=function(t){var e,n;t instanceof qr?(e=t._id,t=t._name):(e=$r(),(n=Zr).time=Hn(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)(o=s[u])&&er(o,t,e,u,s,n||Qr(o,e));return new qr(r,this._parents,t,e)};var Kr=[null];function Jr(t,e){var n,r,i=t.__transition;if(i)for(r in e=null==e?null:e+"",i)if((n=i[r]).state>1&&n.name===e)return new qr([[t]],Kr,e,+r);return null}function ti(t){return function(){return t}}function ei(t,e,n){this.target=t,this.type=e,this.selection=n}function ni(){le.stopImmediatePropagation()}function ri(){le.preventDefault(),le.stopImmediatePropagation()}var ii={name:"drag"},ai={name:"space"},oi={name:"handle"},si={name:"center"};function ci(t){return[+t[0],+t[1]]}function ui(t){return[ci(t[0]),ci(t[1])]}function li(t){return function(e){return On(e,le.touches,t)}}var hi={name:"x",handles:["w","e"].map(bi),input:function(t,e){return null==t?null:[[+t[0],e[0][1]],[+t[1],e[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},fi={name:"y",handles:["n","s"].map(bi),input:function(t,e){return null==t?null:[[e[0][0],+t[0]],[e[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},di={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(bi),input:function(t){return null==t?null:ui(t)},output:function(t){return t}},pi={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},yi={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},gi={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},mi={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},vi={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function bi(t){return{type:t}}function _i(){return!le.ctrlKey&&!le.button}function xi(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function wi(){return navigator.maxTouchPoints||"ontouchstart"in this}function ki(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function Ti(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function Ei(t){var e=t.__brush;return e?e.dim.output(e.selection):null}function Ci(){return Mi(hi)}function Si(){return Mi(fi)}function Ai(){return Mi(di)}function Mi(t){var e,n=xi,r=_i,i=wi,a=!0,o=ft("start","brush","end"),s=6;function c(e){var n=e.property("__brush",y).selectAll(".overlay").data([bi("overlay")]);n.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",pi.overlay).merge(n).each((function(){var t=ki(this).extent;Te(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),e.selectAll(".selection").data([bi("selection")]).enter().append("rect").attr("class","selection").attr("cursor",pi.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=e.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return pi[t.type]})),e.each(u).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",f).filter(i).on("touchstart.brush",f).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function u(){var t=Te(this),e=ki(this).selection;e?(t.selectAll(".selection").style("display",null).attr("x",e[0][0]).attr("y",e[0][1]).attr("width",e[1][0]-e[0][0]).attr("height",e[1][1]-e[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?e[1][0]-s/2:e[0][0]-s/2})).attr("y",(function(t){return"s"===t.type[0]?e[1][1]-s/2:e[0][1]-s/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?e[1][0]-e[0][0]+s:s})).attr("height",(function(t){return"e"===t.type||"w"===t.type?e[1][1]-e[0][1]+s:s}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function l(t,e,n){var r=t.__brush.emitter;return!r||n&&r.clean?new h(t,e,n):r}function h(t,e,n){this.that=t,this.args=e,this.state=t.__brush,this.active=0,this.clean=n}function f(){if((!e||le.touches)&&r.apply(this,arguments)){var n,i,o,s,c,h,f,d,p,y,g,m=this,v=le.target.__data__.type,b="selection"===(a&&le.metaKey?v="overlay":v)?ii:a&&le.altKey?si:oi,_=t===fi?null:mi[v],x=t===hi?null:vi[v],w=ki(m),k=w.extent,T=w.selection,E=k[0][0],C=k[0][1],S=k[1][0],A=k[1][1],M=0,N=0,D=_&&x&&a&&le.shiftKey,O=le.touches?li(le.changedTouches[0].identifier):Bn,B=O(m),L=B,I=l(m,arguments,!0).beforestart();"overlay"===v?(T&&(p=!0),w.selection=T=[[n=t===fi?E:B[0],o=t===hi?C:B[1]],[c=t===fi?S:n,f=t===hi?A:o]]):(n=T[0][0],o=T[0][1],c=T[1][0],f=T[1][1]),i=n,s=o,h=c,d=f;var R=Te(m).attr("pointer-events","none"),F=R.selectAll(".overlay").attr("cursor",pi[v]);if(le.touches)I.moved=j,I.ended=z;else{var P=Te(le.view).on("mousemove.brush",j,!0).on("mouseup.brush",z,!0);a&&P.on("keydown.brush",U,!0).on("keyup.brush",q,!0),Se(le.view)}ni(),ar(m),u.call(m),I.start()}function j(){var t=O(m);!D||y||g||(Math.abs(t[0]-L[0])>Math.abs(t[1]-L[1])?g=!0:y=!0),L=t,p=!0,ri(),Y()}function Y(){var t;switch(M=L[0]-B[0],N=L[1]-B[1],b){case ai:case ii:_&&(M=Math.max(E-n,Math.min(S-c,M)),i=n+M,h=c+M),x&&(N=Math.max(C-o,Math.min(A-f,N)),s=o+N,d=f+N);break;case oi:_<0?(M=Math.max(E-n,Math.min(S-n,M)),i=n+M,h=c):_>0&&(M=Math.max(E-c,Math.min(S-c,M)),i=n,h=c+M),x<0?(N=Math.max(C-o,Math.min(A-o,N)),s=o+N,d=f):x>0&&(N=Math.max(C-f,Math.min(A-f,N)),s=o,d=f+N);break;case si:_&&(i=Math.max(E,Math.min(S,n-M*_)),h=Math.max(E,Math.min(S,c+M*_))),x&&(s=Math.max(C,Math.min(A,o-N*x)),d=Math.max(C,Math.min(A,f+N*x)))}h<i&&(_*=-1,t=n,n=c,c=t,t=i,i=h,h=t,v in yi&&F.attr("cursor",pi[v=yi[v]])),d<s&&(x*=-1,t=o,o=f,f=t,t=s,s=d,d=t,v in gi&&F.attr("cursor",pi[v=gi[v]])),w.selection&&(T=w.selection),y&&(i=T[0][0],h=T[1][0]),g&&(s=T[0][1],d=T[1][1]),T[0][0]===i&&T[0][1]===s&&T[1][0]===h&&T[1][1]===d||(w.selection=[[i,s],[h,d]],u.call(m),I.brush())}function z(){if(ni(),le.touches){if(le.touches.length)return;e&&clearTimeout(e),e=setTimeout((function(){e=null}),500)}else Ae(le.view,p),P.on("keydown.brush keyup.brush mousemove.brush mouseup.brush",null);R.attr("pointer-events","all"),F.attr("cursor",pi.overlay),w.selection&&(T=w.selection),Ti(T)&&(w.selection=null,u.call(m)),I.end()}function U(){switch(le.keyCode){case 16:D=_&&x;break;case 18:b===oi&&(_&&(c=h-M*_,n=i+M*_),x&&(f=d-N*x,o=s+N*x),b=si,Y());break;case 32:b!==oi&&b!==si||(_<0?c=h-M:_>0&&(n=i-M),x<0?f=d-N:x>0&&(o=s-N),b=ai,F.attr("cursor",pi.selection),Y());break;default:return}ri()}function q(){switch(le.keyCode){case 16:D&&(y=g=D=!1,Y());break;case 18:b===si&&(_<0?c=h:_>0&&(n=i),x<0?f=d:x>0&&(o=s),b=oi,Y());break;case 32:b===ai&&(le.altKey?(_&&(c=h-M*_,n=i+M*_),x&&(f=d-N*x,o=s+N*x),b=si):(_<0?c=h:_>0&&(n=i),x<0?f=d:x>0&&(o=s),b=oi),F.attr("cursor",pi[v]),Y());break;default:return}ri()}}function d(){l(this,arguments).moved()}function p(){l(this,arguments).ended()}function y(){var e=this.__brush||{selection:null};return e.extent=ui(n.apply(this,arguments)),e.dim=t,e}return c.move=function(e,n){e.selection?e.on("start.brush",(function(){l(this,arguments).beforestart().start()})).on("interrupt.brush end.brush",(function(){l(this,arguments).end()})).tween("brush",(function(){var e=this,r=e.__brush,i=l(e,arguments),a=r.selection,o=t.input("function"==typeof n?n.apply(this,arguments):n,r.extent),s=Mn(a,o);function c(t){r.selection=1===t&&null===o?null:s(t),u.call(e),i.brush()}return null!==a&&null!==o?c:c(1)})):e.each((function(){var e=this,r=arguments,i=e.__brush,a=t.input("function"==typeof n?n.apply(e,r):n,i.extent),o=l(e,r).beforestart();ar(e),i.selection=null===a?null:a,u.call(e),o.start().brush().end()}))},c.clear=function(t){c.move(t,null)},h.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(){return this.starting?(this.starting=!1,this.emit("start")):this.emit("brush"),this},brush:function(){return this.emit("brush"),this},end:function(){return 0==--this.active&&(delete this.state.emitter,this.emit("end")),this},emit:function(e){ge(new ei(c,e,t.output(this.state.selection)),o.apply,o,[e,this.that,this.args])}},c.extent=function(t){return arguments.length?(n="function"==typeof t?t:ti(ui(t)),c):n},c.filter=function(t){return arguments.length?(r="function"==typeof t?t:ti(!!t),c):r},c.touchable=function(t){return arguments.length?(i="function"==typeof t?t:ti(!!t),c):i},c.handleSize=function(t){return arguments.length?(s=+t,c):s},c.keyModifiers=function(t){return arguments.length?(a=!!t,c):a},c.on=function(){var t=o.on.apply(o,arguments);return t===o?c:t},c}var Ni=Math.cos,Di=Math.sin,Oi=Math.PI,Bi=Oi/2,Li=2*Oi,Ii=Math.max;function Ri(t){return function(e,n){return t(e.source.value+e.target.value,n.source.value+n.target.value)}}function Fi(){var t=0,e=null,n=null,r=null;function i(i){var a,o,s,c,u,l,h=i.length,f=[],d=k(h),p=[],y=[],g=y.groups=new Array(h),m=new Array(h*h);for(a=0,u=-1;++u<h;){for(o=0,l=-1;++l<h;)o+=i[u][l];f.push(o),p.push(k(h)),a+=o}for(e&&d.sort((function(t,n){return e(f[t],f[n])})),n&&p.forEach((function(t,e){t.sort((function(t,r){return n(i[e][t],i[e][r])}))})),c=(a=Ii(0,Li-t*h)/a)?t:Li/h,o=0,u=-1;++u<h;){for(s=o,l=-1;++l<h;){var v=d[u],b=p[v][l],_=i[v][b],x=o,w=o+=_*a;m[b*h+v]={index:v,subindex:b,startAngle:x,endAngle:w,value:_}}g[v]={index:v,startAngle:s,endAngle:o,value:f[v]},o+=c}for(u=-1;++u<h;)for(l=u-1;++l<h;){var T=m[l*h+u],E=m[u*h+l];(T.value||E.value)&&y.push(T.value<E.value?{source:E,target:T}:{source:T,target:E})}return r?y.sort(r):y}return i.padAngle=function(e){return arguments.length?(t=Ii(0,e),i):t},i.sortGroups=function(t){return arguments.length?(e=t,i):e},i.sortSubgroups=function(t){return arguments.length?(n=t,i):n},i.sortChords=function(t){return arguments.length?(null==t?r=null:(r=Ri(t))._=t,i):r&&r._},i}var Pi=Array.prototype.slice;function ji(t){return function(){return t}}var Yi=Math.PI,zi=2*Yi,Ui=1e-6,qi=zi-Ui;function Hi(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function $i(){return new Hi}Hi.prototype=$i.prototype={constructor:Hi,moveTo:function(t,e){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)},closePath:function(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},lineTo:function(t,e){this._+="L"+(this._x1=+t)+","+(this._y1=+e)},quadraticCurveTo:function(t,e,n,r){this._+="Q"+ +t+","+ +e+","+(this._x1=+n)+","+(this._y1=+r)},bezierCurveTo:function(t,e,n,r,i,a){this._+="C"+ +t+","+ +e+","+ +n+","+ +r+","+(this._x1=+i)+","+(this._y1=+a)},arcTo:function(t,e,n,r,i){t=+t,e=+e,n=+n,r=+r,i=+i;var a=this._x1,o=this._y1,s=n-t,c=r-e,u=a-t,l=o-e,h=u*u+l*l;if(i<0)throw new Error("negative radius: "+i);if(null===this._x1)this._+="M"+(this._x1=t)+","+(this._y1=e);else if(h>Ui)if(Math.abs(l*s-c*u)>Ui&&i){var f=n-a,d=r-o,p=s*s+c*c,y=f*f+d*d,g=Math.sqrt(p),m=Math.sqrt(h),v=i*Math.tan((Yi-Math.acos((p+h-y)/(2*g*m)))/2),b=v/m,_=v/g;Math.abs(b-1)>Ui&&(this._+="L"+(t+b*u)+","+(e+b*l)),this._+="A"+i+","+i+",0,0,"+ +(l*f>u*d)+","+(this._x1=t+_*s)+","+(this._y1=e+_*c)}else this._+="L"+(this._x1=t)+","+(this._y1=e)},arc:function(t,e,n,r,i,a){t=+t,e=+e,a=!!a;var o=(n=+n)*Math.cos(r),s=n*Math.sin(r),c=t+o,u=e+s,l=1^a,h=a?r-i:i-r;if(n<0)throw new Error("negative radius: "+n);null===this._x1?this._+="M"+c+","+u:(Math.abs(this._x1-c)>Ui||Math.abs(this._y1-u)>Ui)&&(this._+="L"+c+","+u),n&&(h<0&&(h=h%zi+zi),h>qi?this._+="A"+n+","+n+",0,1,"+l+","+(t-o)+","+(e-s)+"A"+n+","+n+",0,1,"+l+","+(this._x1=c)+","+(this._y1=u):h>Ui&&(this._+="A"+n+","+n+",0,"+ +(h>=Yi)+","+l+","+(this._x1=t+n*Math.cos(i))+","+(this._y1=e+n*Math.sin(i))))},rect:function(t,e,n,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +n+"v"+ +r+"h"+-n+"Z"},toString:function(){return this._}};const Wi=$i;function Vi(t){return t.source}function Gi(t){return t.target}function Xi(t){return t.radius}function Zi(t){return t.startAngle}function Qi(t){return t.endAngle}function Ki(){var t=Vi,e=Gi,n=Xi,r=Zi,i=Qi,a=null;function o(){var o,s=Pi.call(arguments),c=t.apply(this,s),u=e.apply(this,s),l=+n.apply(this,(s[0]=c,s)),h=r.apply(this,s)-Bi,f=i.apply(this,s)-Bi,d=l*Ni(h),p=l*Di(h),y=+n.apply(this,(s[0]=u,s)),g=r.apply(this,s)-Bi,m=i.apply(this,s)-Bi;if(a||(a=o=Wi()),a.moveTo(d,p),a.arc(0,0,l,h,f),h===g&&f===m||(a.quadraticCurveTo(0,0,y*Ni(g),y*Di(g)),a.arc(0,0,y,g,m)),a.quadraticCurveTo(0,0,d,p),a.closePath(),o)return a=null,o+""||null}return o.radius=function(t){return arguments.length?(n="function"==typeof t?t:ji(+t),o):n},o.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:ji(+t),o):r},o.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:ji(+t),o):i},o.source=function(e){return arguments.length?(t=e,o):t},o.target=function(t){return arguments.length?(e=t,o):e},o.context=function(t){return arguments.length?(a=null==t?null:t,o):a},o}var Ji="$";function ta(){}function ea(t,e){var n=new ta;if(t instanceof ta)t.each((function(t,e){n.set(e,t)}));else if(Array.isArray(t)){var r,i=-1,a=t.length;if(null==e)for(;++i<a;)n.set(i,t[i]);else for(;++i<a;)n.set(e(r=t[i],i,t),r)}else if(t)for(var o in t)n.set(o,t[o]);return n}ta.prototype=ea.prototype={constructor:ta,has:function(t){return Ji+t in this},get:function(t){return this[Ji+t]},set:function(t,e){return this[Ji+t]=e,this},remove:function(t){var e=Ji+t;return e in this&&delete this[e]},clear:function(){for(var t in this)t[0]===Ji&&delete this[t]},keys:function(){var t=[];for(var e in this)e[0]===Ji&&t.push(e.slice(1));return t},values:function(){var t=[];for(var e in this)e[0]===Ji&&t.push(this[e]);return t},entries:function(){var t=[];for(var e in this)e[0]===Ji&&t.push({key:e.slice(1),value:this[e]});return t},size:function(){var t=0;for(var e in this)e[0]===Ji&&++t;return t},empty:function(){for(var t in this)if(t[0]===Ji)return!1;return!0},each:function(t){for(var e in this)e[0]===Ji&&t(this[e],e.slice(1),this)}};const na=ea;function ra(){var t,e,n,r=[],i=[];function a(n,i,o,s){if(i>=r.length)return null!=t&&n.sort(t),null!=e?e(n):n;for(var c,u,l,h=-1,f=n.length,d=r[i++],p=na(),y=o();++h<f;)(l=p.get(c=d(u=n[h])+""))?l.push(u):p.set(c,[u]);return p.each((function(t,e){s(y,e,a(t,i,o,s))})),y}function o(t,n){if(++n>r.length)return t;var a,s=i[n-1];return null!=e&&n>=r.length?a=t.entries():(a=[],t.each((function(t,e){a.push({key:e,values:o(t,n)})}))),null!=s?a.sort((function(t,e){return s(t.key,e.key)})):a}return n={object:function(t){return a(t,0,ia,aa)},map:function(t){return a(t,0,oa,sa)},entries:function(t){return o(a(t,0,oa,sa),0)},key:function(t){return r.push(t),n},sortKeys:function(t){return i[r.length-1]=t,n},sortValues:function(e){return t=e,n},rollup:function(t){return e=t,n}}}function ia(){return{}}function aa(t,e,n){t[e]=n}function oa(){return na()}function sa(t,e,n){t.set(e,n)}function ca(){}var ua=na.prototype;function la(t,e){var n=new ca;if(t instanceof ca)t.each((function(t){n.add(t)}));else if(t){var r=-1,i=t.length;if(null==e)for(;++r<i;)n.add(t[r]);else for(;++r<i;)n.add(e(t[r],r,t))}return n}ca.prototype=la.prototype={constructor:ca,has:ua.has,add:function(t){return this[Ji+(t+="")]=t,this},remove:ua.remove,clear:ua.clear,values:ua.keys,size:ua.size,empty:ua.empty,each:ua.each};const ha=la;function fa(t){var e=[];for(var n in t)e.push(n);return e}function da(t){var e=[];for(var n in t)e.push(t[n]);return e}function pa(t){var e=[];for(var n in t)e.push({key:n,value:t[n]});return e}var ya=Math.PI/180,ga=180/Math.PI,ma=.96422,va=.82521,ba=4/29,_a=6/29,xa=3*_a*_a;function wa(t){if(t instanceof Ea)return new Ea(t.l,t.a,t.b,t.opacity);if(t instanceof Ba)return La(t);t instanceof Ke||(t=Ze(t));var e,n,r=Ma(t.r),i=Ma(t.g),a=Ma(t.b),o=Ca((.2225045*r+.7168786*i+.0606169*a)/1);return r===i&&i===a?e=n=o:(e=Ca((.4360747*r+.3850649*i+.1430804*a)/ma),n=Ca((.0139322*r+.0971045*i+.7141733*a)/va)),new Ea(116*o-16,500*(e-o),200*(o-n),t.opacity)}function ka(t,e){return new Ea(t,0,0,null==e?1:e)}function Ta(t,e,n,r){return 1===arguments.length?wa(t):new Ea(t,e,n,null==r?1:r)}function Ea(t,e,n,r){this.l=+t,this.a=+e,this.b=+n,this.opacity=+r}function Ca(t){return t>.008856451679035631?Math.pow(t,1/3):t/xa+ba}function Sa(t){return t>_a?t*t*t:xa*(t-ba)}function Aa(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Ma(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Na(t){if(t instanceof Ba)return new Ba(t.h,t.c,t.l,t.opacity);if(t instanceof Ea||(t=wa(t)),0===t.a&&0===t.b)return new Ba(NaN,0<t.l&&t.l<100?0:NaN,t.l,t.opacity);var e=Math.atan2(t.b,t.a)*ga;return new Ba(e<0?e+360:e,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function Da(t,e,n,r){return 1===arguments.length?Na(t):new Ba(n,e,t,null==r?1:r)}function Oa(t,e,n,r){return 1===arguments.length?Na(t):new Ba(t,e,n,null==r?1:r)}function Ba(t,e,n,r){this.h=+t,this.c=+e,this.l=+n,this.opacity=+r}function La(t){if(isNaN(t.h))return new Ea(t.l,0,0,t.opacity);var e=t.h*ya;return new Ea(t.l,Math.cos(e)*t.c,Math.sin(e)*t.c,t.opacity)}Me(Ea,Ta,Ne(De,{brighter:function(t){return new Ea(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new Ea(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,n=isNaN(this.b)?t:t-this.b/200;return new Ke(Aa(3.1338561*(e=ma*Sa(e))-1.6168667*(t=1*Sa(t))-.4906146*(n=va*Sa(n))),Aa(-.9787684*e+1.9161415*t+.033454*n),Aa(.0719453*e-.2289914*t+1.4052427*n),this.opacity)}})),Me(Ba,Oa,Ne(De,{brighter:function(t){return new Ba(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new Ba(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return La(this).rgb()}}));var Ia=-.14861,Ra=1.78277,Fa=-.29227,Pa=-.90649,ja=1.97294,Ya=ja*Pa,za=ja*Ra,Ua=Ra*Fa-Pa*Ia;function qa(t){if(t instanceof $a)return new $a(t.h,t.s,t.l,t.opacity);t instanceof Ke||(t=Ze(t));var e=t.r/255,n=t.g/255,r=t.b/255,i=(Ua*r+Ya*e-za*n)/(Ua+Ya-za),a=r-i,o=(ja*(n-i)-Fa*a)/Pa,s=Math.sqrt(o*o+a*a)/(ja*i*(1-i)),c=s?Math.atan2(o,a)*ga-120:NaN;return new $a(c<0?c+360:c,s,i,t.opacity)}function Ha(t,e,n,r){return 1===arguments.length?qa(t):new $a(t,e,n,null==r?1:r)}function $a(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}Me($a,Ha,Ne(De,{brighter:function(t){return t=null==t?Be:Math.pow(Be,t),new $a(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Oe:Math.pow(Oe,t),new $a(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*ya,e=+this.l,n=isNaN(this.s)?0:this.s*e*(1-e),r=Math.cos(t),i=Math.sin(t);return new Ke(255*(e+n*(Ia*r+Ra*i)),255*(e+n*(Fa*r+Pa*i)),255*(e+n*(ja*r)),this.opacity)}}));var Wa=Array.prototype.slice;function Va(t,e){return t-e}function Ga(t){return function(){return t}}function Xa(t,e){for(var n,r=-1,i=e.length;++r<i;)if(n=Za(t,e[r]))return n;return 0}function Za(t,e){for(var n=e[0],r=e[1],i=-1,a=0,o=t.length,s=o-1;a<o;s=a++){var c=t[a],u=c[0],l=c[1],h=t[s],f=h[0],d=h[1];if(Qa(c,h,e))return 0;l>r!=d>r&&n<(f-u)*(r-l)/(d-l)+u&&(i=-i)}return i}function Qa(t,e,n){var r,i,a,o;return function(t,e,n){return(e[0]-t[0])*(n[1]-t[1])==(n[0]-t[0])*(e[1]-t[1])}(t,e,n)&&(i=t[r=+(t[0]===e[0])],a=n[r],o=e[r],i<=a&&a<=o||o<=a&&a<=i)}function Ka(){}var Ja=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function to(){var t=1,e=1,n=N,r=s;function i(t){var e=n(t);if(Array.isArray(e))e=e.slice().sort(Va);else{var r=m(t),i=r[0],o=r[1];e=M(i,o,e),e=k(Math.floor(i/e)*e,Math.floor(o/e)*e,e)}return e.map((function(e){return a(t,e)}))}function a(n,i){var a=[],s=[];return function(n,r,i){var a,s,c,u,l,h,f=new Array,d=new Array;for(a=s=-1,u=n[0]>=r,Ja[u<<1].forEach(p);++a<t-1;)c=u,u=n[a+1]>=r,Ja[c|u<<1].forEach(p);for(Ja[u<<0].forEach(p);++s<e-1;){for(a=-1,u=n[s*t+t]>=r,l=n[s*t]>=r,Ja[u<<1|l<<2].forEach(p);++a<t-1;)c=u,u=n[s*t+t+a+1]>=r,h=l,l=n[s*t+a+1]>=r,Ja[c|u<<1|l<<2|h<<3].forEach(p);Ja[u|l<<3].forEach(p)}for(a=-1,l=n[s*t]>=r,Ja[l<<2].forEach(p);++a<t-1;)h=l,l=n[s*t+a+1]>=r,Ja[l<<2|h<<3].forEach(p);function p(t){var e,n,r=[t[0][0]+a,t[0][1]+s],c=[t[1][0]+a,t[1][1]+s],u=o(r),l=o(c);(e=d[u])?(n=f[l])?(delete d[e.end],delete f[n.start],e===n?(e.ring.push(c),i(e.ring)):f[e.start]=d[n.end]={start:e.start,end:n.end,ring:e.ring.concat(n.ring)}):(delete d[e.end],e.ring.push(c),d[e.end=l]=e):(e=f[l])?(n=d[u])?(delete f[e.start],delete d[n.end],e===n?(e.ring.push(c),i(e.ring)):f[n.start]=d[e.end]={start:n.start,end:e.end,ring:n.ring.concat(e.ring)}):(delete f[e.start],e.ring.unshift(r),f[e.start=u]=e):f[u]=d[l]={start:u,end:l,ring:[r,c]}}Ja[l<<3].forEach(p)}(n,i,(function(t){r(t,n,i),function(t){for(var e=0,n=t.length,r=t[n-1][1]*t[0][0]-t[n-1][0]*t[0][1];++e<n;)r+=t[e-1][1]*t[e][0]-t[e-1][0]*t[e][1];return r}(t)>0?a.push([t]):s.push(t)})),s.forEach((function(t){for(var e,n=0,r=a.length;n<r;++n)if(-1!==Xa((e=a[n])[0],t))return void e.push(t)})),{type:"MultiPolygon",value:i,coordinates:a}}function o(e){return 2*e[0]+e[1]*(t+1)*4}function s(n,r,i){n.forEach((function(n){var a,o=n[0],s=n[1],c=0|o,u=0|s,l=r[u*t+c];o>0&&o<t&&c===o&&(a=r[u*t+c-1],n[0]=o+(i-a)/(l-a)-.5),s>0&&s<e&&u===s&&(a=r[(u-1)*t+c],n[1]=s+(i-a)/(l-a)-.5)}))}return i.contour=a,i.size=function(n){if(!arguments.length)return[t,e];var r=Math.ceil(n[0]),a=Math.ceil(n[1]);if(!(r>0&&a>0))throw new Error("invalid size");return t=r,e=a,i},i.thresholds=function(t){return arguments.length?(n="function"==typeof t?t:Array.isArray(t)?Ga(Wa.call(t)):Ga(t),i):n},i.smooth=function(t){return arguments.length?(r=t?s:Ka,i):r===s},i}function eo(t,e,n){for(var r=t.width,i=t.height,a=1+(n<<1),o=0;o<i;++o)for(var s=0,c=0;s<r+n;++s)s<r&&(c+=t.data[s+o*r]),s>=n&&(s>=a&&(c-=t.data[s-a+o*r]),e.data[s-n+o*r]=c/Math.min(s+1,r-1+a-s,a))}function no(t,e,n){for(var r=t.width,i=t.height,a=1+(n<<1),o=0;o<r;++o)for(var s=0,c=0;s<i+n;++s)s<i&&(c+=t.data[o+s*r]),s>=n&&(s>=a&&(c-=t.data[o+(s-a)*r]),e.data[o+(s-n)*r]=c/Math.min(s+1,i-1+a-s,a))}function ro(t){return t[0]}function io(t){return t[1]}function ao(){return 1}function oo(){var t=ro,e=io,n=ao,r=960,i=500,a=20,o=2,s=3*a,c=r+2*s>>o,u=i+2*s>>o,l=Ga(20);function h(r){var i=new Float32Array(c*u),h=new Float32Array(c*u);r.forEach((function(r,a,l){var h=+t(r,a,l)+s>>o,f=+e(r,a,l)+s>>o,d=+n(r,a,l);h>=0&&h<c&&f>=0&&f<u&&(i[h+f*c]+=d)})),eo({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),no({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o),eo({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),no({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o),eo({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),no({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o);var d=l(i);if(!Array.isArray(d)){var p=I(i);d=M(0,p,d),(d=k(0,Math.floor(p/d)*d,d)).shift()}return to().thresholds(d).size([c,u])(i).map(f)}function f(t){return t.value*=Math.pow(2,-2*o),t.coordinates.forEach(d),t}function d(t){t.forEach(p)}function p(t){t.forEach(y)}function y(t){t[0]=t[0]*Math.pow(2,o)-s,t[1]=t[1]*Math.pow(2,o)-s}function g(){return c=r+2*(s=3*a)>>o,u=i+2*s>>o,h}return h.x=function(e){return arguments.length?(t="function"==typeof e?e:Ga(+e),h):t},h.y=function(t){return arguments.length?(e="function"==typeof t?t:Ga(+t),h):e},h.weight=function(t){return arguments.length?(n="function"==typeof t?t:Ga(+t),h):n},h.size=function(t){if(!arguments.length)return[r,i];var e=Math.ceil(t[0]),n=Math.ceil(t[1]);if(!(e>=0||e>=0))throw new Error("invalid size");return r=e,i=n,g()},h.cellSize=function(t){if(!arguments.length)return 1<<o;if(!((t=+t)>=1))throw new Error("invalid cell size");return o=Math.floor(Math.log(t)/Math.LN2),g()},h.thresholds=function(t){return arguments.length?(l="function"==typeof t?t:Array.isArray(t)?Ga(Wa.call(t)):Ga(t),h):l},h.bandwidth=function(t){if(!arguments.length)return Math.sqrt(a*(a+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return a=Math.round((Math.sqrt(4*t*t+1)-1)/2),g()},h}function so(t){return function(){return t}}function co(t,e,n,r,i,a,o,s,c,u){this.target=t,this.type=e,this.subject=n,this.identifier=r,this.active=i,this.x=a,this.y=o,this.dx=s,this.dy=c,this._=u}function uo(){return!le.ctrlKey&&!le.button}function lo(){return this.parentNode}function ho(t){return null==t?{x:le.x,y:le.y}:t}function fo(){return navigator.maxTouchPoints||"ontouchstart"in this}function po(){var t,e,n,r,i=uo,a=lo,o=ho,s=fo,c={},u=ft("start","drag","end"),l=0,h=0;function f(t){t.on("mousedown.drag",d).filter(s).on("touchstart.drag",g).on("touchmove.drag",m).on("touchend.drag touchcancel.drag",v).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(){if(!r&&i.apply(this,arguments)){var o=b("mouse",a.apply(this,arguments),Bn,this,arguments);o&&(Te(le.view).on("mousemove.drag",p,!0).on("mouseup.drag",y,!0),Se(le.view),Ee(),n=!1,t=le.clientX,e=le.clientY,o("start"))}}function p(){if(Ce(),!n){var r=le.clientX-t,i=le.clientY-e;n=r*r+i*i>h}c.mouse("drag")}function y(){Te(le.view).on("mousemove.drag mouseup.drag",null),Ae(le.view,n),Ce(),c.mouse("end")}function g(){if(i.apply(this,arguments)){var t,e,n=le.changedTouches,r=a.apply(this,arguments),o=n.length;for(t=0;t<o;++t)(e=b(n[t].identifier,r,On,this,arguments))&&(Ee(),e("start"))}}function m(){var t,e,n=le.changedTouches,r=n.length;for(t=0;t<r;++t)(e=c[n[t].identifier])&&(Ce(),e("drag"))}function v(){var t,e,n=le.changedTouches,i=n.length;for(r&&clearTimeout(r),r=setTimeout((function(){r=null}),500),t=0;t<i;++t)(e=c[n[t].identifier])&&(Ee(),e("end"))}function b(t,e,n,r,i){var a,s,h,d=n(e,t),p=u.copy();if(ge(new co(f,"beforestart",a,t,l,d[0],d[1],0,0,p),(function(){return null!=(le.subject=a=o.apply(r,i))&&(s=a.x-d[0]||0,h=a.y-d[1]||0,!0)})))return function o(u){var y,g=d;switch(u){case"start":c[t]=o,y=l++;break;case"end":delete c[t],--l;case"drag":d=n(e,t),y=l}ge(new co(f,u,a,t,y,d[0]+s,d[1]+h,d[0]-g[0],d[1]-g[1],p),p.apply,p,[u,r,i])}}return f.filter=function(t){return arguments.length?(i="function"==typeof t?t:so(!!t),f):i},f.container=function(t){return arguments.length?(a="function"==typeof t?t:so(t),f):a},f.subject=function(t){return arguments.length?(o="function"==typeof t?t:so(t),f):o},f.touchable=function(t){return arguments.length?(s="function"==typeof t?t:so(!!t),f):s},f.on=function(){var t=u.on.apply(u,arguments);return t===u?f:t},f.clickDistance=function(t){return arguments.length?(h=(t=+t)*t,f):Math.sqrt(h)},f}co.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var yo={},go={};function mo(t){return new Function("d","return {"+t.map((function(t,e){return JSON.stringify(t)+": d["+e+'] || ""'})).join(",")+"}")}function vo(t){var e=Object.create(null),n=[];return t.forEach((function(t){for(var r in t)r in e||n.push(e[r]=r)})),n}function bo(t,e){var n=t+"",r=n.length;return r<e?new Array(e-r+1).join(0)+n:n}function _o(t){var e=new RegExp('["'+t+"\n\r]"),n=t.charCodeAt(0);function r(t,e){var r,i=[],a=t.length,o=0,s=0,c=a<=0,u=!1;function l(){if(c)return go;if(u)return u=!1,yo;var e,r,i=o;if(34===t.charCodeAt(i)){for(;o++<a&&34!==t.charCodeAt(o)||34===t.charCodeAt(++o););return(e=o)>=a?c=!0:10===(r=t.charCodeAt(o++))?u=!0:13===r&&(u=!0,10===t.charCodeAt(o)&&++o),t.slice(i+1,e-1).replace(/""/g,'"')}for(;o<a;){if(10===(r=t.charCodeAt(e=o++)))u=!0;else if(13===r)u=!0,10===t.charCodeAt(o)&&++o;else if(r!==n)continue;return t.slice(i,e)}return c=!0,t.slice(i,a)}for(10===t.charCodeAt(a-1)&&--a,13===t.charCodeAt(a-1)&&--a;(r=l())!==go;){for(var h=[];r!==yo&&r!==go;)h.push(r),r=l();e&&null==(h=e(h,s++))||i.push(h)}return i}function i(e,n){return e.map((function(e){return n.map((function(t){return o(e[t])})).join(t)}))}function a(e){return e.map(o).join(t)}function o(t){return null==t?"":t instanceof Date?function(t){var e=t.getUTCHours(),n=t.getUTCMinutes(),r=t.getUTCSeconds(),i=t.getUTCMilliseconds();return isNaN(t)?"Invalid Date":function(t){return t<0?"-"+bo(-t,6):t>9999?"+"+bo(t,6):bo(t,4)}(t.getUTCFullYear())+"-"+bo(t.getUTCMonth()+1,2)+"-"+bo(t.getUTCDate(),2)+(i?"T"+bo(e,2)+":"+bo(n,2)+":"+bo(r,2)+"."+bo(i,3)+"Z":r?"T"+bo(e,2)+":"+bo(n,2)+":"+bo(r,2)+"Z":n||e?"T"+bo(e,2)+":"+bo(n,2)+"Z":"")}(t):e.test(t+="")?'"'+t.replace(/"/g,'""')+'"':t}return{parse:function(t,e){var n,i,a=r(t,(function(t,r){if(n)return n(t,r-1);i=t,n=e?function(t,e){var n=mo(t);return function(r,i){return e(n(r),i,t)}}(t,e):mo(t)}));return a.columns=i||[],a},parseRows:r,format:function(e,n){return null==n&&(n=vo(e)),[n.map(o).join(t)].concat(i(e,n)).join("\n")},formatBody:function(t,e){return null==e&&(e=vo(t)),i(t,e).join("\n")},formatRows:function(t){return t.map(a).join("\n")},formatRow:a,formatValue:o}}var xo=_o(","),wo=xo.parse,ko=xo.parseRows,To=xo.format,Eo=xo.formatBody,Co=xo.formatRows,So=xo.formatRow,Ao=xo.formatValue,Mo=_o("\t"),No=Mo.parse,Do=Mo.parseRows,Oo=Mo.format,Bo=Mo.formatBody,Lo=Mo.formatRows,Io=Mo.formatRow,Ro=Mo.formatValue;function Fo(t){for(var e in t){var n,r,i=t[e].trim();if(i)if("true"===i)i=!0;else if("false"===i)i=!1;else if("NaN"===i)i=NaN;else if(isNaN(n=+i)){if(!(r=i.match(/^([-+]\d{2})?\d{4}(-\d{2}(-\d{2})?)?(T\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/)))continue;Po&&r[4]&&!r[7]&&(i=i.replace(/-/g,"/").replace(/T/," ")),i=new Date(i)}else i=n;else i=null;t[e]=i}return t}var Po=new Date("2019-01-01T00:00").getHours()||new Date("2019-07-01T00:00").getHours();function jo(t){return+t}function Yo(t){return t*t}function zo(t){return t*(2-t)}function Uo(t){return((t*=2)<=1?t*t:--t*(2-t)+1)/2}var qo=function t(e){function n(t){return Math.pow(t,e)}return e=+e,n.exponent=t,n}(3),Ho=function t(e){function n(t){return 1-Math.pow(1-t,e)}return e=+e,n.exponent=t,n}(3),$o=function t(e){function n(t){return((t*=2)<=1?Math.pow(t,e):2-Math.pow(2-t,e))/2}return e=+e,n.exponent=t,n}(3),Wo=Math.PI,Vo=Wo/2;function Go(t){return 1==+t?1:1-Math.cos(t*Vo)}function Xo(t){return Math.sin(t*Vo)}function Zo(t){return(1-Math.cos(Wo*t))/2}function Qo(t){return 1.0009775171065494*(Math.pow(2,-10*t)-.0009765625)}function Ko(t){return Qo(1-+t)}function Jo(t){return 1-Qo(t)}function ts(t){return((t*=2)<=1?Qo(1-t):2-Qo(t-1))/2}function es(t){return 1-Math.sqrt(1-t*t)}function ns(t){return Math.sqrt(1- --t*t)}function rs(t){return((t*=2)<=1?1-Math.sqrt(1-t*t):Math.sqrt(1-(t-=2)*t)+1)/2}var is=7.5625;function as(t){return 1-os(1-t)}function os(t){return(t=+t)<.36363636363636365?is*t*t:t<.7272727272727273?is*(t-=.5454545454545454)*t+.75:t<.9090909090909091?is*(t-=.8181818181818182)*t+.9375:is*(t-=.9545454545454546)*t+.984375}function ss(t){return((t*=2)<=1?1-os(1-t):os(t-1)+1)/2}var cs=1.70158,us=function t(e){function n(t){return(t=+t)*t*(e*(t-1)+t)}return e=+e,n.overshoot=t,n}(cs),ls=function t(e){function n(t){return--t*t*((t+1)*e+t)+1}return e=+e,n.overshoot=t,n}(cs),hs=function t(e){function n(t){return((t*=2)<1?t*t*((e+1)*t-e):(t-=2)*t*((e+1)*t+e)+2)/2}return e=+e,n.overshoot=t,n}(cs),fs=2*Math.PI,ds=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=fs);function i(t){return e*Qo(- --t)*Math.sin((r-t)/n)}return i.amplitude=function(e){return t(e,n*fs)},i.period=function(n){return t(e,n)},i}(1,.3),ps=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=fs);function i(t){return 1-e*Qo(t=+t)*Math.sin((t+r)/n)}return i.amplitude=function(e){return t(e,n*fs)},i.period=function(n){return t(e,n)},i}(1,.3),ys=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=fs);function i(t){return((t=2*t-1)<0?e*Qo(-t)*Math.sin((r-t)/n):2-e*Qo(t)*Math.sin((r+t)/n))/2}return i.amplitude=function(e){return t(e,n*fs)},i.period=function(n){return t(e,n)},i}(1,.3);function gs(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.blob()}function ms(t,e){return fetch(t,e).then(gs)}function vs(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.arrayBuffer()}function bs(t,e){return fetch(t,e).then(vs)}function _s(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.text()}function xs(t,e){return fetch(t,e).then(_s)}function ws(t){return function(e,n,r){return 2===arguments.length&&"function"==typeof n&&(r=n,n=void 0),xs(e,n).then((function(e){return t(e,r)}))}}function ks(t,e,n,r){3===arguments.length&&"function"==typeof n&&(r=n,n=void 0);var i=_o(t);return xs(e,n).then((function(t){return i.parse(t,r)}))}var Ts=ws(wo),Es=ws(No);function Cs(t,e){return new Promise((function(n,r){var i=new Image;for(var a in e)i[a]=e[a];i.onerror=r,i.onload=function(){n(i)},i.src=t}))}function Ss(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);if(204!==t.status&&205!==t.status)return t.json()}function As(t,e){return fetch(t,e).then(Ss)}function Ms(t){return function(e,n){return xs(e,n).then((function(e){return(new DOMParser).parseFromString(e,t)}))}}const Ns=Ms("application/xml");var Ds=Ms("text/html"),Os=Ms("image/svg+xml");function Bs(t,e){var n;function r(){var r,i,a=n.length,o=0,s=0;for(r=0;r<a;++r)o+=(i=n[r]).x,s+=i.y;for(o=o/a-t,s=s/a-e,r=0;r<a;++r)(i=n[r]).x-=o,i.y-=s}return null==t&&(t=0),null==e&&(e=0),r.initialize=function(t){n=t},r.x=function(e){return arguments.length?(t=+e,r):t},r.y=function(t){return arguments.length?(e=+t,r):e},r}function Ls(t){return function(){return t}}function Is(){return 1e-6*(Math.random()-.5)}function Rs(t,e,n,r){if(isNaN(e)||isNaN(n))return t;var i,a,o,s,c,u,l,h,f,d=t._root,p={data:r},y=t._x0,g=t._y0,m=t._x1,v=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((u=e>=(a=(y+m)/2))?y=a:m=a,(l=n>=(o=(g+v)/2))?g=o:v=o,i=d,!(d=d[h=l<<1|u]))return i[h]=p,t;if(s=+t._x.call(null,d.data),c=+t._y.call(null,d.data),e===s&&n===c)return p.next=d,i?i[h]=p:t._root=p,t;do{i=i?i[h]=new Array(4):t._root=new Array(4),(u=e>=(a=(y+m)/2))?y=a:m=a,(l=n>=(o=(g+v)/2))?g=o:v=o}while((h=l<<1|u)==(f=(c>=o)<<1|s>=a));return i[f]=d,i[h]=p,t}function Fs(t,e,n,r,i){this.node=t,this.x0=e,this.y0=n,this.x1=r,this.y1=i}function Ps(t){return t[0]}function js(t){return t[1]}function Ys(t,e,n){var r=new zs(null==e?Ps:e,null==n?js:n,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function zs(t,e,n,r,i,a){this._x=t,this._y=e,this._x0=n,this._y0=r,this._x1=i,this._y1=a,this._root=void 0}function Us(t){for(var e={data:t.data},n=e;t=t.next;)n=n.next={data:t.data};return e}var qs=Ys.prototype=zs.prototype;function Hs(t){return t.x+t.vx}function $s(t){return t.y+t.vy}function Ws(t){var e,n,r=1,i=1;function a(){for(var t,a,s,c,u,l,h,f=e.length,d=0;d<i;++d)for(a=Ys(e,Hs,$s).visitAfter(o),t=0;t<f;++t)s=e[t],l=n[s.index],h=l*l,c=s.x+s.vx,u=s.y+s.vy,a.visit(p);function p(t,e,n,i,a){var o=t.data,f=t.r,d=l+f;if(!o)return e>c+d||i<c-d||n>u+d||a<u-d;if(o.index>s.index){var p=c-o.x-o.vx,y=u-o.y-o.vy,g=p*p+y*y;g<d*d&&(0===p&&(g+=(p=Is())*p),0===y&&(g+=(y=Is())*y),g=(d-(g=Math.sqrt(g)))/g*r,s.vx+=(p*=g)*(d=(f*=f)/(h+f)),s.vy+=(y*=g)*d,o.vx-=p*(d=1-d),o.vy-=y*d)}}}function o(t){if(t.data)return t.r=n[t.data.index];for(var e=t.r=0;e<4;++e)t[e]&&t[e].r>t.r&&(t.r=t[e].r)}function s(){if(e){var r,i,a=e.length;for(n=new Array(a),r=0;r<a;++r)i=e[r],n[i.index]=+t(i,r,e)}}return"function"!=typeof t&&(t=Ls(null==t?1:+t)),a.initialize=function(t){e=t,s()},a.iterations=function(t){return arguments.length?(i=+t,a):i},a.strength=function(t){return arguments.length?(r=+t,a):r},a.radius=function(e){return arguments.length?(t="function"==typeof e?e:Ls(+e),s(),a):t},a}function Vs(t){return t.index}function Gs(t,e){var n=t.get(e);if(!n)throw new Error("missing: "+e);return n}function Xs(t){var e,n,r,i,a,o=Vs,s=function(t){return 1/Math.min(i[t.source.index],i[t.target.index])},c=Ls(30),u=1;function l(r){for(var i=0,o=t.length;i<u;++i)for(var s,c,l,h,f,d,p,y=0;y<o;++y)c=(s=t[y]).source,h=(l=s.target).x+l.vx-c.x-c.vx||Is(),f=l.y+l.vy-c.y-c.vy||Is(),h*=d=((d=Math.sqrt(h*h+f*f))-n[y])/d*r*e[y],f*=d,l.vx-=h*(p=a[y]),l.vy-=f*p,c.vx+=h*(p=1-p),c.vy+=f*p}function h(){if(r){var s,c,u=r.length,l=t.length,h=na(r,o);for(s=0,i=new Array(u);s<l;++s)(c=t[s]).index=s,"object"!=typeof c.source&&(c.source=Gs(h,c.source)),"object"!=typeof c.target&&(c.target=Gs(h,c.target)),i[c.source.index]=(i[c.source.index]||0)+1,i[c.target.index]=(i[c.target.index]||0)+1;for(s=0,a=new Array(l);s<l;++s)c=t[s],a[s]=i[c.source.index]/(i[c.source.index]+i[c.target.index]);e=new Array(l),f(),n=new Array(l),d()}}function f(){if(r)for(var n=0,i=t.length;n<i;++n)e[n]=+s(t[n],n,t)}function d(){if(r)for(var e=0,i=t.length;e<i;++e)n[e]=+c(t[e],e,t)}return null==t&&(t=[]),l.initialize=function(t){r=t,h()},l.links=function(e){return arguments.length?(t=e,h(),l):t},l.id=function(t){return arguments.length?(o=t,l):o},l.iterations=function(t){return arguments.length?(u=+t,l):u},l.strength=function(t){return arguments.length?(s="function"==typeof t?t:Ls(+t),f(),l):s},l.distance=function(t){return arguments.length?(c="function"==typeof t?t:Ls(+t),d(),l):c},l}function Zs(t){return t.x}function Qs(t){return t.y}qs.copy=function(){var t,e,n=new zs(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return n;if(!r.length)return n._root=Us(r),n;for(t=[{source:r,target:n._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(e=r.source[i])&&(e.length?t.push({source:e,target:r.target[i]=new Array(4)}):r.target[i]=Us(e));return n},qs.add=function(t){var e=+this._x.call(null,t),n=+this._y.call(null,t);return Rs(this.cover(e,n),e,n,t)},qs.addAll=function(t){var e,n,r,i,a=t.length,o=new Array(a),s=new Array(a),c=1/0,u=1/0,l=-1/0,h=-1/0;for(n=0;n<a;++n)isNaN(r=+this._x.call(null,e=t[n]))||isNaN(i=+this._y.call(null,e))||(o[n]=r,s[n]=i,r<c&&(c=r),r>l&&(l=r),i<u&&(u=i),i>h&&(h=i));if(c>l||u>h)return this;for(this.cover(c,u).cover(l,h),n=0;n<a;++n)Rs(this,o[n],s[n],t[n]);return this},qs.cover=function(t,e){if(isNaN(t=+t)||isNaN(e=+e))return this;var n=this._x0,r=this._y0,i=this._x1,a=this._y1;if(isNaN(n))i=(n=Math.floor(t))+1,a=(r=Math.floor(e))+1;else{for(var o,s,c=i-n,u=this._root;n>t||t>=i||r>e||e>=a;)switch(s=(e<r)<<1|t<n,(o=new Array(4))[s]=u,u=o,c*=2,s){case 0:i=n+c,a=r+c;break;case 1:n=i-c,a=r+c;break;case 2:i=n+c,r=a-c;break;case 3:n=i-c,r=a-c}this._root&&this._root.length&&(this._root=u)}return this._x0=n,this._y0=r,this._x1=i,this._y1=a,this},qs.data=function(){var t=[];return this.visit((function(e){if(!e.length)do{t.push(e.data)}while(e=e.next)})),t},qs.extent=function(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},qs.find=function(t,e,n){var r,i,a,o,s,c,u,l=this._x0,h=this._y0,f=this._x1,d=this._y1,p=[],y=this._root;for(y&&p.push(new Fs(y,l,h,f,d)),null==n?n=1/0:(l=t-n,h=e-n,f=t+n,d=e+n,n*=n);c=p.pop();)if(!(!(y=c.node)||(i=c.x0)>f||(a=c.y0)>d||(o=c.x1)<l||(s=c.y1)<h))if(y.length){var g=(i+o)/2,m=(a+s)/2;p.push(new Fs(y[3],g,m,o,s),new Fs(y[2],i,m,g,s),new Fs(y[1],g,a,o,m),new Fs(y[0],i,a,g,m)),(u=(e>=m)<<1|t>=g)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-u],p[p.length-1-u]=c)}else{var v=t-+this._x.call(null,y.data),b=e-+this._y.call(null,y.data),_=v*v+b*b;if(_<n){var x=Math.sqrt(n=_);l=t-x,h=e-x,f=t+x,d=e+x,r=y.data}}return r},qs.remove=function(t){if(isNaN(a=+this._x.call(null,t))||isNaN(o=+this._y.call(null,t)))return this;var e,n,r,i,a,o,s,c,u,l,h,f,d=this._root,p=this._x0,y=this._y0,g=this._x1,m=this._y1;if(!d)return this;if(d.length)for(;;){if((u=a>=(s=(p+g)/2))?p=s:g=s,(l=o>=(c=(y+m)/2))?y=c:m=c,e=d,!(d=d[h=l<<1|u]))return this;if(!d.length)break;(e[h+1&3]||e[h+2&3]||e[h+3&3])&&(n=e,f=h)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):e?(i?e[h]=i:delete e[h],(d=e[0]||e[1]||e[2]||e[3])&&d===(e[3]||e[2]||e[1]||e[0])&&!d.length&&(n?n[f]=d:this._root=d),this):(this._root=i,this)},qs.removeAll=function(t){for(var e=0,n=t.length;e<n;++e)this.remove(t[e]);return this},qs.root=function(){return this._root},qs.size=function(){var t=0;return this.visit((function(e){if(!e.length)do{++t}while(e=e.next)})),t},qs.visit=function(t){var e,n,r,i,a,o,s=[],c=this._root;for(c&&s.push(new Fs(c,this._x0,this._y0,this._x1,this._y1));e=s.pop();)if(!t(c=e.node,r=e.x0,i=e.y0,a=e.x1,o=e.y1)&&c.length){var u=(r+a)/2,l=(i+o)/2;(n=c[3])&&s.push(new Fs(n,u,l,a,o)),(n=c[2])&&s.push(new Fs(n,r,l,u,o)),(n=c[1])&&s.push(new Fs(n,u,i,a,l)),(n=c[0])&&s.push(new Fs(n,r,i,u,l))}return this},qs.visitAfter=function(t){var e,n=[],r=[];for(this._root&&n.push(new Fs(this._root,this._x0,this._y0,this._x1,this._y1));e=n.pop();){var i=e.node;if(i.length){var a,o=e.x0,s=e.y0,c=e.x1,u=e.y1,l=(o+c)/2,h=(s+u)/2;(a=i[0])&&n.push(new Fs(a,o,s,l,h)),(a=i[1])&&n.push(new Fs(a,l,s,c,h)),(a=i[2])&&n.push(new Fs(a,o,h,l,u)),(a=i[3])&&n.push(new Fs(a,l,h,c,u))}r.push(e)}for(;e=r.pop();)t(e.node,e.x0,e.y0,e.x1,e.y1);return this},qs.x=function(t){return arguments.length?(this._x=t,this):this._x},qs.y=function(t){return arguments.length?(this._y=t,this):this._y};var Ks=Math.PI*(3-Math.sqrt(5));function Js(t){var e,n=1,r=.001,i=1-Math.pow(r,1/300),a=0,o=.6,s=na(),c=Vn(l),u=ft("tick","end");function l(){h(),u.call("tick",e),n<r&&(c.stop(),u.call("end",e))}function h(r){var c,u,l=t.length;void 0===r&&(r=1);for(var h=0;h<r;++h)for(n+=(a-n)*i,s.each((function(t){t(n)})),c=0;c<l;++c)null==(u=t[c]).fx?u.x+=u.vx*=o:(u.x=u.fx,u.vx=0),null==u.fy?u.y+=u.vy*=o:(u.y=u.fy,u.vy=0);return e}function f(){for(var e,n=0,r=t.length;n<r;++n){if((e=t[n]).index=n,null!=e.fx&&(e.x=e.fx),null!=e.fy&&(e.y=e.fy),isNaN(e.x)||isNaN(e.y)){var i=10*Math.sqrt(n),a=n*Ks;e.x=i*Math.cos(a),e.y=i*Math.sin(a)}(isNaN(e.vx)||isNaN(e.vy))&&(e.vx=e.vy=0)}}function d(e){return e.initialize&&e.initialize(t),e}return null==t&&(t=[]),f(),e={tick:h,restart:function(){return c.restart(l),e},stop:function(){return c.stop(),e},nodes:function(n){return arguments.length?(t=n,f(),s.each(d),e):t},alpha:function(t){return arguments.length?(n=+t,e):n},alphaMin:function(t){return arguments.length?(r=+t,e):r},alphaDecay:function(t){return arguments.length?(i=+t,e):+i},alphaTarget:function(t){return arguments.length?(a=+t,e):a},velocityDecay:function(t){return arguments.length?(o=1-t,e):1-o},force:function(t,n){return arguments.length>1?(null==n?s.remove(t):s.set(t,d(n)),e):s.get(t)},find:function(e,n,r){var i,a,o,s,c,u=0,l=t.length;for(null==r?r=1/0:r*=r,u=0;u<l;++u)(o=(i=e-(s=t[u]).x)*i+(a=n-s.y)*a)<r&&(c=s,r=o);return c},on:function(t,n){return arguments.length>1?(u.on(t,n),e):u.on(t)}}}function tc(){var t,e,n,r,i=Ls(-30),a=1,o=1/0,s=.81;function c(r){var i,a=t.length,o=Ys(t,Zs,Qs).visitAfter(l);for(n=r,i=0;i<a;++i)e=t[i],o.visit(h)}function u(){if(t){var e,n,a=t.length;for(r=new Array(a),e=0;e<a;++e)n=t[e],r[n.index]=+i(n,e,t)}}function l(t){var e,n,i,a,o,s=0,c=0;if(t.length){for(i=a=o=0;o<4;++o)(e=t[o])&&(n=Math.abs(e.value))&&(s+=e.value,c+=n,i+=n*e.x,a+=n*e.y);t.x=i/c,t.y=a/c}else{(e=t).x=e.data.x,e.y=e.data.y;do{s+=r[e.data.index]}while(e=e.next)}t.value=s}function h(t,i,c,u){if(!t.value)return!0;var l=t.x-e.x,h=t.y-e.y,f=u-i,d=l*l+h*h;if(f*f/s<d)return d<o&&(0===l&&(d+=(l=Is())*l),0===h&&(d+=(h=Is())*h),d<a&&(d=Math.sqrt(a*d)),e.vx+=l*t.value*n/d,e.vy+=h*t.value*n/d),!0;if(!(t.length||d>=o)){(t.data!==e||t.next)&&(0===l&&(d+=(l=Is())*l),0===h&&(d+=(h=Is())*h),d<a&&(d=Math.sqrt(a*d)));do{t.data!==e&&(f=r[t.data.index]*n/d,e.vx+=l*f,e.vy+=h*f)}while(t=t.next)}}return c.initialize=function(e){t=e,u()},c.strength=function(t){return arguments.length?(i="function"==typeof t?t:Ls(+t),u(),c):i},c.distanceMin=function(t){return arguments.length?(a=t*t,c):Math.sqrt(a)},c.distanceMax=function(t){return arguments.length?(o=t*t,c):Math.sqrt(o)},c.theta=function(t){return arguments.length?(s=t*t,c):Math.sqrt(s)},c}function ec(t,e,n){var r,i,a,o=Ls(.1);function s(t){for(var o=0,s=r.length;o<s;++o){var c=r[o],u=c.x-e||1e-6,l=c.y-n||1e-6,h=Math.sqrt(u*u+l*l),f=(a[o]-h)*i[o]*t/h;c.vx+=u*f,c.vy+=l*f}}function c(){if(r){var e,n=r.length;for(i=new Array(n),a=new Array(n),e=0;e<n;++e)a[e]=+t(r[e],e,r),i[e]=isNaN(a[e])?0:+o(r[e],e,r)}}return"function"!=typeof t&&(t=Ls(+t)),null==e&&(e=0),null==n&&(n=0),s.initialize=function(t){r=t,c()},s.strength=function(t){return arguments.length?(o="function"==typeof t?t:Ls(+t),c(),s):o},s.radius=function(e){return arguments.length?(t="function"==typeof e?e:Ls(+e),c(),s):t},s.x=function(t){return arguments.length?(e=+t,s):e},s.y=function(t){return arguments.length?(n=+t,s):n},s}function nc(t){var e,n,r,i=Ls(.1);function a(t){for(var i,a=0,o=e.length;a<o;++a)(i=e[a]).vx+=(r[a]-i.x)*n[a]*t}function o(){if(e){var a,o=e.length;for(n=new Array(o),r=new Array(o),a=0;a<o;++a)n[a]=isNaN(r[a]=+t(e[a],a,e))?0:+i(e[a],a,e)}}return"function"!=typeof t&&(t=Ls(null==t?0:+t)),a.initialize=function(t){e=t,o()},a.strength=function(t){return arguments.length?(i="function"==typeof t?t:Ls(+t),o(),a):i},a.x=function(e){return arguments.length?(t="function"==typeof e?e:Ls(+e),o(),a):t},a}function rc(t){var e,n,r,i=Ls(.1);function a(t){for(var i,a=0,o=e.length;a<o;++a)(i=e[a]).vy+=(r[a]-i.y)*n[a]*t}function o(){if(e){var a,o=e.length;for(n=new Array(o),r=new Array(o),a=0;a<o;++a)n[a]=isNaN(r[a]=+t(e[a],a,e))?0:+i(e[a],a,e)}}return"function"!=typeof t&&(t=Ls(null==t?0:+t)),a.initialize=function(t){e=t,o()},a.strength=function(t){return arguments.length?(i="function"==typeof t?t:Ls(+t),o(),a):i},a.y=function(e){return arguments.length?(t="function"==typeof e?e:Ls(+e),o(),a):t},a}function ic(t,e){if((n=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var n,r=t.slice(0,n);return[r.length>1?r[0]+r.slice(2):r,+t.slice(n+1)]}function ac(t){return(t=ic(Math.abs(t)))?t[1]:NaN}var oc,sc=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function cc(t){if(!(e=sc.exec(t)))throw new Error("invalid format: "+t);var e;return new uc({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}function uc(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function lc(t,e){var n=ic(t,e);if(!n)return t+"";var r=n[0],i=n[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}cc.prototype=uc.prototype,uc.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};const hc={"%":function(t,e){return(100*t).toFixed(e)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:function(t,e){return t.toExponential(e)},f:function(t,e){return t.toFixed(e)},g:function(t,e){return t.toPrecision(e)},o:function(t){return Math.round(t).toString(8)},p:function(t,e){return lc(100*t,e)},r:lc,s:function(t,e){var n=ic(t,e);if(!n)return t+"";var r=n[0],i=n[1],a=i-(oc=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,o=r.length;return a===o?r:a>o?r+new Array(a-o+1).join("0"):a>0?r.slice(0,a)+"."+r.slice(a):"0."+new Array(1-a).join("0")+ic(t,Math.max(0,e+a-1))[0]},X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}};function fc(t){return t}var dc,pc,yc,gc=Array.prototype.map,mc=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function vc(t){var e,n,r=void 0===t.grouping||void 0===t.thousands?fc:(e=gc.call(t.grouping,Number),n=t.thousands+"",function(t,r){for(var i=t.length,a=[],o=0,s=e[0],c=0;i>0&&s>0&&(c+s+1>r&&(s=Math.max(1,r-c)),a.push(t.substring(i-=s,i+s)),!((c+=s+1)>r));)s=e[o=(o+1)%e.length];return a.reverse().join(n)}),i=void 0===t.currency?"":t.currency[0]+"",a=void 0===t.currency?"":t.currency[1]+"",o=void 0===t.decimal?".":t.decimal+"",s=void 0===t.numerals?fc:function(t){return function(e){return e.replace(/[0-9]/g,(function(e){return t[+e]}))}}(gc.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",u=void 0===t.minus?"-":t.minus+"",l=void 0===t.nan?"NaN":t.nan+"";function h(t){var e=(t=cc(t)).fill,n=t.align,h=t.sign,f=t.symbol,d=t.zero,p=t.width,y=t.comma,g=t.precision,m=t.trim,v=t.type;"n"===v?(y=!0,v="g"):hc[v]||(void 0===g&&(g=12),m=!0,v="g"),(d||"0"===e&&"="===n)&&(d=!0,e="0",n="=");var b="$"===f?i:"#"===f&&/[boxX]/.test(v)?"0"+v.toLowerCase():"",_="$"===f?a:/[%p]/.test(v)?c:"",x=hc[v],w=/[defgprs%]/.test(v);function k(t){var i,a,c,f=b,k=_;if("c"===v)k=x(t)+k,t="";else{var T=(t=+t)<0||1/t<0;if(t=isNaN(t)?l:x(Math.abs(t),g),m&&(t=function(t){t:for(var e,n=t.length,r=1,i=-1;r<n;++r)switch(t[r]){case".":i=e=r;break;case"0":0===i&&(i=r),e=r;break;default:if(!+t[r])break t;i>0&&(i=0)}return i>0?t.slice(0,i)+t.slice(e+1):t}(t)),T&&0==+t&&"+"!==h&&(T=!1),f=(T?"("===h?h:u:"-"===h||"("===h?"":h)+f,k=("s"===v?mc[8+oc/3]:"")+k+(T&&"("===h?")":""),w)for(i=-1,a=t.length;++i<a;)if(48>(c=t.charCodeAt(i))||c>57){k=(46===c?o+t.slice(i+1):t.slice(i))+k,t=t.slice(0,i);break}}y&&!d&&(t=r(t,1/0));var E=f.length+t.length+k.length,C=E<p?new Array(p-E+1).join(e):"";switch(y&&d&&(t=r(C+t,C.length?p-k.length:1/0),C=""),n){case"<":t=f+t+k+C;break;case"=":t=f+C+t+k;break;case"^":t=C.slice(0,E=C.length>>1)+f+t+k+C.slice(E);break;default:t=C+f+t+k}return s(t)}return g=void 0===g?6:/[gprs]/.test(v)?Math.max(1,Math.min(21,g)):Math.max(0,Math.min(20,g)),k.toString=function(){return t+""},k}return{format:h,formatPrefix:function(t,e){var n=h(((t=cc(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(ac(e)/3))),i=Math.pow(10,-r),a=mc[8+r/3];return function(t){return n(i*t)+a}}}}function bc(t){return dc=vc(t),pc=dc.format,yc=dc.formatPrefix,dc}function _c(t){return Math.max(0,-ac(Math.abs(t)))}function xc(t,e){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(ac(e)/3)))-ac(Math.abs(t)))}function wc(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,ac(e)-ac(t))+1}function kc(){return new Tc}function Tc(){this.reset()}bc({decimal:".",thousands:",",grouping:[3],currency:["$",""],minus:"-"}),Tc.prototype={constructor:Tc,reset:function(){this.s=this.t=0},add:function(t){Cc(Ec,t,this.t),Cc(this,Ec.s,this.s),this.s?this.t+=Ec.t:this.s=Ec.t},valueOf:function(){return this.s}};var Ec=new Tc;function Cc(t,e,n){var r=t.s=e+n,i=r-e,a=r-i;t.t=e-a+(n-i)}var Sc=1e-6,Ac=1e-12,Mc=Math.PI,Nc=Mc/2,Dc=Mc/4,Oc=2*Mc,Bc=180/Mc,Lc=Mc/180,Ic=Math.abs,Rc=Math.atan,Fc=Math.atan2,Pc=Math.cos,jc=Math.ceil,Yc=Math.exp,zc=(Math.floor,Math.log),Uc=Math.pow,qc=Math.sin,Hc=Math.sign||function(t){return t>0?1:t<0?-1:0},$c=Math.sqrt,Wc=Math.tan;function Vc(t){return t>1?0:t<-1?Mc:Math.acos(t)}function Gc(t){return t>1?Nc:t<-1?-Nc:Math.asin(t)}function Xc(t){return(t=qc(t/2))*t}function Zc(){}function Qc(t,e){t&&Jc.hasOwnProperty(t.type)&&Jc[t.type](t,e)}var Kc={Feature:function(t,e){Qc(t.geometry,e)},FeatureCollection:function(t,e){for(var n=t.features,r=-1,i=n.length;++r<i;)Qc(n[r].geometry,e)}},Jc={Sphere:function(t,e){e.sphere()},Point:function(t,e){t=t.coordinates,e.point(t[0],t[1],t[2])},MultiPoint:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)t=n[r],e.point(t[0],t[1],t[2])},LineString:function(t,e){tu(t.coordinates,e,0)},MultiLineString:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)tu(n[r],e,0)},Polygon:function(t,e){eu(t.coordinates,e)},MultiPolygon:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)eu(n[r],e)},GeometryCollection:function(t,e){for(var n=t.geometries,r=-1,i=n.length;++r<i;)Qc(n[r],e)}};function tu(t,e,n){var r,i=-1,a=t.length-n;for(e.lineStart();++i<a;)r=t[i],e.point(r[0],r[1],r[2]);e.lineEnd()}function eu(t,e){var n=-1,r=t.length;for(e.polygonStart();++n<r;)tu(t[n],e,1);e.polygonEnd()}function nu(t,e){t&&Kc.hasOwnProperty(t.type)?Kc[t.type](t,e):Qc(t,e)}var ru,iu,au,ou,su,cu=kc(),uu=kc(),lu={point:Zc,lineStart:Zc,lineEnd:Zc,polygonStart:function(){cu.reset(),lu.lineStart=hu,lu.lineEnd=fu},polygonEnd:function(){var t=+cu;uu.add(t<0?Oc+t:t),this.lineStart=this.lineEnd=this.point=Zc},sphere:function(){uu.add(Oc)}};function hu(){lu.point=du}function fu(){pu(ru,iu)}function du(t,e){lu.point=pu,ru=t,iu=e,au=t*=Lc,ou=Pc(e=(e*=Lc)/2+Dc),su=qc(e)}function pu(t,e){var n=(t*=Lc)-au,r=n>=0?1:-1,i=r*n,a=Pc(e=(e*=Lc)/2+Dc),o=qc(e),s=su*o,c=ou*a+s*Pc(i),u=s*r*qc(i);cu.add(Fc(u,c)),au=t,ou=a,su=o}function yu(t){return uu.reset(),nu(t,lu),2*uu}function gu(t){return[Fc(t[1],t[0]),Gc(t[2])]}function mu(t){var e=t[0],n=t[1],r=Pc(n);return[r*Pc(e),r*qc(e),qc(n)]}function vu(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}function bu(t,e){return[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]]}function _u(t,e){t[0]+=e[0],t[1]+=e[1],t[2]+=e[2]}function xu(t,e){return[t[0]*e,t[1]*e,t[2]*e]}function wu(t){var e=$c(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=e,t[1]/=e,t[2]/=e}var ku,Tu,Eu,Cu,Su,Au,Mu,Nu,Du,Ou,Bu,Lu,Iu,Ru,Fu,Pu,ju,Yu,zu,Uu,qu,Hu,$u,Wu,Vu,Gu,Xu=kc(),Zu={point:Qu,lineStart:Ju,lineEnd:tl,polygonStart:function(){Zu.point=el,Zu.lineStart=nl,Zu.lineEnd=rl,Xu.reset(),lu.polygonStart()},polygonEnd:function(){lu.polygonEnd(),Zu.point=Qu,Zu.lineStart=Ju,Zu.lineEnd=tl,cu<0?(ku=-(Eu=180),Tu=-(Cu=90)):Xu>Sc?Cu=90:Xu<-1e-6&&(Tu=-90),Ou[0]=ku,Ou[1]=Eu},sphere:function(){ku=-(Eu=180),Tu=-(Cu=90)}};function Qu(t,e){Du.push(Ou=[ku=t,Eu=t]),e<Tu&&(Tu=e),e>Cu&&(Cu=e)}function Ku(t,e){var n=mu([t*Lc,e*Lc]);if(Nu){var r=bu(Nu,n),i=bu([r[1],-r[0],0],r);wu(i),i=gu(i);var a,o=t-Su,s=o>0?1:-1,c=i[0]*Bc*s,u=Ic(o)>180;u^(s*Su<c&&c<s*t)?(a=i[1]*Bc)>Cu&&(Cu=a):u^(s*Su<(c=(c+360)%360-180)&&c<s*t)?(a=-i[1]*Bc)<Tu&&(Tu=a):(e<Tu&&(Tu=e),e>Cu&&(Cu=e)),u?t<Su?il(ku,t)>il(ku,Eu)&&(Eu=t):il(t,Eu)>il(ku,Eu)&&(ku=t):Eu>=ku?(t<ku&&(ku=t),t>Eu&&(Eu=t)):t>Su?il(ku,t)>il(ku,Eu)&&(Eu=t):il(t,Eu)>il(ku,Eu)&&(ku=t)}else Du.push(Ou=[ku=t,Eu=t]);e<Tu&&(Tu=e),e>Cu&&(Cu=e),Nu=n,Su=t}function Ju(){Zu.point=Ku}function tl(){Ou[0]=ku,Ou[1]=Eu,Zu.point=Qu,Nu=null}function el(t,e){if(Nu){var n=t-Su;Xu.add(Ic(n)>180?n+(n>0?360:-360):n)}else Au=t,Mu=e;lu.point(t,e),Ku(t,e)}function nl(){lu.lineStart()}function rl(){el(Au,Mu),lu.lineEnd(),Ic(Xu)>Sc&&(ku=-(Eu=180)),Ou[0]=ku,Ou[1]=Eu,Nu=null}function il(t,e){return(e-=t)<0?e+360:e}function al(t,e){return t[0]-e[0]}function ol(t,e){return t[0]<=t[1]?t[0]<=e&&e<=t[1]:e<t[0]||t[1]<e}function sl(t){var e,n,r,i,a,o,s;if(Cu=Eu=-(ku=Tu=1/0),Du=[],nu(t,Zu),n=Du.length){for(Du.sort(al),e=1,a=[r=Du[0]];e<n;++e)ol(r,(i=Du[e])[0])||ol(r,i[1])?(il(r[0],i[1])>il(r[0],r[1])&&(r[1]=i[1]),il(i[0],r[1])>il(r[0],r[1])&&(r[0]=i[0])):a.push(r=i);for(o=-1/0,e=0,r=a[n=a.length-1];e<=n;r=i,++e)i=a[e],(s=il(r[1],i[0]))>o&&(o=s,ku=i[0],Eu=r[1])}return Du=Ou=null,ku===1/0||Tu===1/0?[[NaN,NaN],[NaN,NaN]]:[[ku,Tu],[Eu,Cu]]}var cl={sphere:Zc,point:ul,lineStart:hl,lineEnd:pl,polygonStart:function(){cl.lineStart=yl,cl.lineEnd=gl},polygonEnd:function(){cl.lineStart=hl,cl.lineEnd=pl}};function ul(t,e){t*=Lc;var n=Pc(e*=Lc);ll(n*Pc(t),n*qc(t),qc(e))}function ll(t,e,n){++Bu,Iu+=(t-Iu)/Bu,Ru+=(e-Ru)/Bu,Fu+=(n-Fu)/Bu}function hl(){cl.point=fl}function fl(t,e){t*=Lc;var n=Pc(e*=Lc);Wu=n*Pc(t),Vu=n*qc(t),Gu=qc(e),cl.point=dl,ll(Wu,Vu,Gu)}function dl(t,e){t*=Lc;var n=Pc(e*=Lc),r=n*Pc(t),i=n*qc(t),a=qc(e),o=Fc($c((o=Vu*a-Gu*i)*o+(o=Gu*r-Wu*a)*o+(o=Wu*i-Vu*r)*o),Wu*r+Vu*i+Gu*a);Lu+=o,Pu+=o*(Wu+(Wu=r)),ju+=o*(Vu+(Vu=i)),Yu+=o*(Gu+(Gu=a)),ll(Wu,Vu,Gu)}function pl(){cl.point=ul}function yl(){cl.point=ml}function gl(){vl(Hu,$u),cl.point=ul}function ml(t,e){Hu=t,$u=e,t*=Lc,e*=Lc,cl.point=vl;var n=Pc(e);Wu=n*Pc(t),Vu=n*qc(t),Gu=qc(e),ll(Wu,Vu,Gu)}function vl(t,e){t*=Lc;var n=Pc(e*=Lc),r=n*Pc(t),i=n*qc(t),a=qc(e),o=Vu*a-Gu*i,s=Gu*r-Wu*a,c=Wu*i-Vu*r,u=$c(o*o+s*s+c*c),l=Gc(u),h=u&&-l/u;zu+=h*o,Uu+=h*s,qu+=h*c,Lu+=l,Pu+=l*(Wu+(Wu=r)),ju+=l*(Vu+(Vu=i)),Yu+=l*(Gu+(Gu=a)),ll(Wu,Vu,Gu)}function bl(t){Bu=Lu=Iu=Ru=Fu=Pu=ju=Yu=zu=Uu=qu=0,nu(t,cl);var e=zu,n=Uu,r=qu,i=e*e+n*n+r*r;return i<Ac&&(e=Pu,n=ju,r=Yu,Lu<Sc&&(e=Iu,n=Ru,r=Fu),(i=e*e+n*n+r*r)<Ac)?[NaN,NaN]:[Fc(n,e)*Bc,Gc(r/$c(i))*Bc]}function _l(t){return function(){return t}}function xl(t,e){function n(n,r){return n=t(n,r),e(n[0],n[1])}return t.invert&&e.invert&&(n.invert=function(n,r){return(n=e.invert(n,r))&&t.invert(n[0],n[1])}),n}function wl(t,e){return[Ic(t)>Mc?t+Math.round(-t/Oc)*Oc:t,e]}function kl(t,e,n){return(t%=Oc)?e||n?xl(El(t),Cl(e,n)):El(t):e||n?Cl(e,n):wl}function Tl(t){return function(e,n){return[(e+=t)>Mc?e-Oc:e<-Mc?e+Oc:e,n]}}function El(t){var e=Tl(t);return e.invert=Tl(-t),e}function Cl(t,e){var n=Pc(t),r=qc(t),i=Pc(e),a=qc(e);function o(t,e){var o=Pc(e),s=Pc(t)*o,c=qc(t)*o,u=qc(e),l=u*n+s*r;return[Fc(c*i-l*a,s*n-u*r),Gc(l*i+c*a)]}return o.invert=function(t,e){var o=Pc(e),s=Pc(t)*o,c=qc(t)*o,u=qc(e),l=u*i-c*a;return[Fc(c*i+u*a,s*n+l*r),Gc(l*n-s*r)]},o}function Sl(t){function e(e){return(e=t(e[0]*Lc,e[1]*Lc))[0]*=Bc,e[1]*=Bc,e}return t=kl(t[0]*Lc,t[1]*Lc,t.length>2?t[2]*Lc:0),e.invert=function(e){return(e=t.invert(e[0]*Lc,e[1]*Lc))[0]*=Bc,e[1]*=Bc,e},e}function Al(t,e,n,r,i,a){if(n){var o=Pc(e),s=qc(e),c=r*n;null==i?(i=e+r*Oc,a=e-c/2):(i=Ml(o,i),a=Ml(o,a),(r>0?i<a:i>a)&&(i+=r*Oc));for(var u,l=i;r>0?l>a:l<a;l-=c)u=gu([o,-s*Pc(l),-s*qc(l)]),t.point(u[0],u[1])}}function Ml(t,e){(e=mu(e))[0]-=t,wu(e);var n=Vc(-e[1]);return((-e[2]<0?-n:n)+Oc-Sc)%Oc}function Nl(){var t,e,n=_l([0,0]),r=_l(90),i=_l(6),a={point:function(n,r){t.push(n=e(n,r)),n[0]*=Bc,n[1]*=Bc}};function o(){var o=n.apply(this,arguments),s=r.apply(this,arguments)*Lc,c=i.apply(this,arguments)*Lc;return t=[],e=kl(-o[0]*Lc,-o[1]*Lc,0).invert,Al(a,s,c,1),o={type:"Polygon",coordinates:[t]},t=e=null,o}return o.center=function(t){return arguments.length?(n="function"==typeof t?t:_l([+t[0],+t[1]]),o):n},o.radius=function(t){return arguments.length?(r="function"==typeof t?t:_l(+t),o):r},o.precision=function(t){return arguments.length?(i="function"==typeof t?t:_l(+t),o):i},o}function Dl(){var t,e=[];return{point:function(e,n,r){t.push([e,n,r])},lineStart:function(){e.push(t=[])},lineEnd:Zc,rejoin:function(){e.length>1&&e.push(e.pop().concat(e.shift()))},result:function(){var n=e;return e=[],t=null,n}}}function Ol(t,e){return Ic(t[0]-e[0])<Sc&&Ic(t[1]-e[1])<Sc}function Bl(t,e,n,r){this.x=t,this.z=e,this.o=n,this.e=r,this.v=!1,this.n=this.p=null}function Ll(t,e,n,r,i){var a,o,s=[],c=[];if(t.forEach((function(t){if(!((e=t.length-1)<=0)){var e,n,r=t[0],o=t[e];if(Ol(r,o)){if(!r[2]&&!o[2]){for(i.lineStart(),a=0;a<e;++a)i.point((r=t[a])[0],r[1]);return void i.lineEnd()}o[0]+=2e-6}s.push(n=new Bl(r,t,null,!0)),c.push(n.o=new Bl(r,null,n,!1)),s.push(n=new Bl(o,t,null,!1)),c.push(n.o=new Bl(o,null,n,!0))}})),s.length){for(c.sort(e),Il(s),Il(c),a=0,o=c.length;a<o;++a)c[a].e=n=!n;for(var u,l,h=s[0];;){for(var f=h,d=!0;f.v;)if((f=f.n)===h)return;u=f.z,i.lineStart();do{if(f.v=f.o.v=!0,f.e){if(d)for(a=0,o=u.length;a<o;++a)i.point((l=u[a])[0],l[1]);else r(f.x,f.n.x,1,i);f=f.n}else{if(d)for(u=f.p.z,a=u.length-1;a>=0;--a)i.point((l=u[a])[0],l[1]);else r(f.x,f.p.x,-1,i);f=f.p}u=(f=f.o).z,d=!d}while(!f.v);i.lineEnd()}}}function Il(t){if(e=t.length){for(var e,n,r=0,i=t[0];++r<e;)i.n=n=t[r],n.p=i,i=n;i.n=n=t[0],n.p=i}}wl.invert=wl;var Rl=kc();function Fl(t){return Ic(t[0])<=Mc?t[0]:Hc(t[0])*((Ic(t[0])+Mc)%Oc-Mc)}function Pl(t,e){var n=Fl(e),r=e[1],i=qc(r),a=[qc(n),-Pc(n),0],o=0,s=0;Rl.reset(),1===i?r=Nc+Sc:-1===i&&(r=-Nc-Sc);for(var c=0,u=t.length;c<u;++c)if(h=(l=t[c]).length)for(var l,h,f=l[h-1],d=Fl(f),p=f[1]/2+Dc,y=qc(p),g=Pc(p),m=0;m<h;++m,d=b,y=x,g=w,f=v){var v=l[m],b=Fl(v),_=v[1]/2+Dc,x=qc(_),w=Pc(_),k=b-d,T=k>=0?1:-1,E=T*k,C=E>Mc,S=y*x;if(Rl.add(Fc(S*T*qc(E),g*w+S*Pc(E))),o+=C?k+T*Oc:k,C^d>=n^b>=n){var A=bu(mu(f),mu(v));wu(A);var M=bu(a,A);wu(M);var N=(C^k>=0?-1:1)*Gc(M[2]);(r>N||r===N&&(A[0]||A[1]))&&(s+=C^k>=0?1:-1)}}return(o<-1e-6||o<Sc&&Rl<-1e-6)^1&s}function jl(t,e,n,r){return function(i){var a,o,s,c=e(i),u=Dl(),l=e(u),h=!1,f={point:d,lineStart:y,lineEnd:g,polygonStart:function(){f.point=m,f.lineStart=v,f.lineEnd=b,o=[],a=[]},polygonEnd:function(){f.point=d,f.lineStart=y,f.lineEnd=g,o=P(o);var t=Pl(a,r);o.length?(h||(i.polygonStart(),h=!0),Ll(o,zl,t,n,i)):t&&(h||(i.polygonStart(),h=!0),i.lineStart(),n(null,null,1,i),i.lineEnd()),h&&(i.polygonEnd(),h=!1),o=a=null},sphere:function(){i.polygonStart(),i.lineStart(),n(null,null,1,i),i.lineEnd(),i.polygonEnd()}};function d(e,n){t(e,n)&&i.point(e,n)}function p(t,e){c.point(t,e)}function y(){f.point=p,c.lineStart()}function g(){f.point=d,c.lineEnd()}function m(t,e){s.push([t,e]),l.point(t,e)}function v(){l.lineStart(),s=[]}function b(){m(s[0][0],s[0][1]),l.lineEnd();var t,e,n,r,c=l.clean(),f=u.result(),d=f.length;if(s.pop(),a.push(s),s=null,d)if(1&c){if((e=(n=f[0]).length-1)>0){for(h||(i.polygonStart(),h=!0),i.lineStart(),t=0;t<e;++t)i.point((r=n[t])[0],r[1]);i.lineEnd()}}else d>1&&2&c&&f.push(f.pop().concat(f.shift())),o.push(f.filter(Yl))}return f}}function Yl(t){return t.length>1}function zl(t,e){return((t=t.x)[0]<0?t[1]-Nc-Sc:Nc-t[1])-((e=e.x)[0]<0?e[1]-Nc-Sc:Nc-e[1])}const Ul=jl((function(){return!0}),(function(t){var e,n=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),e=1},point:function(a,o){var s=a>0?Mc:-Mc,c=Ic(a-n);Ic(c-Mc)<Sc?(t.point(n,r=(r+o)/2>0?Nc:-Nc),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(s,r),t.point(a,r),e=0):i!==s&&c>=Mc&&(Ic(n-i)<Sc&&(n-=i*Sc),Ic(a-s)<Sc&&(a-=s*Sc),r=function(t,e,n,r){var i,a,o=qc(t-n);return Ic(o)>Sc?Rc((qc(e)*(a=Pc(r))*qc(n)-qc(r)*(i=Pc(e))*qc(t))/(i*a*o)):(e+r)/2}(n,r,a,o),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(s,r),e=0),t.point(n=a,r=o),i=s},lineEnd:function(){t.lineEnd(),n=r=NaN},clean:function(){return 2-e}}}),(function(t,e,n,r){var i;if(null==t)i=n*Nc,r.point(-Mc,i),r.point(0,i),r.point(Mc,i),r.point(Mc,0),r.point(Mc,-i),r.point(0,-i),r.point(-Mc,-i),r.point(-Mc,0),r.point(-Mc,i);else if(Ic(t[0]-e[0])>Sc){var a=t[0]<e[0]?Mc:-Mc;i=n*a/2,r.point(-a,i),r.point(0,i),r.point(a,i)}else r.point(e[0],e[1])}),[-Mc,-Nc]);function ql(t){var e=Pc(t),n=6*Lc,r=e>0,i=Ic(e)>Sc;function a(t,n){return Pc(t)*Pc(n)>e}function o(t,n,r){var i=[1,0,0],a=bu(mu(t),mu(n)),o=vu(a,a),s=a[0],c=o-s*s;if(!c)return!r&&t;var u=e*o/c,l=-e*s/c,h=bu(i,a),f=xu(i,u);_u(f,xu(a,l));var d=h,p=vu(f,d),y=vu(d,d),g=p*p-y*(vu(f,f)-1);if(!(g<0)){var m=$c(g),v=xu(d,(-p-m)/y);if(_u(v,f),v=gu(v),!r)return v;var b,_=t[0],x=n[0],w=t[1],k=n[1];x<_&&(b=_,_=x,x=b);var T=x-_,E=Ic(T-Mc)<Sc;if(!E&&k<w&&(b=w,w=k,k=b),E||T<Sc?E?w+k>0^v[1]<(Ic(v[0]-_)<Sc?w:k):w<=v[1]&&v[1]<=k:T>Mc^(_<=v[0]&&v[0]<=x)){var C=xu(d,(-p+m)/y);return _u(C,f),[v,gu(C)]}}}function s(e,n){var i=r?t:Mc-t,a=0;return e<-i?a|=1:e>i&&(a|=2),n<-i?a|=4:n>i&&(a|=8),a}return jl(a,(function(t){var e,n,c,u,l;return{lineStart:function(){u=c=!1,l=1},point:function(h,f){var d,p=[h,f],y=a(h,f),g=r?y?0:s(h,f):y?s(h+(h<0?Mc:-Mc),f):0;if(!e&&(u=c=y)&&t.lineStart(),y!==c&&(!(d=o(e,p))||Ol(e,d)||Ol(p,d))&&(p[2]=1),y!==c)l=0,y?(t.lineStart(),d=o(p,e),t.point(d[0],d[1])):(d=o(e,p),t.point(d[0],d[1],2),t.lineEnd()),e=d;else if(i&&e&&r^y){var m;g&n||!(m=o(p,e,!0))||(l=0,r?(t.lineStart(),t.point(m[0][0],m[0][1]),t.point(m[1][0],m[1][1]),t.lineEnd()):(t.point(m[1][0],m[1][1]),t.lineEnd(),t.lineStart(),t.point(m[0][0],m[0][1],3)))}!y||e&&Ol(e,p)||t.point(p[0],p[1]),e=p,c=y,n=g},lineEnd:function(){c&&t.lineEnd(),e=null},clean:function(){return l|(u&&c)<<1}}}),(function(e,r,i,a){Al(a,t,n,i,e,r)}),r?[0,-t]:[-Mc,t-Mc])}var Hl=1e9,$l=-Hl;function Wl(t,e,n,r){function i(i,a){return t<=i&&i<=n&&e<=a&&a<=r}function a(i,a,s,u){var l=0,h=0;if(null==i||(l=o(i,s))!==(h=o(a,s))||c(i,a)<0^s>0)do{u.point(0===l||3===l?t:n,l>1?r:e)}while((l=(l+s+4)%4)!==h);else u.point(a[0],a[1])}function o(r,i){return Ic(r[0]-t)<Sc?i>0?0:3:Ic(r[0]-n)<Sc?i>0?2:1:Ic(r[1]-e)<Sc?i>0?1:0:i>0?3:2}function s(t,e){return c(t.x,e.x)}function c(t,e){var n=o(t,1),r=o(e,1);return n!==r?n-r:0===n?e[1]-t[1]:1===n?t[0]-e[0]:2===n?t[1]-e[1]:e[0]-t[0]}return function(o){var c,u,l,h,f,d,p,y,g,m,v,b=o,_=Dl(),x={point:w,lineStart:function(){x.point=k,u&&u.push(l=[]),m=!0,g=!1,p=y=NaN},lineEnd:function(){c&&(k(h,f),d&&g&&_.rejoin(),c.push(_.result())),x.point=w,g&&b.lineEnd()},polygonStart:function(){b=_,c=[],u=[],v=!0},polygonEnd:function(){var e=function(){for(var e=0,n=0,i=u.length;n<i;++n)for(var a,o,s=u[n],c=1,l=s.length,h=s[0],f=h[0],d=h[1];c<l;++c)a=f,o=d,f=(h=s[c])[0],d=h[1],o<=r?d>r&&(f-a)*(r-o)>(d-o)*(t-a)&&++e:d<=r&&(f-a)*(r-o)<(d-o)*(t-a)&&--e;return e}(),n=v&&e,i=(c=P(c)).length;(n||i)&&(o.polygonStart(),n&&(o.lineStart(),a(null,null,1,o),o.lineEnd()),i&&Ll(c,s,e,a,o),o.polygonEnd()),b=o,c=u=l=null}};function w(t,e){i(t,e)&&b.point(t,e)}function k(a,o){var s=i(a,o);if(u&&l.push([a,o]),m)h=a,f=o,d=s,m=!1,s&&(b.lineStart(),b.point(a,o));else if(s&&g)b.point(a,o);else{var c=[p=Math.max($l,Math.min(Hl,p)),y=Math.max($l,Math.min(Hl,y))],_=[a=Math.max($l,Math.min(Hl,a)),o=Math.max($l,Math.min(Hl,o))];!function(t,e,n,r,i,a){var o,s=t[0],c=t[1],u=0,l=1,h=e[0]-s,f=e[1]-c;if(o=n-s,h||!(o>0)){if(o/=h,h<0){if(o<u)return;o<l&&(l=o)}else if(h>0){if(o>l)return;o>u&&(u=o)}if(o=i-s,h||!(o<0)){if(o/=h,h<0){if(o>l)return;o>u&&(u=o)}else if(h>0){if(o<u)return;o<l&&(l=o)}if(o=r-c,f||!(o>0)){if(o/=f,f<0){if(o<u)return;o<l&&(l=o)}else if(f>0){if(o>l)return;o>u&&(u=o)}if(o=a-c,f||!(o<0)){if(o/=f,f<0){if(o>l)return;o>u&&(u=o)}else if(f>0){if(o<u)return;o<l&&(l=o)}return u>0&&(t[0]=s+u*h,t[1]=c+u*f),l<1&&(e[0]=s+l*h,e[1]=c+l*f),!0}}}}}(c,_,t,e,n,r)?s&&(b.lineStart(),b.point(a,o),v=!1):(g||(b.lineStart(),b.point(c[0],c[1])),b.point(_[0],_[1]),s||b.lineEnd(),v=!1)}p=a,y=o,g=s}return x}}function Vl(){var t,e,n,r=0,i=0,a=960,o=500;return n={stream:function(n){return t&&e===n?t:t=Wl(r,i,a,o)(e=n)},extent:function(s){return arguments.length?(r=+s[0][0],i=+s[0][1],a=+s[1][0],o=+s[1][1],t=e=null,n):[[r,i],[a,o]]}}}var Gl,Xl,Zl,Ql=kc(),Kl={sphere:Zc,point:Zc,lineStart:function(){Kl.point=th,Kl.lineEnd=Jl},lineEnd:Zc,polygonStart:Zc,polygonEnd:Zc};function Jl(){Kl.point=Kl.lineEnd=Zc}function th(t,e){Gl=t*=Lc,Xl=qc(e*=Lc),Zl=Pc(e),Kl.point=eh}function eh(t,e){t*=Lc;var n=qc(e*=Lc),r=Pc(e),i=Ic(t-Gl),a=Pc(i),o=r*qc(i),s=Zl*n-Xl*r*a,c=Xl*n+Zl*r*a;Ql.add(Fc($c(o*o+s*s),c)),Gl=t,Xl=n,Zl=r}function nh(t){return Ql.reset(),nu(t,Kl),+Ql}var rh=[null,null],ih={type:"LineString",coordinates:rh};function ah(t,e){return rh[0]=t,rh[1]=e,nh(ih)}var oh={Feature:function(t,e){return ch(t.geometry,e)},FeatureCollection:function(t,e){for(var n=t.features,r=-1,i=n.length;++r<i;)if(ch(n[r].geometry,e))return!0;return!1}},sh={Sphere:function(){return!0},Point:function(t,e){return uh(t.coordinates,e)},MultiPoint:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if(uh(n[r],e))return!0;return!1},LineString:function(t,e){return lh(t.coordinates,e)},MultiLineString:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if(lh(n[r],e))return!0;return!1},Polygon:function(t,e){return hh(t.coordinates,e)},MultiPolygon:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if(hh(n[r],e))return!0;return!1},GeometryCollection:function(t,e){for(var n=t.geometries,r=-1,i=n.length;++r<i;)if(ch(n[r],e))return!0;return!1}};function ch(t,e){return!(!t||!sh.hasOwnProperty(t.type))&&sh[t.type](t,e)}function uh(t,e){return 0===ah(t,e)}function lh(t,e){for(var n,r,i,a=0,o=t.length;a<o;a++){if(0===(r=ah(t[a],e)))return!0;if(a>0&&(i=ah(t[a],t[a-1]))>0&&n<=i&&r<=i&&(n+r-i)*(1-Math.pow((n-r)/i,2))<Ac*i)return!0;n=r}return!1}function hh(t,e){return!!Pl(t.map(fh),dh(e))}function fh(t){return(t=t.map(dh)).pop(),t}function dh(t){return[t[0]*Lc,t[1]*Lc]}function ph(t,e){return(t&&oh.hasOwnProperty(t.type)?oh[t.type]:ch)(t,e)}function yh(t,e,n){var r=k(t,e-Sc,n).concat(e);return function(t){return r.map((function(e){return[t,e]}))}}function gh(t,e,n){var r=k(t,e-Sc,n).concat(e);return function(t){return r.map((function(e){return[e,t]}))}}function mh(){var t,e,n,r,i,a,o,s,c,u,l,h,f=10,d=f,p=90,y=360,g=2.5;function m(){return{type:"MultiLineString",coordinates:v()}}function v(){return k(jc(r/p)*p,n,p).map(l).concat(k(jc(s/y)*y,o,y).map(h)).concat(k(jc(e/f)*f,t,f).filter((function(t){return Ic(t%p)>Sc})).map(c)).concat(k(jc(a/d)*d,i,d).filter((function(t){return Ic(t%y)>Sc})).map(u))}return m.lines=function(){return v().map((function(t){return{type:"LineString",coordinates:t}}))},m.outline=function(){return{type:"Polygon",coordinates:[l(r).concat(h(o).slice(1),l(n).reverse().slice(1),h(s).reverse().slice(1))]}},m.extent=function(t){return arguments.length?m.extentMajor(t).extentMinor(t):m.extentMinor()},m.extentMajor=function(t){return arguments.length?(r=+t[0][0],n=+t[1][0],s=+t[0][1],o=+t[1][1],r>n&&(t=r,r=n,n=t),s>o&&(t=s,s=o,o=t),m.precision(g)):[[r,s],[n,o]]},m.extentMinor=function(n){return arguments.length?(e=+n[0][0],t=+n[1][0],a=+n[0][1],i=+n[1][1],e>t&&(n=e,e=t,t=n),a>i&&(n=a,a=i,i=n),m.precision(g)):[[e,a],[t,i]]},m.step=function(t){return arguments.length?m.stepMajor(t).stepMinor(t):m.stepMinor()},m.stepMajor=function(t){return arguments.length?(p=+t[0],y=+t[1],m):[p,y]},m.stepMinor=function(t){return arguments.length?(f=+t[0],d=+t[1],m):[f,d]},m.precision=function(f){return arguments.length?(g=+f,c=yh(a,i,90),u=gh(e,t,g),l=yh(s,o,90),h=gh(r,n,g),m):g},m.extentMajor([[-180,-89.999999],[180,89.999999]]).extentMinor([[-180,-80.000001],[180,80.000001]])}function vh(){return mh()()}function bh(t,e){var n=t[0]*Lc,r=t[1]*Lc,i=e[0]*Lc,a=e[1]*Lc,o=Pc(r),s=qc(r),c=Pc(a),u=qc(a),l=o*Pc(n),h=o*qc(n),f=c*Pc(i),d=c*qc(i),p=2*Gc($c(Xc(a-r)+o*c*Xc(i-n))),y=qc(p),g=p?function(t){var e=qc(t*=p)/y,n=qc(p-t)/y,r=n*l+e*f,i=n*h+e*d,a=n*s+e*u;return[Fc(i,r)*Bc,Fc(a,$c(r*r+i*i))*Bc]}:function(){return[n*Bc,r*Bc]};return g.distance=p,g}function _h(t){return t}var xh,wh,kh,Th,Eh=kc(),Ch=kc(),Sh={point:Zc,lineStart:Zc,lineEnd:Zc,polygonStart:function(){Sh.lineStart=Ah,Sh.lineEnd=Dh},polygonEnd:function(){Sh.lineStart=Sh.lineEnd=Sh.point=Zc,Eh.add(Ic(Ch)),Ch.reset()},result:function(){var t=Eh/2;return Eh.reset(),t}};function Ah(){Sh.point=Mh}function Mh(t,e){Sh.point=Nh,xh=kh=t,wh=Th=e}function Nh(t,e){Ch.add(Th*t-kh*e),kh=t,Th=e}function Dh(){Nh(xh,wh)}const Oh=Sh;var Bh=1/0,Lh=Bh,Ih=-Bh,Rh=Ih,Fh={point:function(t,e){t<Bh&&(Bh=t),t>Ih&&(Ih=t),e<Lh&&(Lh=e),e>Rh&&(Rh=e)},lineStart:Zc,lineEnd:Zc,polygonStart:Zc,polygonEnd:Zc,result:function(){var t=[[Bh,Lh],[Ih,Rh]];return Ih=Rh=-(Lh=Bh=1/0),t}};const Ph=Fh;var jh,Yh,zh,Uh,qh=0,Hh=0,$h=0,Wh=0,Vh=0,Gh=0,Xh=0,Zh=0,Qh=0,Kh={point:Jh,lineStart:tf,lineEnd:rf,polygonStart:function(){Kh.lineStart=af,Kh.lineEnd=of},polygonEnd:function(){Kh.point=Jh,Kh.lineStart=tf,Kh.lineEnd=rf},result:function(){var t=Qh?[Xh/Qh,Zh/Qh]:Gh?[Wh/Gh,Vh/Gh]:$h?[qh/$h,Hh/$h]:[NaN,NaN];return qh=Hh=$h=Wh=Vh=Gh=Xh=Zh=Qh=0,t}};function Jh(t,e){qh+=t,Hh+=e,++$h}function tf(){Kh.point=ef}function ef(t,e){Kh.point=nf,Jh(zh=t,Uh=e)}function nf(t,e){var n=t-zh,r=e-Uh,i=$c(n*n+r*r);Wh+=i*(zh+t)/2,Vh+=i*(Uh+e)/2,Gh+=i,Jh(zh=t,Uh=e)}function rf(){Kh.point=Jh}function af(){Kh.point=sf}function of(){cf(jh,Yh)}function sf(t,e){Kh.point=cf,Jh(jh=zh=t,Yh=Uh=e)}function cf(t,e){var n=t-zh,r=e-Uh,i=$c(n*n+r*r);Wh+=i*(zh+t)/2,Vh+=i*(Uh+e)/2,Gh+=i,Xh+=(i=Uh*t-zh*e)*(zh+t),Zh+=i*(Uh+e),Qh+=3*i,Jh(zh=t,Uh=e)}const uf=Kh;function lf(t){this._context=t}lf.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,e){switch(this._point){case 0:this._context.moveTo(t,e),this._point=1;break;case 1:this._context.lineTo(t,e);break;default:this._context.moveTo(t+this._radius,e),this._context.arc(t,e,this._radius,0,Oc)}},result:Zc};var hf,ff,df,pf,yf,gf=kc(),mf={point:Zc,lineStart:function(){mf.point=vf},lineEnd:function(){hf&&bf(ff,df),mf.point=Zc},polygonStart:function(){hf=!0},polygonEnd:function(){hf=null},result:function(){var t=+gf;return gf.reset(),t}};function vf(t,e){mf.point=bf,ff=pf=t,df=yf=e}function bf(t,e){pf-=t,yf-=e,gf.add($c(pf*pf+yf*yf)),pf=t,yf=e}const _f=mf;function xf(){this._string=[]}function wf(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function kf(t,e){var n,r,i=4.5;function a(t){return t&&("function"==typeof i&&r.pointRadius(+i.apply(this,arguments)),nu(t,n(r))),r.result()}return a.area=function(t){return nu(t,n(Oh)),Oh.result()},a.measure=function(t){return nu(t,n(_f)),_f.result()},a.bounds=function(t){return nu(t,n(Ph)),Ph.result()},a.centroid=function(t){return nu(t,n(uf)),uf.result()},a.projection=function(e){return arguments.length?(n=null==e?(t=null,_h):(t=e).stream,a):t},a.context=function(t){return arguments.length?(r=null==t?(e=null,new xf):new lf(e=t),"function"!=typeof i&&r.pointRadius(i),a):e},a.pointRadius=function(t){return arguments.length?(i="function"==typeof t?t:(r.pointRadius(+t),+t),a):i},a.projection(t).context(e)}function Tf(t){return{stream:Ef(t)}}function Ef(t){return function(e){var n=new Cf;for(var r in t)n[r]=t[r];return n.stream=e,n}}function Cf(){}function Sf(t,e,n){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),nu(n,t.stream(Ph)),e(Ph.result()),null!=r&&t.clipExtent(r),t}function Af(t,e,n){return Sf(t,(function(n){var r=e[1][0]-e[0][0],i=e[1][1]-e[0][1],a=Math.min(r/(n[1][0]-n[0][0]),i/(n[1][1]-n[0][1])),o=+e[0][0]+(r-a*(n[1][0]+n[0][0]))/2,s=+e[0][1]+(i-a*(n[1][1]+n[0][1]))/2;t.scale(150*a).translate([o,s])}),n)}function Mf(t,e,n){return Af(t,[[0,0],e],n)}function Nf(t,e,n){return Sf(t,(function(n){var r=+e,i=r/(n[1][0]-n[0][0]),a=(r-i*(n[1][0]+n[0][0]))/2,o=-i*n[0][1];t.scale(150*i).translate([a,o])}),n)}function Df(t,e,n){return Sf(t,(function(n){var r=+e,i=r/(n[1][1]-n[0][1]),a=-i*n[0][0],o=(r-i*(n[1][1]+n[0][1]))/2;t.scale(150*i).translate([a,o])}),n)}xf.prototype={_radius:4.5,_circle:wf(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,e){switch(this._point){case 0:this._string.push("M",t,",",e),this._point=1;break;case 1:this._string.push("L",t,",",e);break;default:null==this._circle&&(this._circle=wf(this._radius)),this._string.push("M",t,",",e,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}},Cf.prototype={constructor:Cf,point:function(t,e){this.stream.point(t,e)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var Of=Pc(30*Lc);function Bf(t,e){return+e?function(t,e){function n(r,i,a,o,s,c,u,l,h,f,d,p,y,g){var m=u-r,v=l-i,b=m*m+v*v;if(b>4*e&&y--){var _=o+f,x=s+d,w=c+p,k=$c(_*_+x*x+w*w),T=Gc(w/=k),E=Ic(Ic(w)-1)<Sc||Ic(a-h)<Sc?(a+h)/2:Fc(x,_),C=t(E,T),S=C[0],A=C[1],M=S-r,N=A-i,D=v*M-m*N;(D*D/b>e||Ic((m*M+v*N)/b-.5)>.3||o*f+s*d+c*p<Of)&&(n(r,i,a,o,s,c,S,A,E,_/=k,x/=k,w,y,g),g.point(S,A),n(S,A,E,_,x,w,u,l,h,f,d,p,y,g))}}return function(e){var r,i,a,o,s,c,u,l,h,f,d,p,y={point:g,lineStart:m,lineEnd:b,polygonStart:function(){e.polygonStart(),y.lineStart=_},polygonEnd:function(){e.polygonEnd(),y.lineStart=m}};function g(n,r){n=t(n,r),e.point(n[0],n[1])}function m(){l=NaN,y.point=v,e.lineStart()}function v(r,i){var a=mu([r,i]),o=t(r,i);n(l,h,u,f,d,p,l=o[0],h=o[1],u=r,f=a[0],d=a[1],p=a[2],16,e),e.point(l,h)}function b(){y.point=g,e.lineEnd()}function _(){m(),y.point=x,y.lineEnd=w}function x(t,e){v(r=t,e),i=l,a=h,o=f,s=d,c=p,y.point=v}function w(){n(l,h,u,f,d,p,i,a,r,o,s,c,16,e),y.lineEnd=b,b()}return y}}(t,e):function(t){return Ef({point:function(e,n){e=t(e,n),this.stream.point(e[0],e[1])}})}(t)}var Lf=Ef({point:function(t,e){this.stream.point(t*Lc,e*Lc)}});function If(t,e,n,r,i){function a(a,o){return[e+t*(a*=r),n-t*(o*=i)]}return a.invert=function(a,o){return[(a-e)/t*r,(n-o)/t*i]},a}function Rf(t,e,n,r,i,a){var o=Pc(a),s=qc(a),c=o*t,u=s*t,l=o/t,h=s/t,f=(s*n-o*e)/t,d=(s*e+o*n)/t;function p(t,a){return[c*(t*=r)-u*(a*=i)+e,n-u*t-c*a]}return p.invert=function(t,e){return[r*(l*t-h*e+f),i*(d-h*t-l*e)]},p}function Ff(t){return Pf((function(){return t}))()}function Pf(t){var e,n,r,i,a,o,s,c,u,l,h=150,f=480,d=250,p=0,y=0,g=0,m=0,v=0,b=0,_=1,x=1,w=null,k=Ul,T=null,E=_h,C=.5;function S(t){return c(t[0]*Lc,t[1]*Lc)}function A(t){return(t=c.invert(t[0],t[1]))&&[t[0]*Bc,t[1]*Bc]}function M(){var t=Rf(h,0,0,_,x,b).apply(null,e(p,y)),r=(b?Rf:If)(h,f-t[0],d-t[1],_,x,b);return n=kl(g,m,v),s=xl(e,r),c=xl(n,s),o=Bf(s,C),N()}function N(){return u=l=null,S}return S.stream=function(t){return u&&l===t?u:u=Lf(function(t){return Ef({point:function(e,n){var r=t(e,n);return this.stream.point(r[0],r[1])}})}(n)(k(o(E(l=t)))))},S.preclip=function(t){return arguments.length?(k=t,w=void 0,N()):k},S.postclip=function(t){return arguments.length?(E=t,T=r=i=a=null,N()):E},S.clipAngle=function(t){return arguments.length?(k=+t?ql(w=t*Lc):(w=null,Ul),N()):w*Bc},S.clipExtent=function(t){return arguments.length?(E=null==t?(T=r=i=a=null,_h):Wl(T=+t[0][0],r=+t[0][1],i=+t[1][0],a=+t[1][1]),N()):null==T?null:[[T,r],[i,a]]},S.scale=function(t){return arguments.length?(h=+t,M()):h},S.translate=function(t){return arguments.length?(f=+t[0],d=+t[1],M()):[f,d]},S.center=function(t){return arguments.length?(p=t[0]%360*Lc,y=t[1]%360*Lc,M()):[p*Bc,y*Bc]},S.rotate=function(t){return arguments.length?(g=t[0]%360*Lc,m=t[1]%360*Lc,v=t.length>2?t[2]%360*Lc:0,M()):[g*Bc,m*Bc,v*Bc]},S.angle=function(t){return arguments.length?(b=t%360*Lc,M()):b*Bc},S.reflectX=function(t){return arguments.length?(_=t?-1:1,M()):_<0},S.reflectY=function(t){return arguments.length?(x=t?-1:1,M()):x<0},S.precision=function(t){return arguments.length?(o=Bf(s,C=t*t),N()):$c(C)},S.fitExtent=function(t,e){return Af(S,t,e)},S.fitSize=function(t,e){return Mf(S,t,e)},S.fitWidth=function(t,e){return Nf(S,t,e)},S.fitHeight=function(t,e){return Df(S,t,e)},function(){return e=t.apply(this,arguments),S.invert=e.invert&&A,M()}}function jf(t){var e=0,n=Mc/3,r=Pf(t),i=r(e,n);return i.parallels=function(t){return arguments.length?r(e=t[0]*Lc,n=t[1]*Lc):[e*Bc,n*Bc]},i}function Yf(t,e){var n=qc(t),r=(n+qc(e))/2;if(Ic(r)<Sc)return function(t){var e=Pc(t);function n(t,n){return[t*e,qc(n)/e]}return n.invert=function(t,n){return[t/e,Gc(n*e)]},n}(t);var i=1+n*(2*r-n),a=$c(i)/r;function o(t,e){var n=$c(i-2*r*qc(e))/r;return[n*qc(t*=r),a-n*Pc(t)]}return o.invert=function(t,e){var n=a-e,o=Fc(t,Ic(n))*Hc(n);return n*r<0&&(o-=Mc*Hc(t)*Hc(n)),[o/r,Gc((i-(t*t+n*n)*r*r)/(2*r))]},o}function zf(){return jf(Yf).scale(155.424).center([0,33.6442])}function Uf(){return zf().parallels([29.5,45.5]).scale(1070).translate([480,250]).rotate([96,0]).center([-.6,38.7])}function qf(){var t,e,n,r,i,a,o=Uf(),s=zf().rotate([154,0]).center([-2,58.5]).parallels([55,65]),c=zf().rotate([157,0]).center([-3,19.9]).parallels([8,18]),u={point:function(t,e){a=[t,e]}};function l(t){var e=t[0],o=t[1];return a=null,n.point(e,o),a||(r.point(e,o),a)||(i.point(e,o),a)}function h(){return t=e=null,l}return l.invert=function(t){var e=o.scale(),n=o.translate(),r=(t[0]-n[0])/e,i=(t[1]-n[1])/e;return(i>=.12&&i<.234&&r>=-.425&&r<-.214?s:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:o).invert(t)},l.stream=function(n){return t&&e===n?t:(r=[o.stream(e=n),s.stream(n),c.stream(n)],i=r.length,t={point:function(t,e){for(var n=-1;++n<i;)r[n].point(t,e)},sphere:function(){for(var t=-1;++t<i;)r[t].sphere()},lineStart:function(){for(var t=-1;++t<i;)r[t].lineStart()},lineEnd:function(){for(var t=-1;++t<i;)r[t].lineEnd()},polygonStart:function(){for(var t=-1;++t<i;)r[t].polygonStart()},polygonEnd:function(){for(var t=-1;++t<i;)r[t].polygonEnd()}});var r,i},l.precision=function(t){return arguments.length?(o.precision(t),s.precision(t),c.precision(t),h()):o.precision()},l.scale=function(t){return arguments.length?(o.scale(t),s.scale(.35*t),c.scale(t),l.translate(o.translate())):o.scale()},l.translate=function(t){if(!arguments.length)return o.translate();var e=o.scale(),a=+t[0],l=+t[1];return n=o.translate(t).clipExtent([[a-.455*e,l-.238*e],[a+.455*e,l+.238*e]]).stream(u),r=s.translate([a-.307*e,l+.201*e]).clipExtent([[a-.425*e+Sc,l+.12*e+Sc],[a-.214*e-Sc,l+.234*e-Sc]]).stream(u),i=c.translate([a-.205*e,l+.212*e]).clipExtent([[a-.214*e+Sc,l+.166*e+Sc],[a-.115*e-Sc,l+.234*e-Sc]]).stream(u),h()},l.fitExtent=function(t,e){return Af(l,t,e)},l.fitSize=function(t,e){return Mf(l,t,e)},l.fitWidth=function(t,e){return Nf(l,t,e)},l.fitHeight=function(t,e){return Df(l,t,e)},l.scale(1070)}function Hf(t){return function(e,n){var r=Pc(e),i=Pc(n),a=t(r*i);return[a*i*qc(e),a*qc(n)]}}function $f(t){return function(e,n){var r=$c(e*e+n*n),i=t(r),a=qc(i),o=Pc(i);return[Fc(e*a,r*o),Gc(r&&n*a/r)]}}var Wf=Hf((function(t){return $c(2/(1+t))}));function Vf(){return Ff(Wf).scale(124.75).clipAngle(179.999)}Wf.invert=$f((function(t){return 2*Gc(t/2)}));var Gf=Hf((function(t){return(t=Vc(t))&&t/qc(t)}));function Xf(){return Ff(Gf).scale(79.4188).clipAngle(179.999)}function Zf(t,e){return[t,zc(Wc((Nc+e)/2))]}function Qf(){return Kf(Zf).scale(961/Oc)}function Kf(t){var e,n,r,i=Ff(t),a=i.center,o=i.scale,s=i.translate,c=i.clipExtent,u=null;function l(){var a=Mc*o(),s=i(Sl(i.rotate()).invert([0,0]));return c(null==u?[[s[0]-a,s[1]-a],[s[0]+a,s[1]+a]]:t===Zf?[[Math.max(s[0]-a,u),e],[Math.min(s[0]+a,n),r]]:[[u,Math.max(s[1]-a,e)],[n,Math.min(s[1]+a,r)]])}return i.scale=function(t){return arguments.length?(o(t),l()):o()},i.translate=function(t){return arguments.length?(s(t),l()):s()},i.center=function(t){return arguments.length?(a(t),l()):a()},i.clipExtent=function(t){return arguments.length?(null==t?u=e=n=r=null:(u=+t[0][0],e=+t[0][1],n=+t[1][0],r=+t[1][1]),l()):null==u?null:[[u,e],[n,r]]},l()}function Jf(t){return Wc((Nc+t)/2)}function td(t,e){var n=Pc(t),r=t===e?qc(t):zc(n/Pc(e))/zc(Jf(e)/Jf(t)),i=n*Uc(Jf(t),r)/r;if(!r)return Zf;function a(t,e){i>0?e<-Nc+Sc&&(e=-Nc+Sc):e>Nc-Sc&&(e=Nc-Sc);var n=i/Uc(Jf(e),r);return[n*qc(r*t),i-n*Pc(r*t)]}return a.invert=function(t,e){var n=i-e,a=Hc(r)*$c(t*t+n*n),o=Fc(t,Ic(n))*Hc(n);return n*r<0&&(o-=Mc*Hc(t)*Hc(n)),[o/r,2*Rc(Uc(i/a,1/r))-Nc]},a}function ed(){return jf(td).scale(109.5).parallels([30,30])}function nd(t,e){return[t,e]}function rd(){return Ff(nd).scale(152.63)}function id(t,e){var n=Pc(t),r=t===e?qc(t):(n-Pc(e))/(e-t),i=n/r+t;if(Ic(r)<Sc)return nd;function a(t,e){var n=i-e,a=r*t;return[n*qc(a),i-n*Pc(a)]}return a.invert=function(t,e){var n=i-e,a=Fc(t,Ic(n))*Hc(n);return n*r<0&&(a-=Mc*Hc(t)*Hc(n)),[a/r,i-Hc(r)*$c(t*t+n*n)]},a}function ad(){return jf(id).scale(131.154).center([0,13.9389])}Gf.invert=$f((function(t){return t})),Zf.invert=function(t,e){return[t,2*Rc(Yc(e))-Nc]},nd.invert=nd;var od=1.340264,sd=-.081106,cd=893e-6,ud=.003796,ld=$c(3)/2;function hd(t,e){var n=Gc(ld*qc(e)),r=n*n,i=r*r*r;return[t*Pc(n)/(ld*(od+3*sd*r+i*(7*cd+9*ud*r))),n*(od+sd*r+i*(cd+ud*r))]}function fd(){return Ff(hd).scale(177.158)}function dd(t,e){var n=Pc(e),r=Pc(t)*n;return[n*qc(t)/r,qc(e)/r]}function pd(){return Ff(dd).scale(144.049).clipAngle(60)}function yd(){var t,e,n,r,i,a,o,s=1,c=0,u=0,l=1,h=1,f=0,d=null,p=1,y=1,g=Ef({point:function(t,e){var n=b([t,e]);this.stream.point(n[0],n[1])}}),m=_h;function v(){return p=s*l,y=s*h,a=o=null,b}function b(n){var r=n[0]*p,i=n[1]*y;if(f){var a=i*t-r*e;r=r*t+i*e,i=a}return[r+c,i+u]}return b.invert=function(n){var r=n[0]-c,i=n[1]-u;if(f){var a=i*t+r*e;r=r*t-i*e,i=a}return[r/p,i/y]},b.stream=function(t){return a&&o===t?a:a=g(m(o=t))},b.postclip=function(t){return arguments.length?(m=t,d=n=r=i=null,v()):m},b.clipExtent=function(t){return arguments.length?(m=null==t?(d=n=r=i=null,_h):Wl(d=+t[0][0],n=+t[0][1],r=+t[1][0],i=+t[1][1]),v()):null==d?null:[[d,n],[r,i]]},b.scale=function(t){return arguments.length?(s=+t,v()):s},b.translate=function(t){return arguments.length?(c=+t[0],u=+t[1],v()):[c,u]},b.angle=function(n){return arguments.length?(e=qc(f=n%360*Lc),t=Pc(f),v()):f*Bc},b.reflectX=function(t){return arguments.length?(l=t?-1:1,v()):l<0},b.reflectY=function(t){return arguments.length?(h=t?-1:1,v()):h<0},b.fitExtent=function(t,e){return Af(b,t,e)},b.fitSize=function(t,e){return Mf(b,t,e)},b.fitWidth=function(t,e){return Nf(b,t,e)},b.fitHeight=function(t,e){return Df(b,t,e)},b}function gd(t,e){var n=e*e,r=n*n;return[t*(.8707-.131979*n+r*(r*(.003971*n-.001529*r)-.013791)),e*(1.007226+n*(.015085+r*(.028874*n-.044475-.005916*r)))]}function md(){return Ff(gd).scale(175.295)}function vd(t,e){return[Pc(e)*qc(t),qc(e)]}function bd(){return Ff(vd).scale(249.5).clipAngle(90.000001)}function _d(t,e){var n=Pc(e),r=1+Pc(t)*n;return[n*qc(t)/r,qc(e)/r]}function xd(){return Ff(_d).scale(250).clipAngle(142)}function wd(t,e){return[zc(Wc((Nc+e)/2)),-t]}function kd(){var t=Kf(wd),e=t.center,n=t.rotate;return t.center=function(t){return arguments.length?e([-t[1],t[0]]):[(t=e())[1],-t[0]]},t.rotate=function(t){return arguments.length?n([t[0],t[1],t.length>2?t[2]+90:90]):[(t=n())[0],t[1],t[2]-90]},n([0,0,90]).scale(159.155)}function Td(t,e){return t.parent===e.parent?1:2}function Ed(t,e){return t+e.x}function Cd(t,e){return Math.max(t,e.y)}function Sd(){var t=Td,e=1,n=1,r=!1;function i(i){var a,o=0;i.eachAfter((function(e){var n=e.children;n?(e.x=function(t){return t.reduce(Ed,0)/t.length}(n),e.y=function(t){return 1+t.reduce(Cd,0)}(n)):(e.x=a?o+=t(e,a):0,e.y=0,a=e)}));var s=function(t){for(var e;e=t.children;)t=e[0];return t}(i),c=function(t){for(var e;e=t.children;)t=e[e.length-1];return t}(i),u=s.x-t(s,c)/2,l=c.x+t(c,s)/2;return i.eachAfter(r?function(t){t.x=(t.x-i.x)*e,t.y=(i.y-t.y)*n}:function(t){t.x=(t.x-u)/(l-u)*e,t.y=(1-(i.y?t.y/i.y:1))*n})}return i.separation=function(e){return arguments.length?(t=e,i):t},i.size=function(t){return arguments.length?(r=!1,e=+t[0],n=+t[1],i):r?null:[e,n]},i.nodeSize=function(t){return arguments.length?(r=!0,e=+t[0],n=+t[1],i):r?[e,n]:null},i}function Ad(t){var e=0,n=t.children,r=n&&n.length;if(r)for(;--r>=0;)e+=n[r].value;else e=1;t.value=e}function Md(t,e){var n,r,i,a,o,s=new Bd(t),c=+t.value&&(s.value=t.value),u=[s];for(null==e&&(e=Nd);n=u.pop();)if(c&&(n.value=+n.data.value),(i=e(n.data))&&(o=i.length))for(n.children=new Array(o),a=o-1;a>=0;--a)u.push(r=n.children[a]=new Bd(i[a])),r.parent=n,r.depth=n.depth+1;return s.eachBefore(Od)}function Nd(t){return t.children}function Dd(t){t.data=t.data.data}function Od(t){var e=0;do{t.height=e}while((t=t.parent)&&t.height<++e)}function Bd(t){this.data=t,this.depth=this.height=0,this.parent=null}hd.invert=function(t,e){for(var n,r=e,i=r*r,a=i*i*i,o=0;o<12&&(a=(i=(r-=n=(r*(od+sd*i+a*(cd+ud*i))-e)/(od+3*sd*i+a*(7*cd+9*ud*i)))*r)*i*i,!(Ic(n)<Ac));++o);return[ld*t*(od+3*sd*i+a*(7*cd+9*ud*i))/Pc(r),Gc(qc(r)/ld)]},dd.invert=$f(Rc),gd.invert=function(t,e){var n,r=e,i=25;do{var a=r*r,o=a*a;r-=n=(r*(1.007226+a*(.015085+o*(.028874*a-.044475-.005916*o)))-e)/(1.007226+a*(.045255+o*(.259866*a-.311325-.005916*11*o)))}while(Ic(n)>Sc&&--i>0);return[t/(.8707+(a=r*r)*(a*(a*a*a*(.003971-.001529*a)-.013791)-.131979)),r]},vd.invert=$f(Gc),_d.invert=$f((function(t){return 2*Rc(t)})),wd.invert=function(t,e){return[-e,2*Rc(Yc(t))-Nc]},Bd.prototype=Md.prototype={constructor:Bd,count:function(){return this.eachAfter(Ad)},each:function(t){var e,n,r,i,a=this,o=[a];do{for(e=o.reverse(),o=[];a=e.pop();)if(t(a),n=a.children)for(r=0,i=n.length;r<i;++r)o.push(n[r])}while(o.length);return this},eachAfter:function(t){for(var e,n,r,i=this,a=[i],o=[];i=a.pop();)if(o.push(i),e=i.children)for(n=0,r=e.length;n<r;++n)a.push(e[n]);for(;i=o.pop();)t(i);return this},eachBefore:function(t){for(var e,n,r=this,i=[r];r=i.pop();)if(t(r),e=r.children)for(n=e.length-1;n>=0;--n)i.push(e[n]);return this},sum:function(t){return this.eachAfter((function(e){for(var n=+t(e.data)||0,r=e.children,i=r&&r.length;--i>=0;)n+=r[i].value;e.value=n}))},sort:function(t){return this.eachBefore((function(e){e.children&&e.children.sort(t)}))},path:function(t){for(var e=this,n=function(t,e){if(t===e)return t;var n=t.ancestors(),r=e.ancestors(),i=null;for(t=n.pop(),e=r.pop();t===e;)i=t,t=n.pop(),e=r.pop();return i}(e,t),r=[e];e!==n;)e=e.parent,r.push(e);for(var i=r.length;t!==n;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,e=[t];t=t.parent;)e.push(t);return e},descendants:function(){var t=[];return this.each((function(e){t.push(e)})),t},leaves:function(){var t=[];return this.eachBefore((function(e){e.children||t.push(e)})),t},links:function(){var t=this,e=[];return t.each((function(n){n!==t&&e.push({source:n.parent,target:n})})),e},copy:function(){return Md(this).eachBefore(Dd)}};var Ld=Array.prototype.slice;function Id(t){for(var e,n,r=0,i=(t=function(t){for(var e,n,r=t.length;r;)n=Math.random()*r--|0,e=t[r],t[r]=t[n],t[n]=e;return t}(Ld.call(t))).length,a=[];r<i;)e=t[r],n&&Pd(n,e)?++r:(n=Yd(a=Rd(a,e)),r=0);return n}function Rd(t,e){var n,r;if(jd(e,t))return[e];for(n=0;n<t.length;++n)if(Fd(e,t[n])&&jd(zd(t[n],e),t))return[t[n],e];for(n=0;n<t.length-1;++n)for(r=n+1;r<t.length;++r)if(Fd(zd(t[n],t[r]),e)&&Fd(zd(t[n],e),t[r])&&Fd(zd(t[r],e),t[n])&&jd(Ud(t[n],t[r],e),t))return[t[n],t[r],e];throw new Error}function Fd(t,e){var n=t.r-e.r,r=e.x-t.x,i=e.y-t.y;return n<0||n*n<r*r+i*i}function Pd(t,e){var n=t.r-e.r+1e-6,r=e.x-t.x,i=e.y-t.y;return n>0&&n*n>r*r+i*i}function jd(t,e){for(var n=0;n<e.length;++n)if(!Pd(t,e[n]))return!1;return!0}function Yd(t){switch(t.length){case 1:return function(t){return{x:t.x,y:t.y,r:t.r}}(t[0]);case 2:return zd(t[0],t[1]);case 3:return Ud(t[0],t[1],t[2])}}function zd(t,e){var n=t.x,r=t.y,i=t.r,a=e.x,o=e.y,s=e.r,c=a-n,u=o-r,l=s-i,h=Math.sqrt(c*c+u*u);return{x:(n+a+c/h*l)/2,y:(r+o+u/h*l)/2,r:(h+i+s)/2}}function Ud(t,e,n){var r=t.x,i=t.y,a=t.r,o=e.x,s=e.y,c=e.r,u=n.x,l=n.y,h=n.r,f=r-o,d=r-u,p=i-s,y=i-l,g=c-a,m=h-a,v=r*r+i*i-a*a,b=v-o*o-s*s+c*c,_=v-u*u-l*l+h*h,x=d*p-f*y,w=(p*_-y*b)/(2*x)-r,k=(y*g-p*m)/x,T=(d*b-f*_)/(2*x)-i,E=(f*m-d*g)/x,C=k*k+E*E-1,S=2*(a+w*k+T*E),A=w*w+T*T-a*a,M=-(C?(S+Math.sqrt(S*S-4*C*A))/(2*C):A/S);return{x:r+w+k*M,y:i+T+E*M,r:M}}function qd(t,e,n){var r,i,a,o,s=t.x-e.x,c=t.y-e.y,u=s*s+c*c;u?(i=e.r+n.r,i*=i,o=t.r+n.r,i>(o*=o)?(r=(u+o-i)/(2*u),a=Math.sqrt(Math.max(0,o/u-r*r)),n.x=t.x-r*s-a*c,n.y=t.y-r*c+a*s):(r=(u+i-o)/(2*u),a=Math.sqrt(Math.max(0,i/u-r*r)),n.x=e.x+r*s-a*c,n.y=e.y+r*c+a*s)):(n.x=e.x+n.r,n.y=e.y)}function Hd(t,e){var n=t.r+e.r-1e-6,r=e.x-t.x,i=e.y-t.y;return n>0&&n*n>r*r+i*i}function $d(t){var e=t._,n=t.next._,r=e.r+n.r,i=(e.x*n.r+n.x*e.r)/r,a=(e.y*n.r+n.y*e.r)/r;return i*i+a*a}function Wd(t){this._=t,this.next=null,this.previous=null}function Vd(t){if(!(i=t.length))return 0;var e,n,r,i,a,o,s,c,u,l,h;if((e=t[0]).x=0,e.y=0,!(i>1))return e.r;if(n=t[1],e.x=-n.r,n.x=e.r,n.y=0,!(i>2))return e.r+n.r;qd(n,e,r=t[2]),e=new Wd(e),n=new Wd(n),r=new Wd(r),e.next=r.previous=n,n.next=e.previous=r,r.next=n.previous=e;t:for(s=3;s<i;++s){qd(e._,n._,r=t[s]),r=new Wd(r),c=n.next,u=e.previous,l=n._.r,h=e._.r;do{if(l<=h){if(Hd(c._,r._)){n=c,e.next=n,n.previous=e,--s;continue t}l+=c._.r,c=c.next}else{if(Hd(u._,r._)){(e=u).next=n,n.previous=e,--s;continue t}h+=u._.r,u=u.previous}}while(c!==u.next);for(r.previous=e,r.next=n,e.next=n.previous=n=r,a=$d(e);(r=r.next)!==n;)(o=$d(r))<a&&(e=r,a=o);n=e.next}for(e=[n._],r=n;(r=r.next)!==n;)e.push(r._);for(r=Id(e),s=0;s<i;++s)(e=t[s]).x-=r.x,e.y-=r.y;return r.r}function Gd(t){return Vd(t),t}function Xd(t){return null==t?null:Zd(t)}function Zd(t){if("function"!=typeof t)throw new Error;return t}function Qd(){return 0}function Kd(t){return function(){return t}}function Jd(t){return Math.sqrt(t.value)}function tp(){var t=null,e=1,n=1,r=Qd;function i(i){return i.x=e/2,i.y=n/2,t?i.eachBefore(ep(t)).eachAfter(np(r,.5)).eachBefore(rp(1)):i.eachBefore(ep(Jd)).eachAfter(np(Qd,1)).eachAfter(np(r,i.r/Math.min(e,n))).eachBefore(rp(Math.min(e,n)/(2*i.r))),i}return i.radius=function(e){return arguments.length?(t=Xd(e),i):t},i.size=function(t){return arguments.length?(e=+t[0],n=+t[1],i):[e,n]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:Kd(+t),i):r},i}function ep(t){return function(e){e.children||(e.r=Math.max(0,+t(e)||0))}}function np(t,e){return function(n){if(r=n.children){var r,i,a,o=r.length,s=t(n)*e||0;if(s)for(i=0;i<o;++i)r[i].r+=s;if(a=Vd(r),s)for(i=0;i<o;++i)r[i].r-=s;n.r=a+s}}}function rp(t){return function(e){var n=e.parent;e.r*=t,n&&(e.x=n.x+t*e.x,e.y=n.y+t*e.y)}}function ip(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)}function ap(t,e,n,r,i){for(var a,o=t.children,s=-1,c=o.length,u=t.value&&(r-e)/t.value;++s<c;)(a=o[s]).y0=n,a.y1=i,a.x0=e,a.x1=e+=a.value*u}function op(){var t=1,e=1,n=0,r=!1;function i(i){var a=i.height+1;return i.x0=i.y0=n,i.x1=t,i.y1=e/a,i.eachBefore(function(t,e){return function(r){r.children&&ap(r,r.x0,t*(r.depth+1)/e,r.x1,t*(r.depth+2)/e);var i=r.x0,a=r.y0,o=r.x1-n,s=r.y1-n;o<i&&(i=o=(i+o)/2),s<a&&(a=s=(a+s)/2),r.x0=i,r.y0=a,r.x1=o,r.y1=s}}(e,a)),r&&i.eachBefore(ip),i}return i.round=function(t){return arguments.length?(r=!!t,i):r},i.size=function(n){return arguments.length?(t=+n[0],e=+n[1],i):[t,e]},i.padding=function(t){return arguments.length?(n=+t,i):n},i}var sp={depth:-1},cp={};function up(t){return t.id}function lp(t){return t.parentId}function hp(){var t=up,e=lp;function n(n){var r,i,a,o,s,c,u,l=n.length,h=new Array(l),f={};for(i=0;i<l;++i)r=n[i],s=h[i]=new Bd(r),null!=(c=t(r,i,n))&&(c+="")&&(f[u="$"+(s.id=c)]=u in f?cp:s);for(i=0;i<l;++i)if(s=h[i],null!=(c=e(n[i],i,n))&&(c+="")){if(!(o=f["$"+c]))throw new Error("missing: "+c);if(o===cp)throw new Error("ambiguous: "+c);o.children?o.children.push(s):o.children=[s],s.parent=o}else{if(a)throw new Error("multiple roots");a=s}if(!a)throw new Error("no root");if(a.parent=sp,a.eachBefore((function(t){t.depth=t.parent.depth+1,--l})).eachBefore(Od),a.parent=null,l>0)throw new Error("cycle");return a}return n.id=function(e){return arguments.length?(t=Zd(e),n):t},n.parentId=function(t){return arguments.length?(e=Zd(t),n):e},n}function fp(t,e){return t.parent===e.parent?1:2}function dp(t){var e=t.children;return e?e[0]:t.t}function pp(t){var e=t.children;return e?e[e.length-1]:t.t}function yp(t,e,n){var r=n/(e.i-t.i);e.c-=r,e.s+=n,t.c+=r,e.z+=n,e.m+=n}function gp(t,e,n){return t.a.parent===e.parent?t.a:n}function mp(t,e){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=e}function vp(){var t=fp,e=1,n=1,r=null;function i(i){var c=function(t){for(var e,n,r,i,a,o=new mp(t,0),s=[o];e=s.pop();)if(r=e._.children)for(e.children=new Array(a=r.length),i=a-1;i>=0;--i)s.push(n=e.children[i]=new mp(r[i],i)),n.parent=e;return(o.parent=new mp(null,0)).children=[o],o}(i);if(c.eachAfter(a),c.parent.m=-c.z,c.eachBefore(o),r)i.eachBefore(s);else{var u=i,l=i,h=i;i.eachBefore((function(t){t.x<u.x&&(u=t),t.x>l.x&&(l=t),t.depth>h.depth&&(h=t)}));var f=u===l?1:t(u,l)/2,d=f-u.x,p=e/(l.x+f+d),y=n/(h.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*y}))}return i}function a(e){var n=e.children,r=e.parent.children,i=e.i?r[e.i-1]:null;if(n){!function(t){for(var e,n=0,r=0,i=t.children,a=i.length;--a>=0;)(e=i[a]).z+=n,e.m+=n,n+=e.s+(r+=e.c)}(e);var a=(n[0].z+n[n.length-1].z)/2;i?(e.z=i.z+t(e._,i._),e.m=e.z-a):e.z=a}else i&&(e.z=i.z+t(e._,i._));e.parent.A=function(e,n,r){if(n){for(var i,a=e,o=e,s=n,c=a.parent.children[0],u=a.m,l=o.m,h=s.m,f=c.m;s=pp(s),a=dp(a),s&&a;)c=dp(c),(o=pp(o)).a=e,(i=s.z+h-a.z-u+t(s._,a._))>0&&(yp(gp(s,e,r),e,i),u+=i,l+=i),h+=s.m,u+=a.m,f+=c.m,l+=o.m;s&&!pp(o)&&(o.t=s,o.m+=h-l),a&&!dp(c)&&(c.t=a,c.m+=u-f,r=e)}return r}(e,i,e.parent.A||r[0])}function o(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function s(t){t.x*=e,t.y=t.depth*n}return i.separation=function(e){return arguments.length?(t=e,i):t},i.size=function(t){return arguments.length?(r=!1,e=+t[0],n=+t[1],i):r?null:[e,n]},i.nodeSize=function(t){return arguments.length?(r=!0,e=+t[0],n=+t[1],i):r?[e,n]:null},i}function bp(t,e,n,r,i){for(var a,o=t.children,s=-1,c=o.length,u=t.value&&(i-n)/t.value;++s<c;)(a=o[s]).x0=e,a.x1=r,a.y0=n,a.y1=n+=a.value*u}mp.prototype=Object.create(Bd.prototype);var _p=(1+Math.sqrt(5))/2;function xp(t,e,n,r,i,a){for(var o,s,c,u,l,h,f,d,p,y,g,m=[],v=e.children,b=0,_=0,x=v.length,w=e.value;b<x;){c=i-n,u=a-r;do{l=v[_++].value}while(!l&&_<x);for(h=f=l,g=l*l*(y=Math.max(u/c,c/u)/(w*t)),p=Math.max(f/g,g/h);_<x;++_){if(l+=s=v[_].value,s<h&&(h=s),s>f&&(f=s),g=l*l*y,(d=Math.max(f/g,g/h))>p){l-=s;break}p=d}m.push(o={value:l,dice:c<u,children:v.slice(b,_)}),o.dice?ap(o,n,r,i,w?r+=u*l/w:a):bp(o,n,r,w?n+=c*l/w:i,a),w-=l,b=_}return m}const wp=function t(e){function n(t,n,r,i,a){xp(e,t,n,r,i,a)}return n.ratio=function(e){return t((e=+e)>1?e:1)},n}(_p);function kp(){var t=wp,e=!1,n=1,r=1,i=[0],a=Qd,o=Qd,s=Qd,c=Qd,u=Qd;function l(t){return t.x0=t.y0=0,t.x1=n,t.y1=r,t.eachBefore(h),i=[0],e&&t.eachBefore(ip),t}function h(e){var n=i[e.depth],r=e.x0+n,l=e.y0+n,h=e.x1-n,f=e.y1-n;h<r&&(r=h=(r+h)/2),f<l&&(l=f=(l+f)/2),e.x0=r,e.y0=l,e.x1=h,e.y1=f,e.children&&(n=i[e.depth+1]=a(e)/2,r+=u(e)-n,l+=o(e)-n,(h-=s(e)-n)<r&&(r=h=(r+h)/2),(f-=c(e)-n)<l&&(l=f=(l+f)/2),t(e,r,l,h,f))}return l.round=function(t){return arguments.length?(e=!!t,l):e},l.size=function(t){return arguments.length?(n=+t[0],r=+t[1],l):[n,r]},l.tile=function(e){return arguments.length?(t=Zd(e),l):t},l.padding=function(t){return arguments.length?l.paddingInner(t).paddingOuter(t):l.paddingInner()},l.paddingInner=function(t){return arguments.length?(a="function"==typeof t?t:Kd(+t),l):a},l.paddingOuter=function(t){return arguments.length?l.paddingTop(t).paddingRight(t).paddingBottom(t).paddingLeft(t):l.paddingTop()},l.paddingTop=function(t){return arguments.length?(o="function"==typeof t?t:Kd(+t),l):o},l.paddingRight=function(t){return arguments.length?(s="function"==typeof t?t:Kd(+t),l):s},l.paddingBottom=function(t){return arguments.length?(c="function"==typeof t?t:Kd(+t),l):c},l.paddingLeft=function(t){return arguments.length?(u="function"==typeof t?t:Kd(+t),l):u},l}function Tp(t,e,n,r,i){var a,o,s=t.children,c=s.length,u=new Array(c+1);for(u[0]=o=a=0;a<c;++a)u[a+1]=o+=s[a].value;!function t(e,n,r,i,a,o,c){if(e>=n-1){var l=s[e];return l.x0=i,l.y0=a,l.x1=o,void(l.y1=c)}for(var h=u[e],f=r/2+h,d=e+1,p=n-1;d<p;){var y=d+p>>>1;u[y]<f?d=y+1:p=y}f-u[d-1]<u[d]-f&&e+1<d&&--d;var g=u[d]-h,m=r-g;if(o-i>c-a){var v=(i*m+o*g)/r;t(e,d,g,i,a,v,c),t(d,n,m,v,a,o,c)}else{var b=(a*m+c*g)/r;t(e,d,g,i,a,o,b),t(d,n,m,i,b,o,c)}}(0,c,t.value,e,n,r,i)}function Ep(t,e,n,r,i){(1&t.depth?bp:ap)(t,e,n,r,i)}const Cp=function t(e){function n(t,n,r,i,a){if((o=t._squarify)&&o.ratio===e)for(var o,s,c,u,l,h=-1,f=o.length,d=t.value;++h<f;){for(c=(s=o[h]).children,u=s.value=0,l=c.length;u<l;++u)s.value+=c[u].value;s.dice?ap(s,n,r,i,r+=(a-r)*s.value/d):bp(s,n,r,n+=(i-n)*s.value/d,a),d-=s.value}else t._squarify=o=xp(e,t,n,r,i,a),o.ratio=e}return n.ratio=function(e){return t((e=+e)>1?e:1)},n}(_p);function Sp(t){var e=t.length;return function(n){return t[Math.max(0,Math.min(e-1,Math.floor(n*e)))]}}function Ap(t,e){var n=dn(+t,+e);return function(t){var e=n(t);return e-360*Math.floor(e/360)}}function Mp(t,e){return t=+t,e=+e,function(n){return Math.round(t*(1-n)+e*n)}}var Np=Math.SQRT2;function Dp(t){return((t=Math.exp(t))+1/t)/2}function Op(t,e){var n,r,i=t[0],a=t[1],o=t[2],s=e[0],c=e[1],u=e[2],l=s-i,h=c-a,f=l*l+h*h;if(f<1e-12)r=Math.log(u/o)/Np,n=function(t){return[i+t*l,a+t*h,o*Math.exp(Np*t*r)]};else{var d=Math.sqrt(f),p=(u*u-o*o+4*f)/(2*o*2*d),y=(u*u-o*o-4*f)/(2*u*2*d),g=Math.log(Math.sqrt(p*p+1)-p),m=Math.log(Math.sqrt(y*y+1)-y);r=(m-g)/Np,n=function(t){var e,n=t*r,s=Dp(g),c=o/(2*d)*(s*(e=Np*n+g,((e=Math.exp(2*e))-1)/(e+1))-function(t){return((t=Math.exp(t))-1/t)/2}(g));return[i+c*l,a+c*h,o*s/Dp(Np*n+g)]}}return n.duration=1e3*r,n}function Bp(t){return function(e,n){var r=t((e=an(e)).h,(n=an(n)).h),i=pn(e.s,n.s),a=pn(e.l,n.l),o=pn(e.opacity,n.opacity);return function(t){return e.h=r(t),e.s=i(t),e.l=a(t),e.opacity=o(t),e+""}}}const Lp=Bp(dn);var Ip=Bp(pn);function Rp(t,e){var n=pn((t=Ta(t)).l,(e=Ta(e)).l),r=pn(t.a,e.a),i=pn(t.b,e.b),a=pn(t.opacity,e.opacity);return function(e){return t.l=n(e),t.a=r(e),t.b=i(e),t.opacity=a(e),t+""}}function Fp(t){return function(e,n){var r=t((e=Oa(e)).h,(n=Oa(n)).h),i=pn(e.c,n.c),a=pn(e.l,n.l),o=pn(e.opacity,n.opacity);return function(t){return e.h=r(t),e.c=i(t),e.l=a(t),e.opacity=o(t),e+""}}}const Pp=Fp(dn);var jp=Fp(pn);function Yp(t){return function e(n){function r(e,r){var i=t((e=Ha(e)).h,(r=Ha(r)).h),a=pn(e.s,r.s),o=pn(e.l,r.l),s=pn(e.opacity,r.opacity);return function(t){return e.h=i(t),e.s=a(t),e.l=o(Math.pow(t,n)),e.opacity=s(t),e+""}}return n=+n,r.gamma=e,r}(1)}const zp=Yp(dn);var Up=Yp(pn);function qp(t,e){for(var n=0,r=e.length-1,i=e[0],a=new Array(r<0?0:r);n<r;)a[n]=t(i,i=e[++n]);return function(t){var e=Math.max(0,Math.min(r-1,Math.floor(t*=r)));return a[e](t-e)}}function Hp(t,e){for(var n=new Array(e),r=0;r<e;++r)n[r]=t(r/(e-1));return n}function $p(t){for(var e,n=-1,r=t.length,i=t[r-1],a=0;++n<r;)e=i,i=t[n],a+=e[1]*i[0]-e[0]*i[1];return a/2}function Wp(t){for(var e,n,r=-1,i=t.length,a=0,o=0,s=t[i-1],c=0;++r<i;)e=s,s=t[r],c+=n=e[0]*s[1]-s[0]*e[1],a+=(e[0]+s[0])*n,o+=(e[1]+s[1])*n;return[a/(c*=3),o/c]}function Vp(t,e,n){return(e[0]-t[0])*(n[1]-t[1])-(e[1]-t[1])*(n[0]-t[0])}function Gp(t,e){return t[0]-e[0]||t[1]-e[1]}function Xp(t){for(var e=t.length,n=[0,1],r=2,i=2;i<e;++i){for(;r>1&&Vp(t[n[r-2]],t[n[r-1]],t[i])<=0;)--r;n[r++]=i}return n.slice(0,r)}function Zp(t){if((n=t.length)<3)return null;var e,n,r=new Array(n),i=new Array(n);for(e=0;e<n;++e)r[e]=[+t[e][0],+t[e][1],e];for(r.sort(Gp),e=0;e<n;++e)i[e]=[r[e][0],-r[e][1]];var a=Xp(r),o=Xp(i),s=o[0]===a[0],c=o[o.length-1]===a[a.length-1],u=[];for(e=a.length-1;e>=0;--e)u.push(t[r[a[e]][2]]);for(e=+s;e<o.length-c;++e)u.push(t[r[o[e]][2]]);return u}function Qp(t,e){for(var n,r,i=t.length,a=t[i-1],o=e[0],s=e[1],c=a[0],u=a[1],l=!1,h=0;h<i;++h)n=(a=t[h])[0],(r=a[1])>s!=u>s&&o<(c-n)*(s-r)/(u-r)+n&&(l=!l),c=n,u=r;return l}function Kp(t){for(var e,n,r=-1,i=t.length,a=t[i-1],o=a[0],s=a[1],c=0;++r<i;)e=o,n=s,e-=o=(a=t[r])[0],n-=s=a[1],c+=Math.sqrt(e*e+n*n);return c}function Jp(){return Math.random()}const ty=function t(e){function n(t,n){return t=null==t?0:+t,n=null==n?1:+n,1===arguments.length?(n=t,t=0):n-=t,function(){return e()*n+t}}return n.source=t,n}(Jp),ey=function t(e){function n(t,n){var r,i;return t=null==t?0:+t,n=null==n?1:+n,function(){var a;if(null!=r)a=r,r=null;else do{r=2*e()-1,a=2*e()-1,i=r*r+a*a}while(!i||i>1);return t+n*a*Math.sqrt(-2*Math.log(i)/i)}}return n.source=t,n}(Jp),ny=function t(e){function n(){var t=ey.source(e).apply(this,arguments);return function(){return Math.exp(t())}}return n.source=t,n}(Jp),ry=function t(e){function n(t){return function(){for(var n=0,r=0;r<t;++r)n+=e();return n}}return n.source=t,n}(Jp),iy=function t(e){function n(t){var n=ry.source(e)(t);return function(){return n()/t}}return n.source=t,n}(Jp),ay=function t(e){function n(t){return function(){return-Math.log(1-e())/t}}return n.source=t,n}(Jp);function oy(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t)}return this}function sy(t,e){switch(arguments.length){case 0:break;case 1:this.interpolator(t);break;default:this.interpolator(e).domain(t)}return this}var cy=Array.prototype,uy=cy.map,ly=cy.slice,hy={name:"implicit"};function fy(){var t=na(),e=[],n=[],r=hy;function i(i){var a=i+"",o=t.get(a);if(!o){if(r!==hy)return r;t.set(a,o=e.push(i))}return n[(o-1)%n.length]}return i.domain=function(n){if(!arguments.length)return e.slice();e=[],t=na();for(var r,a,o=-1,s=n.length;++o<s;)t.has(a=(r=n[o])+"")||t.set(a,e.push(r));return i},i.range=function(t){return arguments.length?(n=ly.call(t),i):n.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return fy(e,n).unknown(r)},oy.apply(i,arguments),i}function dy(){var t,e,n=fy().unknown(void 0),r=n.domain,i=n.range,a=[0,1],o=!1,s=0,c=0,u=.5;function l(){var n=r().length,l=a[1]<a[0],h=a[l-0],f=a[1-l];t=(f-h)/Math.max(1,n-s+2*c),o&&(t=Math.floor(t)),h+=(f-h-t*(n-s))*u,e=t*(1-s),o&&(h=Math.round(h),e=Math.round(e));var d=k(n).map((function(e){return h+t*e}));return i(l?d.reverse():d)}return delete n.unknown,n.domain=function(t){return arguments.length?(r(t),l()):r()},n.range=function(t){return arguments.length?(a=[+t[0],+t[1]],l()):a.slice()},n.rangeRound=function(t){return a=[+t[0],+t[1]],o=!0,l()},n.bandwidth=function(){return e},n.step=function(){return t},n.round=function(t){return arguments.length?(o=!!t,l()):o},n.padding=function(t){return arguments.length?(s=Math.min(1,c=+t),l()):s},n.paddingInner=function(t){return arguments.length?(s=Math.min(1,t),l()):s},n.paddingOuter=function(t){return arguments.length?(c=+t,l()):c},n.align=function(t){return arguments.length?(u=Math.max(0,Math.min(1,t)),l()):u},n.copy=function(){return dy(r(),a).round(o).paddingInner(s).paddingOuter(c).align(u)},oy.apply(l(),arguments)}function py(t){var e=t.copy;return t.padding=t.paddingOuter,delete t.paddingInner,delete t.paddingOuter,t.copy=function(){return py(e())},t}function yy(){return py(dy.apply(null,arguments).paddingInner(1))}function gy(t){return+t}var my=[0,1];function vy(t){return t}function by(t,e){return(e-=t=+t)?function(n){return(n-t)/e}:(n=isNaN(e)?NaN:.5,function(){return n});var n}function _y(t){var e,n=t[0],r=t[t.length-1];return n>r&&(e=n,n=r,r=e),function(t){return Math.max(n,Math.min(r,t))}}function xy(t,e,n){var r=t[0],i=t[1],a=e[0],o=e[1];return i<r?(r=by(i,r),a=n(o,a)):(r=by(r,i),a=n(a,o)),function(t){return a(r(t))}}function wy(t,e,n){var r=Math.min(t.length,e.length)-1,i=new Array(r),a=new Array(r),o=-1;for(t[r]<t[0]&&(t=t.slice().reverse(),e=e.slice().reverse());++o<r;)i[o]=by(t[o],t[o+1]),a[o]=n(e[o],e[o+1]);return function(e){var n=u(t,e,1,r)-1;return a[n](i[n](e))}}function ky(t,e){return e.domain(t.domain()).range(t.range()).interpolate(t.interpolate()).clamp(t.clamp()).unknown(t.unknown())}function Ty(){var t,e,n,r,i,a,o=my,s=my,c=Mn,u=vy;function l(){return r=Math.min(o.length,s.length)>2?wy:xy,i=a=null,h}function h(e){return isNaN(e=+e)?n:(i||(i=r(o.map(t),s,c)))(t(u(e)))}return h.invert=function(n){return u(e((a||(a=r(s,o.map(t),Tn)))(n)))},h.domain=function(t){return arguments.length?(o=uy.call(t,gy),u===vy||(u=_y(o)),l()):o.slice()},h.range=function(t){return arguments.length?(s=ly.call(t),l()):s.slice()},h.rangeRound=function(t){return s=ly.call(t),c=Mp,l()},h.clamp=function(t){return arguments.length?(u=t?_y(o):vy,h):u!==vy},h.interpolate=function(t){return arguments.length?(c=t,l()):c},h.unknown=function(t){return arguments.length?(n=t,h):n},function(n,r){return t=n,e=r,l()}}function Ey(t,e){return Ty()(t,e)}function Cy(t,e,n,r){var i,a=M(t,e,n);switch((r=cc(null==r?",f":r)).type){case"s":var o=Math.max(Math.abs(t),Math.abs(e));return null!=r.precision||isNaN(i=xc(a,o))||(r.precision=i),yc(r,o);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=wc(a,Math.max(Math.abs(t),Math.abs(e))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=_c(a))||(r.precision=i-2*("%"===r.type))}return pc(r)}function Sy(t){var e=t.domain;return t.ticks=function(t){var n=e();return S(n[0],n[n.length-1],null==t?10:t)},t.tickFormat=function(t,n){var r=e();return Cy(r[0],r[r.length-1],null==t?10:t,n)},t.nice=function(n){null==n&&(n=10);var r,i=e(),a=0,o=i.length-1,s=i[a],c=i[o];return c<s&&(r=s,s=c,c=r,r=a,a=o,o=r),(r=A(s,c,n))>0?r=A(s=Math.floor(s/r)*r,c=Math.ceil(c/r)*r,n):r<0&&(r=A(s=Math.ceil(s*r)/r,c=Math.floor(c*r)/r,n)),r>0?(i[a]=Math.floor(s/r)*r,i[o]=Math.ceil(c/r)*r,e(i)):r<0&&(i[a]=Math.ceil(s*r)/r,i[o]=Math.floor(c*r)/r,e(i)),t},t}function Ay(){var t=Ey(vy,vy);return t.copy=function(){return ky(t,Ay())},oy.apply(t,arguments),Sy(t)}function My(t){var e;function n(t){return isNaN(t=+t)?e:t}return n.invert=n,n.domain=n.range=function(e){return arguments.length?(t=uy.call(e,gy),n):t.slice()},n.unknown=function(t){return arguments.length?(e=t,n):e},n.copy=function(){return My(t).unknown(e)},t=arguments.length?uy.call(t,gy):[0,1],Sy(n)}function Ny(t,e){var n,r=0,i=(t=t.slice()).length-1,a=t[r],o=t[i];return o<a&&(n=r,r=i,i=n,n=a,a=o,o=n),t[r]=e.floor(a),t[i]=e.ceil(o),t}function Dy(t){return Math.log(t)}function Oy(t){return Math.exp(t)}function By(t){return-Math.log(-t)}function Ly(t){return-Math.exp(-t)}function Iy(t){return isFinite(t)?+("1e"+t):t<0?0:t}function Ry(t){return function(e){return-t(-e)}}function Fy(t){var e,n,r=t(Dy,Oy),i=r.domain,a=10;function o(){return e=function(t){return t===Math.E?Math.log:10===t&&Math.log10||2===t&&Math.log2||(t=Math.log(t),function(e){return Math.log(e)/t})}(a),n=function(t){return 10===t?Iy:t===Math.E?Math.exp:function(e){return Math.pow(t,e)}}(a),i()[0]<0?(e=Ry(e),n=Ry(n),t(By,Ly)):t(Dy,Oy),r}return r.base=function(t){return arguments.length?(a=+t,o()):a},r.domain=function(t){return arguments.length?(i(t),o()):i()},r.ticks=function(t){var r,o=i(),s=o[0],c=o[o.length-1];(r=c<s)&&(f=s,s=c,c=f);var u,l,h,f=e(s),d=e(c),p=null==t?10:+t,y=[];if(!(a%1)&&d-f<p){if(f=Math.round(f)-1,d=Math.round(d)+1,s>0){for(;f<d;++f)for(l=1,u=n(f);l<a;++l)if(!((h=u*l)<s)){if(h>c)break;y.push(h)}}else for(;f<d;++f)for(l=a-1,u=n(f);l>=1;--l)if(!((h=u*l)<s)){if(h>c)break;y.push(h)}}else y=S(f,d,Math.min(d-f,p)).map(n);return r?y.reverse():y},r.tickFormat=function(t,i){if(null==i&&(i=10===a?".0e":","),"function"!=typeof i&&(i=pc(i)),t===1/0)return i;null==t&&(t=10);var o=Math.max(1,a*t/r.ticks().length);return function(t){var r=t/n(Math.round(e(t)));return r*a<a-.5&&(r*=a),r<=o?i(t):""}},r.nice=function(){return i(Ny(i(),{floor:function(t){return n(Math.floor(e(t)))},ceil:function(t){return n(Math.ceil(e(t)))}}))},r}function Py(){var t=Fy(Ty()).domain([1,10]);return t.copy=function(){return ky(t,Py()).base(t.base())},oy.apply(t,arguments),t}function jy(t){return function(e){return Math.sign(e)*Math.log1p(Math.abs(e/t))}}function Yy(t){return function(e){return Math.sign(e)*Math.expm1(Math.abs(e))*t}}function zy(t){var e=1,n=t(jy(e),Yy(e));return n.constant=function(n){return arguments.length?t(jy(e=+n),Yy(e)):e},Sy(n)}function Uy(){var t=zy(Ty());return t.copy=function(){return ky(t,Uy()).constant(t.constant())},oy.apply(t,arguments)}function qy(t){return function(e){return e<0?-Math.pow(-e,t):Math.pow(e,t)}}function Hy(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}function $y(t){return t<0?-t*t:t*t}function Wy(t){var e=t(vy,vy),n=1;function r(){return 1===n?t(vy,vy):.5===n?t(Hy,$y):t(qy(n),qy(1/n))}return e.exponent=function(t){return arguments.length?(n=+t,r()):n},Sy(e)}function Vy(){var t=Wy(Ty());return t.copy=function(){return ky(t,Vy()).exponent(t.exponent())},oy.apply(t,arguments),t}function Gy(){return Vy.apply(null,arguments).exponent(.5)}function Xy(){var t,e=[],n=[],r=[];function a(){var t=0,i=Math.max(1,n.length);for(r=new Array(i-1);++t<i;)r[t-1]=O(e,t/i);return o}function o(e){return isNaN(e=+e)?t:n[u(r,e)]}return o.invertExtent=function(t){var i=n.indexOf(t);return i<0?[NaN,NaN]:[i>0?r[i-1]:e[0],i<r.length?r[i]:e[e.length-1]]},o.domain=function(t){if(!arguments.length)return e.slice();e=[];for(var n,r=0,o=t.length;r<o;++r)null==(n=t[r])||isNaN(n=+n)||e.push(n);return e.sort(i),a()},o.range=function(t){return arguments.length?(n=ly.call(t),a()):n.slice()},o.unknown=function(e){return arguments.length?(t=e,o):t},o.quantiles=function(){return r.slice()},o.copy=function(){return Xy().domain(e).range(n).unknown(t)},oy.apply(o,arguments)}function Zy(){var t,e=0,n=1,r=1,i=[.5],a=[0,1];function o(e){return e<=e?a[u(i,e,0,r)]:t}function s(){var t=-1;for(i=new Array(r);++t<r;)i[t]=((t+1)*n-(t-r)*e)/(r+1);return o}return o.domain=function(t){return arguments.length?(e=+t[0],n=+t[1],s()):[e,n]},o.range=function(t){return arguments.length?(r=(a=ly.call(t)).length-1,s()):a.slice()},o.invertExtent=function(t){var o=a.indexOf(t);return o<0?[NaN,NaN]:o<1?[e,i[0]]:o>=r?[i[r-1],n]:[i[o-1],i[o]]},o.unknown=function(e){return arguments.length?(t=e,o):o},o.thresholds=function(){return i.slice()},o.copy=function(){return Zy().domain([e,n]).range(a).unknown(t)},oy.apply(Sy(o),arguments)}function Qy(){var t,e=[.5],n=[0,1],r=1;function i(i){return i<=i?n[u(e,i,0,r)]:t}return i.domain=function(t){return arguments.length?(e=ly.call(t),r=Math.min(e.length,n.length-1),i):e.slice()},i.range=function(t){return arguments.length?(n=ly.call(t),r=Math.min(e.length,n.length-1),i):n.slice()},i.invertExtent=function(t){var r=n.indexOf(t);return[e[r-1],e[r]]},i.unknown=function(e){return arguments.length?(t=e,i):t},i.copy=function(){return Qy().domain(e).range(n).unknown(t)},oy.apply(i,arguments)}var Ky=new Date,Jy=new Date;function tg(t,e,n,r){function i(e){return t(e=0===arguments.length?new Date:new Date(+e)),e}return i.floor=function(e){return t(e=new Date(+e)),e},i.ceil=function(n){return t(n=new Date(n-1)),e(n,1),t(n),n},i.round=function(t){var e=i(t),n=i.ceil(t);return t-e<n-t?e:n},i.offset=function(t,n){return e(t=new Date(+t),null==n?1:Math.floor(n)),t},i.range=function(n,r,a){var o,s=[];if(n=i.ceil(n),a=null==a?1:Math.floor(a),!(n<r&&a>0))return s;do{s.push(o=new Date(+n)),e(n,a),t(n)}while(o<n&&n<r);return s},i.filter=function(n){return tg((function(e){if(e>=e)for(;t(e),!n(e);)e.setTime(e-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;e(t,-1),!n(t););else for(;--r>=0;)for(;e(t,1),!n(t););}))},n&&(i.count=function(e,r){return Ky.setTime(+e),Jy.setTime(+r),t(Ky),t(Jy),Math.floor(n(Ky,Jy))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(e){return r(e)%t==0}:function(e){return i.count(0,e)%t==0}):i:null}),i}var eg=tg((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,e){t.setFullYear(t.getFullYear()+e)}),(function(t,e){return e.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));eg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?tg((function(e){e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)}),(function(e,n){e.setFullYear(e.getFullYear()+n*t)})):null};const ng=eg;var rg=eg.range,ig=tg((function(t){t.setDate(1),t.setHours(0,0,0,0)}),(function(t,e){t.setMonth(t.getMonth()+e)}),(function(t,e){return e.getMonth()-t.getMonth()+12*(e.getFullYear()-t.getFullYear())}),(function(t){return t.getMonth()}));const ag=ig;var og=ig.range,sg=1e3,cg=6e4,ug=36e5,lg=864e5,hg=6048e5;function fg(t){return tg((function(e){e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+7*e)}),(function(t,e){return(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*cg)/hg}))}var dg=fg(0),pg=fg(1),yg=fg(2),gg=fg(3),mg=fg(4),vg=fg(5),bg=fg(6),_g=dg.range,xg=pg.range,wg=yg.range,kg=gg.range,Tg=mg.range,Eg=vg.range,Cg=bg.range,Sg=tg((function(t){t.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+e)}),(function(t,e){return(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*cg)/lg}),(function(t){return t.getDate()-1}));const Ag=Sg;var Mg=Sg.range,Ng=tg((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*sg-t.getMinutes()*cg)}),(function(t,e){t.setTime(+t+e*ug)}),(function(t,e){return(e-t)/ug}),(function(t){return t.getHours()}));const Dg=Ng;var Og=Ng.range,Bg=tg((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*sg)}),(function(t,e){t.setTime(+t+e*cg)}),(function(t,e){return(e-t)/cg}),(function(t){return t.getMinutes()}));const Lg=Bg;var Ig=Bg.range,Rg=tg((function(t){t.setTime(t-t.getMilliseconds())}),(function(t,e){t.setTime(+t+e*sg)}),(function(t,e){return(e-t)/sg}),(function(t){return t.getUTCSeconds()}));const Fg=Rg;var Pg=Rg.range,jg=tg((function(){}),(function(t,e){t.setTime(+t+e)}),(function(t,e){return e-t}));jg.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?tg((function(e){e.setTime(Math.floor(e/t)*t)}),(function(e,n){e.setTime(+e+n*t)}),(function(e,n){return(n-e)/t})):jg:null};const Yg=jg;var zg=jg.range;function Ug(t){return tg((function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+7*e)}),(function(t,e){return(e-t)/hg}))}var qg=Ug(0),Hg=Ug(1),$g=Ug(2),Wg=Ug(3),Vg=Ug(4),Gg=Ug(5),Xg=Ug(6),Zg=qg.range,Qg=Hg.range,Kg=$g.range,Jg=Wg.range,tm=Vg.range,em=Gg.range,nm=Xg.range,rm=tg((function(t){t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+e)}),(function(t,e){return(e-t)/lg}),(function(t){return t.getUTCDate()-1}));const im=rm;var am=rm.range,om=tg((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCFullYear(t.getUTCFullYear()+e)}),(function(t,e){return e.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));om.every=function(t){return isFinite(t=Math.floor(t))&&t>0?tg((function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)}),(function(e,n){e.setUTCFullYear(e.getUTCFullYear()+n*t)})):null};const sm=om;var cm=om.range;function um(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function lm(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function hm(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}function fm(t){var e=t.dateTime,n=t.date,r=t.time,i=t.periods,a=t.days,o=t.shortDays,s=t.months,c=t.shortMonths,u=Tm(i),l=Em(i),h=Tm(a),f=Em(a),d=Tm(o),p=Em(o),y=Tm(s),g=Em(s),m=Tm(c),v=Em(c),b={a:function(t){return o[t.getDay()]},A:function(t){return a[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return s[t.getMonth()]},c:null,d:Wm,e:Wm,f:Qm,g:cv,G:lv,H:Vm,I:Gm,j:Xm,L:Zm,m:Km,M:Jm,p:function(t){return i[+(t.getHours()>=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:Bv,s:Lv,S:tv,u:ev,U:nv,V:iv,w:av,W:ov,x:null,X:null,y:sv,Y:uv,Z:hv,"%":Ov},_={a:function(t){return o[t.getUTCDay()]},A:function(t){return a[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return s[t.getUTCMonth()]},c:null,d:fv,e:fv,f:mv,g:Av,G:Nv,H:dv,I:pv,j:yv,L:gv,m:vv,M:bv,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:Bv,s:Lv,S:_v,u:xv,U:wv,V:Tv,w:Ev,W:Cv,x:null,X:null,y:Sv,Y:Mv,Z:Dv,"%":Ov},x={a:function(t,e,n){var r=d.exec(e.slice(n));return r?(t.w=p[r[0].toLowerCase()],n+r[0].length):-1},A:function(t,e,n){var r=h.exec(e.slice(n));return r?(t.w=f[r[0].toLowerCase()],n+r[0].length):-1},b:function(t,e,n){var r=m.exec(e.slice(n));return r?(t.m=v[r[0].toLowerCase()],n+r[0].length):-1},B:function(t,e,n){var r=y.exec(e.slice(n));return r?(t.m=g[r[0].toLowerCase()],n+r[0].length):-1},c:function(t,n,r){return T(t,e,n,r)},d:Rm,e:Rm,f:Um,g:Om,G:Dm,H:Pm,I:Pm,j:Fm,L:zm,m:Im,M:jm,p:function(t,e,n){var r=u.exec(e.slice(n));return r?(t.p=l[r[0].toLowerCase()],n+r[0].length):-1},q:Lm,Q:Hm,s:$m,S:Ym,u:Sm,U:Am,V:Mm,w:Cm,W:Nm,x:function(t,e,r){return T(t,n,e,r)},X:function(t,e,n){return T(t,r,e,n)},y:Om,Y:Dm,Z:Bm,"%":qm};function w(t,e){return function(n){var r,i,a,o=[],s=-1,c=0,u=t.length;for(n instanceof Date||(n=new Date(+n));++s<u;)37===t.charCodeAt(s)&&(o.push(t.slice(c,s)),null!=(i=vm[r=t.charAt(++s)])?r=t.charAt(++s):i="e"===r?" ":"0",(a=e[r])&&(r=a(n,i)),o.push(r),c=s+1);return o.push(t.slice(c,s)),o.join("")}}function k(t,e){return function(n){var r,i,a=hm(1900,void 0,1);if(T(a,t,n+="",0)!=n.length)return null;if("Q"in a)return new Date(a.Q);if("s"in a)return new Date(1e3*a.s+("L"in a?a.L:0));if(e&&!("Z"in a)&&(a.Z=0),"p"in a&&(a.H=a.H%12+12*a.p),void 0===a.m&&(a.m="q"in a?a.q:0),"V"in a){if(a.V<1||a.V>53)return null;"w"in a||(a.w=1),"Z"in a?(i=(r=lm(hm(a.y,0,1))).getUTCDay(),r=i>4||0===i?Hg.ceil(r):Hg(r),r=im.offset(r,7*(a.V-1)),a.y=r.getUTCFullYear(),a.m=r.getUTCMonth(),a.d=r.getUTCDate()+(a.w+6)%7):(i=(r=um(hm(a.y,0,1))).getDay(),r=i>4||0===i?pg.ceil(r):pg(r),r=Ag.offset(r,7*(a.V-1)),a.y=r.getFullYear(),a.m=r.getMonth(),a.d=r.getDate()+(a.w+6)%7)}else("W"in a||"U"in a)&&("w"in a||(a.w="u"in a?a.u%7:"W"in a?1:0),i="Z"in a?lm(hm(a.y,0,1)).getUTCDay():um(hm(a.y,0,1)).getDay(),a.m=0,a.d="W"in a?(a.w+6)%7+7*a.W-(i+5)%7:a.w+7*a.U-(i+6)%7);return"Z"in a?(a.H+=a.Z/100|0,a.M+=a.Z%100,lm(a)):um(a)}}function T(t,e,n,r){for(var i,a,o=0,s=e.length,c=n.length;o<s;){if(r>=c)return-1;if(37===(i=e.charCodeAt(o++))){if(i=e.charAt(o++),!(a=x[i in vm?e.charAt(o++):i])||(r=a(t,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}return b.x=w(n,b),b.X=w(r,b),b.c=w(e,b),_.x=w(n,_),_.X=w(r,_),_.c=w(e,_),{format:function(t){var e=w(t+="",b);return e.toString=function(){return t},e},parse:function(t){var e=k(t+="",!1);return e.toString=function(){return t},e},utcFormat:function(t){var e=w(t+="",_);return e.toString=function(){return t},e},utcParse:function(t){var e=k(t+="",!0);return e.toString=function(){return t},e}}}var dm,pm,ym,gm,mm,vm={"-":"",_:" ",0:"0"},bm=/^\s*\d+/,_m=/^%/,xm=/[\\^$*+?|[\]().{}]/g;function wm(t,e,n){var r=t<0?"-":"",i=(r?-t:t)+"",a=i.length;return r+(a<n?new Array(n-a+1).join(e)+i:i)}function km(t){return t.replace(xm,"\\$&")}function Tm(t){return new RegExp("^(?:"+t.map(km).join("|")+")","i")}function Em(t){for(var e={},n=-1,r=t.length;++n<r;)e[t[n].toLowerCase()]=n;return e}function Cm(t,e,n){var r=bm.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r[0].length):-1}function Sm(t,e,n){var r=bm.exec(e.slice(n,n+1));return r?(t.u=+r[0],n+r[0].length):-1}function Am(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.U=+r[0],n+r[0].length):-1}function Mm(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.V=+r[0],n+r[0].length):-1}function Nm(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.W=+r[0],n+r[0].length):-1}function Dm(t,e,n){var r=bm.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r[0].length):-1}function Om(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),n+r[0].length):-1}function Bm(t,e,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function Lm(t,e,n){var r=bm.exec(e.slice(n,n+1));return r?(t.q=3*r[0]-3,n+r[0].length):-1}function Im(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+r[0].length):-1}function Rm(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r[0].length):-1}function Fm(t,e,n){var r=bm.exec(e.slice(n,n+3));return r?(t.m=0,t.d=+r[0],n+r[0].length):-1}function Pm(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r[0].length):-1}function jm(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r[0].length):-1}function Ym(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r[0].length):-1}function zm(t,e,n){var r=bm.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r[0].length):-1}function Um(t,e,n){var r=bm.exec(e.slice(n,n+6));return r?(t.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function qm(t,e,n){var r=_m.exec(e.slice(n,n+1));return r?n+r[0].length:-1}function Hm(t,e,n){var r=bm.exec(e.slice(n));return r?(t.Q=+r[0],n+r[0].length):-1}function $m(t,e,n){var r=bm.exec(e.slice(n));return r?(t.s=+r[0],n+r[0].length):-1}function Wm(t,e){return wm(t.getDate(),e,2)}function Vm(t,e){return wm(t.getHours(),e,2)}function Gm(t,e){return wm(t.getHours()%12||12,e,2)}function Xm(t,e){return wm(1+Ag.count(ng(t),t),e,3)}function Zm(t,e){return wm(t.getMilliseconds(),e,3)}function Qm(t,e){return Zm(t,e)+"000"}function Km(t,e){return wm(t.getMonth()+1,e,2)}function Jm(t,e){return wm(t.getMinutes(),e,2)}function tv(t,e){return wm(t.getSeconds(),e,2)}function ev(t){var e=t.getDay();return 0===e?7:e}function nv(t,e){return wm(dg.count(ng(t)-1,t),e,2)}function rv(t){var e=t.getDay();return e>=4||0===e?mg(t):mg.ceil(t)}function iv(t,e){return t=rv(t),wm(mg.count(ng(t),t)+(4===ng(t).getDay()),e,2)}function av(t){return t.getDay()}function ov(t,e){return wm(pg.count(ng(t)-1,t),e,2)}function sv(t,e){return wm(t.getFullYear()%100,e,2)}function cv(t,e){return wm((t=rv(t)).getFullYear()%100,e,2)}function uv(t,e){return wm(t.getFullYear()%1e4,e,4)}function lv(t,e){var n=t.getDay();return wm((t=n>=4||0===n?mg(t):mg.ceil(t)).getFullYear()%1e4,e,4)}function hv(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+wm(e/60|0,"0",2)+wm(e%60,"0",2)}function fv(t,e){return wm(t.getUTCDate(),e,2)}function dv(t,e){return wm(t.getUTCHours(),e,2)}function pv(t,e){return wm(t.getUTCHours()%12||12,e,2)}function yv(t,e){return wm(1+im.count(sm(t),t),e,3)}function gv(t,e){return wm(t.getUTCMilliseconds(),e,3)}function mv(t,e){return gv(t,e)+"000"}function vv(t,e){return wm(t.getUTCMonth()+1,e,2)}function bv(t,e){return wm(t.getUTCMinutes(),e,2)}function _v(t,e){return wm(t.getUTCSeconds(),e,2)}function xv(t){var e=t.getUTCDay();return 0===e?7:e}function wv(t,e){return wm(qg.count(sm(t)-1,t),e,2)}function kv(t){var e=t.getUTCDay();return e>=4||0===e?Vg(t):Vg.ceil(t)}function Tv(t,e){return t=kv(t),wm(Vg.count(sm(t),t)+(4===sm(t).getUTCDay()),e,2)}function Ev(t){return t.getUTCDay()}function Cv(t,e){return wm(Hg.count(sm(t)-1,t),e,2)}function Sv(t,e){return wm(t.getUTCFullYear()%100,e,2)}function Av(t,e){return wm((t=kv(t)).getUTCFullYear()%100,e,2)}function Mv(t,e){return wm(t.getUTCFullYear()%1e4,e,4)}function Nv(t,e){var n=t.getUTCDay();return wm((t=n>=4||0===n?Vg(t):Vg.ceil(t)).getUTCFullYear()%1e4,e,4)}function Dv(){return"+0000"}function Ov(){return"%"}function Bv(t){return+t}function Lv(t){return Math.floor(+t/1e3)}function Iv(t){return dm=fm(t),pm=dm.format,ym=dm.parse,gm=dm.utcFormat,mm=dm.utcParse,dm}Iv({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var Rv=31536e6;function Fv(t){return new Date(t)}function Pv(t){return t instanceof Date?+t:+new Date(+t)}function jv(t,e,n,r,i,o,s,c,u){var l=Ey(vy,vy),h=l.invert,f=l.domain,d=u(".%L"),p=u(":%S"),y=u("%I:%M"),g=u("%I %p"),m=u("%a %d"),v=u("%b %d"),b=u("%B"),_=u("%Y"),x=[[s,1,1e3],[s,5,5e3],[s,15,15e3],[s,30,3e4],[o,1,6e4],[o,5,3e5],[o,15,9e5],[o,30,18e5],[i,1,36e5],[i,3,108e5],[i,6,216e5],[i,12,432e5],[r,1,864e5],[r,2,1728e5],[n,1,6048e5],[e,1,2592e6],[e,3,7776e6],[t,1,Rv]];function w(a){return(s(a)<a?d:o(a)<a?p:i(a)<a?y:r(a)<a?g:e(a)<a?n(a)<a?m:v:t(a)<a?b:_)(a)}function k(e,n,r,i){if(null==e&&(e=10),"number"==typeof e){var o=Math.abs(r-n)/e,s=a((function(t){return t[2]})).right(x,o);s===x.length?(i=M(n/Rv,r/Rv,e),e=t):s?(i=(s=x[o/x[s-1][2]<x[s][2]/o?s-1:s])[1],e=s[0]):(i=Math.max(M(n,r,e),1),e=c)}return null==i?e:e.every(i)}return l.invert=function(t){return new Date(h(t))},l.domain=function(t){return arguments.length?f(uy.call(t,Pv)):f().map(Fv)},l.ticks=function(t,e){var n,r=f(),i=r[0],a=r[r.length-1],o=a<i;return o&&(n=i,i=a,a=n),n=(n=k(t,i,a,e))?n.range(i,a+1):[],o?n.reverse():n},l.tickFormat=function(t,e){return null==e?w:u(e)},l.nice=function(t,e){var n=f();return(t=k(t,n[0],n[n.length-1],e))?f(Ny(n,t)):l},l.copy=function(){return ky(l,jv(t,e,n,r,i,o,s,c,u))},l}function Yv(){return oy.apply(jv(ng,ag,dg,Ag,Dg,Lg,Fg,Yg,pm).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)}var zv=tg((function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCMonth(t.getUTCMonth()+e)}),(function(t,e){return e.getUTCMonth()-t.getUTCMonth()+12*(e.getUTCFullYear()-t.getUTCFullYear())}),(function(t){return t.getUTCMonth()}));const Uv=zv;var qv=zv.range,Hv=tg((function(t){t.setUTCMinutes(0,0,0)}),(function(t,e){t.setTime(+t+e*ug)}),(function(t,e){return(e-t)/ug}),(function(t){return t.getUTCHours()}));const $v=Hv;var Wv=Hv.range,Vv=tg((function(t){t.setUTCSeconds(0,0)}),(function(t,e){t.setTime(+t+e*cg)}),(function(t,e){return(e-t)/cg}),(function(t){return t.getUTCMinutes()}));const Gv=Vv;var Xv=Vv.range;function Zv(){return oy.apply(jv(sm,Uv,qg,im,$v,Gv,Fg,Yg,gm).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)}function Qv(){var t,e,n,r,i,a=0,o=1,s=vy,c=!1;function u(e){return isNaN(e=+e)?i:s(0===n?.5:(e=(r(e)-t)*n,c?Math.max(0,Math.min(1,e)):e))}return u.domain=function(i){return arguments.length?(t=r(a=+i[0]),e=r(o=+i[1]),n=t===e?0:1/(e-t),u):[a,o]},u.clamp=function(t){return arguments.length?(c=!!t,u):c},u.interpolator=function(t){return arguments.length?(s=t,u):s},u.unknown=function(t){return arguments.length?(i=t,u):i},function(i){return r=i,t=i(a),e=i(o),n=t===e?0:1/(e-t),u}}function Kv(t,e){return e.domain(t.domain()).interpolator(t.interpolator()).clamp(t.clamp()).unknown(t.unknown())}function Jv(){var t=Sy(Qv()(vy));return t.copy=function(){return Kv(t,Jv())},sy.apply(t,arguments)}function tb(){var t=Fy(Qv()).domain([1,10]);return t.copy=function(){return Kv(t,tb()).base(t.base())},sy.apply(t,arguments)}function eb(){var t=zy(Qv());return t.copy=function(){return Kv(t,eb()).constant(t.constant())},sy.apply(t,arguments)}function nb(){var t=Wy(Qv());return t.copy=function(){return Kv(t,nb()).exponent(t.exponent())},sy.apply(t,arguments)}function rb(){return nb.apply(null,arguments).exponent(.5)}function ib(){var t=[],e=vy;function n(n){if(!isNaN(n=+n))return e((u(t,n)-1)/(t.length-1))}return n.domain=function(e){if(!arguments.length)return t.slice();t=[];for(var r,a=0,o=e.length;a<o;++a)null==(r=e[a])||isNaN(r=+r)||t.push(r);return t.sort(i),n},n.interpolator=function(t){return arguments.length?(e=t,n):e},n.copy=function(){return ib(e).domain(t)},sy.apply(n,arguments)}function ab(){var t,e,n,r,i,a,o,s=0,c=.5,u=1,l=vy,h=!1;function f(t){return isNaN(t=+t)?o:(t=.5+((t=+a(t))-e)*(t<e?r:i),l(h?Math.max(0,Math.min(1,t)):t))}return f.domain=function(o){return arguments.length?(t=a(s=+o[0]),e=a(c=+o[1]),n=a(u=+o[2]),r=t===e?0:.5/(e-t),i=e===n?0:.5/(n-e),f):[s,c,u]},f.clamp=function(t){return arguments.length?(h=!!t,f):h},f.interpolator=function(t){return arguments.length?(l=t,f):l},f.unknown=function(t){return arguments.length?(o=t,f):o},function(o){return a=o,t=o(s),e=o(c),n=o(u),r=t===e?0:.5/(e-t),i=e===n?0:.5/(n-e),f}}function ob(){var t=Sy(ab()(vy));return t.copy=function(){return Kv(t,ob())},sy.apply(t,arguments)}function sb(){var t=Fy(ab()).domain([.1,1,10]);return t.copy=function(){return Kv(t,sb()).base(t.base())},sy.apply(t,arguments)}function cb(){var t=zy(ab());return t.copy=function(){return Kv(t,cb()).constant(t.constant())},sy.apply(t,arguments)}function ub(){var t=Wy(ab());return t.copy=function(){return Kv(t,ub()).exponent(t.exponent())},sy.apply(t,arguments)}function lb(){return ub.apply(null,arguments).exponent(.5)}function hb(t){for(var e=t.length/6|0,n=new Array(e),r=0;r<e;)n[r]="#"+t.slice(6*r,6*++r);return n}const fb=hb("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"),db=hb("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666"),pb=hb("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666"),yb=hb("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928"),gb=hb("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2"),mb=hb("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc"),vb=hb("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999"),bb=hb("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3"),_b=hb("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f"),xb=hb("4e79a7f28e2ce1575976b7b259a14fedc949af7aa1ff9da79c755fbab0ab");function wb(t){return mn(t[t.length-1])}var kb=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(hb);const Tb=wb(kb);var Eb=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(hb);const Cb=wb(Eb);var Sb=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(hb);const Ab=wb(Sb);var Mb=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(hb);const Nb=wb(Mb);var Db=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(hb);const Ob=wb(Db);var Bb=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(hb);const Lb=wb(Bb);var Ib=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(hb);const Rb=wb(Ib);var Fb=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(hb);const Pb=wb(Fb);var jb=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(hb);const Yb=wb(jb);var zb=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(hb);const Ub=wb(zb);var qb=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(hb);const Hb=wb(qb);var $b=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(hb);const Wb=wb($b);var Vb=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(hb);const Gb=wb(Vb);var Xb=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(hb);const Zb=wb(Xb);var Qb=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(hb);const Kb=wb(Qb);var Jb=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(hb);const t_=wb(Jb);var e_=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(hb);const n_=wb(e_);var r_=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(hb);const i_=wb(r_);var a_=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(hb);const o_=wb(a_);var s_=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(hb);const c_=wb(s_);var u_=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(hb);const l_=wb(u_);var h_=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(hb);const f_=wb(h_);var d_=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(hb);const p_=wb(d_);var y_=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(hb);const g_=wb(y_);var m_=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(hb);const v_=wb(m_);var b_=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(hb);const __=wb(b_);var x_=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(hb);const w_=wb(x_);function k_(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"}const T_=Up(Ha(300,.5,0),Ha(-240,.5,1));var E_=Up(Ha(-100,.75,.35),Ha(80,1.5,.8)),C_=Up(Ha(260,.75,.35),Ha(80,1.5,.8)),S_=Ha();function A_(t){(t<0||t>1)&&(t-=Math.floor(t));var e=Math.abs(t-.5);return S_.h=360*t-100,S_.s=1.5-1.5*e,S_.l=.8-.9*e,S_+""}var M_=Qe(),N_=Math.PI/3,D_=2*Math.PI/3;function O_(t){var e;return t=(.5-t)*Math.PI,M_.r=255*(e=Math.sin(t))*e,M_.g=255*(e=Math.sin(t+N_))*e,M_.b=255*(e=Math.sin(t+D_))*e,M_+""}function B_(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"}function L_(t){var e=t.length;return function(n){return t[Math.max(0,Math.min(e-1,Math.floor(n*e)))]}}const I_=L_(hb("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725"));var R_=L_(hb("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),F_=L_(hb("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),P_=L_(hb("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function j_(t){return Te(ie(t).call(document.documentElement))}var Y_=0;function z_(){return new U_}function U_(){this._="@"+(++Y_).toString(36)}function q_(t){return"string"==typeof t?new xe([document.querySelectorAll(t)],[document.documentElement]):new xe([null==t?[]:t],_e)}function H_(t,e){null==e&&(e=Nn().touches);for(var n=0,r=e?e.length:0,i=new Array(r);n<r;++n)i[n]=Dn(t,e[n]);return i}function $_(t){return function(){return t}}U_.prototype=z_.prototype={constructor:U_,get:function(t){for(var e=this._;!(e in t);)if(!(t=t.parentNode))return;return t[e]},set:function(t,e){return t[this._]=e},remove:function(t){return this._ in t&&delete t[this._]},toString:function(){return this._}};var W_=Math.abs,V_=Math.atan2,G_=Math.cos,X_=Math.max,Z_=Math.min,Q_=Math.sin,K_=Math.sqrt,J_=1e-12,tx=Math.PI,ex=tx/2,nx=2*tx;function rx(t){return t>1?0:t<-1?tx:Math.acos(t)}function ix(t){return t>=1?ex:t<=-1?-ex:Math.asin(t)}function ax(t){return t.innerRadius}function ox(t){return t.outerRadius}function sx(t){return t.startAngle}function cx(t){return t.endAngle}function ux(t){return t&&t.padAngle}function lx(t,e,n,r,i,a,o,s){var c=n-t,u=r-e,l=o-i,h=s-a,f=h*c-l*u;if(!(f*f<J_))return[t+(f=(l*(e-a)-h*(t-i))/f)*c,e+f*u]}function hx(t,e,n,r,i,a,o){var s=t-n,c=e-r,u=(o?a:-a)/K_(s*s+c*c),l=u*c,h=-u*s,f=t+l,d=e+h,p=n+l,y=r+h,g=(f+p)/2,m=(d+y)/2,v=p-f,b=y-d,_=v*v+b*b,x=i-a,w=f*y-p*d,k=(b<0?-1:1)*K_(X_(0,x*x*_-w*w)),T=(w*b-v*k)/_,E=(-w*v-b*k)/_,C=(w*b+v*k)/_,S=(-w*v+b*k)/_,A=T-g,M=E-m,N=C-g,D=S-m;return A*A+M*M>N*N+D*D&&(T=C,E=S),{cx:T,cy:E,x01:-l,y01:-h,x11:T*(i/x-1),y11:E*(i/x-1)}}function fx(){var t=ax,e=ox,n=$_(0),r=null,i=sx,a=cx,o=ux,s=null;function c(){var c,u,l=+t.apply(this,arguments),h=+e.apply(this,arguments),f=i.apply(this,arguments)-ex,d=a.apply(this,arguments)-ex,p=W_(d-f),y=d>f;if(s||(s=c=Wi()),h<l&&(u=h,h=l,l=u),h>J_)if(p>nx-J_)s.moveTo(h*G_(f),h*Q_(f)),s.arc(0,0,h,f,d,!y),l>J_&&(s.moveTo(l*G_(d),l*Q_(d)),s.arc(0,0,l,d,f,y));else{var g,m,v=f,b=d,_=f,x=d,w=p,k=p,T=o.apply(this,arguments)/2,E=T>J_&&(r?+r.apply(this,arguments):K_(l*l+h*h)),C=Z_(W_(h-l)/2,+n.apply(this,arguments)),S=C,A=C;if(E>J_){var M=ix(E/l*Q_(T)),N=ix(E/h*Q_(T));(w-=2*M)>J_?(_+=M*=y?1:-1,x-=M):(w=0,_=x=(f+d)/2),(k-=2*N)>J_?(v+=N*=y?1:-1,b-=N):(k=0,v=b=(f+d)/2)}var D=h*G_(v),O=h*Q_(v),B=l*G_(x),L=l*Q_(x);if(C>J_){var I,R=h*G_(b),F=h*Q_(b),P=l*G_(_),j=l*Q_(_);if(p<tx&&(I=lx(D,O,P,j,R,F,B,L))){var Y=D-I[0],z=O-I[1],U=R-I[0],q=F-I[1],H=1/Q_(rx((Y*U+z*q)/(K_(Y*Y+z*z)*K_(U*U+q*q)))/2),$=K_(I[0]*I[0]+I[1]*I[1]);S=Z_(C,(l-$)/(H-1)),A=Z_(C,(h-$)/(H+1))}}k>J_?A>J_?(g=hx(P,j,D,O,h,A,y),m=hx(R,F,B,L,h,A,y),s.moveTo(g.cx+g.x01,g.cy+g.y01),A<C?s.arc(g.cx,g.cy,A,V_(g.y01,g.x01),V_(m.y01,m.x01),!y):(s.arc(g.cx,g.cy,A,V_(g.y01,g.x01),V_(g.y11,g.x11),!y),s.arc(0,0,h,V_(g.cy+g.y11,g.cx+g.x11),V_(m.cy+m.y11,m.cx+m.x11),!y),s.arc(m.cx,m.cy,A,V_(m.y11,m.x11),V_(m.y01,m.x01),!y))):(s.moveTo(D,O),s.arc(0,0,h,v,b,!y)):s.moveTo(D,O),l>J_&&w>J_?S>J_?(g=hx(B,L,R,F,l,-S,y),m=hx(D,O,P,j,l,-S,y),s.lineTo(g.cx+g.x01,g.cy+g.y01),S<C?s.arc(g.cx,g.cy,S,V_(g.y01,g.x01),V_(m.y01,m.x01),!y):(s.arc(g.cx,g.cy,S,V_(g.y01,g.x01),V_(g.y11,g.x11),!y),s.arc(0,0,l,V_(g.cy+g.y11,g.cx+g.x11),V_(m.cy+m.y11,m.cx+m.x11),y),s.arc(m.cx,m.cy,S,V_(m.y11,m.x11),V_(m.y01,m.x01),!y))):s.arc(0,0,l,x,_,y):s.lineTo(B,L)}else s.moveTo(0,0);if(s.closePath(),c)return s=null,c+""||null}return c.centroid=function(){var n=(+t.apply(this,arguments)+ +e.apply(this,arguments))/2,r=(+i.apply(this,arguments)+ +a.apply(this,arguments))/2-tx/2;return[G_(r)*n,Q_(r)*n]},c.innerRadius=function(e){return arguments.length?(t="function"==typeof e?e:$_(+e),c):t},c.outerRadius=function(t){return arguments.length?(e="function"==typeof t?t:$_(+t),c):e},c.cornerRadius=function(t){return arguments.length?(n="function"==typeof t?t:$_(+t),c):n},c.padRadius=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:$_(+t),c):r},c.startAngle=function(t){return arguments.length?(i="function"==typeof t?t:$_(+t),c):i},c.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:$_(+t),c):a},c.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:$_(+t),c):o},c.context=function(t){return arguments.length?(s=null==t?null:t,c):s},c}function dx(t){this._context=t}function px(t){return new dx(t)}function yx(t){return t[0]}function gx(t){return t[1]}function mx(){var t=yx,e=gx,n=$_(!0),r=null,i=px,a=null;function o(o){var s,c,u,l=o.length,h=!1;for(null==r&&(a=i(u=Wi())),s=0;s<=l;++s)!(s<l&&n(c=o[s],s,o))===h&&((h=!h)?a.lineStart():a.lineEnd()),h&&a.point(+t(c,s,o),+e(c,s,o));if(u)return a=null,u+""||null}return o.x=function(e){return arguments.length?(t="function"==typeof e?e:$_(+e),o):t},o.y=function(t){return arguments.length?(e="function"==typeof t?t:$_(+t),o):e},o.defined=function(t){return arguments.length?(n="function"==typeof t?t:$_(!!t),o):n},o.curve=function(t){return arguments.length?(i=t,null!=r&&(a=i(r)),o):i},o.context=function(t){return arguments.length?(null==t?r=a=null:a=i(r=t),o):r},o}function vx(){var t=yx,e=null,n=$_(0),r=gx,i=$_(!0),a=null,o=px,s=null;function c(c){var u,l,h,f,d,p=c.length,y=!1,g=new Array(p),m=new Array(p);for(null==a&&(s=o(d=Wi())),u=0;u<=p;++u){if(!(u<p&&i(f=c[u],u,c))===y)if(y=!y)l=u,s.areaStart(),s.lineStart();else{for(s.lineEnd(),s.lineStart(),h=u-1;h>=l;--h)s.point(g[h],m[h]);s.lineEnd(),s.areaEnd()}y&&(g[u]=+t(f,u,c),m[u]=+n(f,u,c),s.point(e?+e(f,u,c):g[u],r?+r(f,u,c):m[u]))}if(d)return s=null,d+""||null}function u(){return mx().defined(i).curve(o).context(a)}return c.x=function(n){return arguments.length?(t="function"==typeof n?n:$_(+n),e=null,c):t},c.x0=function(e){return arguments.length?(t="function"==typeof e?e:$_(+e),c):t},c.x1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:$_(+t),c):e},c.y=function(t){return arguments.length?(n="function"==typeof t?t:$_(+t),r=null,c):n},c.y0=function(t){return arguments.length?(n="function"==typeof t?t:$_(+t),c):n},c.y1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:$_(+t),c):r},c.lineX0=c.lineY0=function(){return u().x(t).y(n)},c.lineY1=function(){return u().x(t).y(r)},c.lineX1=function(){return u().x(e).y(n)},c.defined=function(t){return arguments.length?(i="function"==typeof t?t:$_(!!t),c):i},c.curve=function(t){return arguments.length?(o=t,null!=a&&(s=o(a)),c):o},c.context=function(t){return arguments.length?(null==t?a=s=null:s=o(a=t),c):a},c}function bx(t,e){return e<t?-1:e>t?1:e>=t?0:NaN}function _x(t){return t}function xx(){var t=_x,e=bx,n=null,r=$_(0),i=$_(nx),a=$_(0);function o(o){var s,c,u,l,h,f=o.length,d=0,p=new Array(f),y=new Array(f),g=+r.apply(this,arguments),m=Math.min(nx,Math.max(-nx,i.apply(this,arguments)-g)),v=Math.min(Math.abs(m)/f,a.apply(this,arguments)),b=v*(m<0?-1:1);for(s=0;s<f;++s)(h=y[p[s]=s]=+t(o[s],s,o))>0&&(d+=h);for(null!=e?p.sort((function(t,n){return e(y[t],y[n])})):null!=n&&p.sort((function(t,e){return n(o[t],o[e])})),s=0,u=d?(m-f*b)/d:0;s<f;++s,g=l)c=p[s],l=g+((h=y[c])>0?h*u:0)+b,y[c]={data:o[c],index:s,value:h,startAngle:g,endAngle:l,padAngle:v};return y}return o.value=function(e){return arguments.length?(t="function"==typeof e?e:$_(+e),o):t},o.sortValues=function(t){return arguments.length?(e=t,n=null,o):e},o.sort=function(t){return arguments.length?(n=t,e=null,o):n},o.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:$_(+t),o):r},o.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:$_(+t),o):i},o.padAngle=function(t){return arguments.length?(a="function"==typeof t?t:$_(+t),o):a},o}dx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:this._context.lineTo(t,e)}}};var wx=Tx(px);function kx(t){this._curve=t}function Tx(t){function e(e){return new kx(t(e))}return e._curve=t,e}function Ex(t){var e=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?e(Tx(t)):e()._curve},t}function Cx(){return Ex(mx().curve(wx))}function Sx(){var t=vx().curve(wx),e=t.curve,n=t.lineX0,r=t.lineX1,i=t.lineY0,a=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Ex(n())},delete t.lineX0,t.lineEndAngle=function(){return Ex(r())},delete t.lineX1,t.lineInnerRadius=function(){return Ex(i())},delete t.lineY0,t.lineOuterRadius=function(){return Ex(a())},delete t.lineY1,t.curve=function(t){return arguments.length?e(Tx(t)):e()._curve},t}function Ax(t,e){return[(e=+e)*Math.cos(t-=Math.PI/2),e*Math.sin(t)]}kx.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,e){this._curve.point(e*Math.sin(t),e*-Math.cos(t))}};var Mx=Array.prototype.slice;function Nx(t){return t.source}function Dx(t){return t.target}function Ox(t){var e=Nx,n=Dx,r=yx,i=gx,a=null;function o(){var o,s=Mx.call(arguments),c=e.apply(this,s),u=n.apply(this,s);if(a||(a=o=Wi()),t(a,+r.apply(this,(s[0]=c,s)),+i.apply(this,s),+r.apply(this,(s[0]=u,s)),+i.apply(this,s)),o)return a=null,o+""||null}return o.source=function(t){return arguments.length?(e=t,o):e},o.target=function(t){return arguments.length?(n=t,o):n},o.x=function(t){return arguments.length?(r="function"==typeof t?t:$_(+t),o):r},o.y=function(t){return arguments.length?(i="function"==typeof t?t:$_(+t),o):i},o.context=function(t){return arguments.length?(a=null==t?null:t,o):a},o}function Bx(t,e,n,r,i){t.moveTo(e,n),t.bezierCurveTo(e=(e+r)/2,n,e,i,r,i)}function Lx(t,e,n,r,i){t.moveTo(e,n),t.bezierCurveTo(e,n=(n+i)/2,r,n,r,i)}function Ix(t,e,n,r,i){var a=Ax(e,n),o=Ax(e,n=(n+i)/2),s=Ax(r,n),c=Ax(r,i);t.moveTo(a[0],a[1]),t.bezierCurveTo(o[0],o[1],s[0],s[1],c[0],c[1])}function Rx(){return Ox(Bx)}function Fx(){return Ox(Lx)}function Px(){var t=Ox(Ix);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t}const jx={draw:function(t,e){var n=Math.sqrt(e/tx);t.moveTo(n,0),t.arc(0,0,n,0,nx)}},Yx={draw:function(t,e){var n=Math.sqrt(e/5)/2;t.moveTo(-3*n,-n),t.lineTo(-n,-n),t.lineTo(-n,-3*n),t.lineTo(n,-3*n),t.lineTo(n,-n),t.lineTo(3*n,-n),t.lineTo(3*n,n),t.lineTo(n,n),t.lineTo(n,3*n),t.lineTo(-n,3*n),t.lineTo(-n,n),t.lineTo(-3*n,n),t.closePath()}};var zx=Math.sqrt(1/3),Ux=2*zx;const qx={draw:function(t,e){var n=Math.sqrt(e/Ux),r=n*zx;t.moveTo(0,-n),t.lineTo(r,0),t.lineTo(0,n),t.lineTo(-r,0),t.closePath()}};var Hx=Math.sin(tx/10)/Math.sin(7*tx/10),$x=Math.sin(nx/10)*Hx,Wx=-Math.cos(nx/10)*Hx;const Vx={draw:function(t,e){var n=Math.sqrt(.8908130915292852*e),r=$x*n,i=Wx*n;t.moveTo(0,-n),t.lineTo(r,i);for(var a=1;a<5;++a){var o=nx*a/5,s=Math.cos(o),c=Math.sin(o);t.lineTo(c*n,-s*n),t.lineTo(s*r-c*i,c*r+s*i)}t.closePath()}},Gx={draw:function(t,e){var n=Math.sqrt(e),r=-n/2;t.rect(r,r,n,n)}};var Xx=Math.sqrt(3);const Zx={draw:function(t,e){var n=-Math.sqrt(e/(3*Xx));t.moveTo(0,2*n),t.lineTo(-Xx*n,-n),t.lineTo(Xx*n,-n),t.closePath()}};var Qx=-.5,Kx=Math.sqrt(3)/2,Jx=1/Math.sqrt(12),tw=3*(Jx/2+1);const ew={draw:function(t,e){var n=Math.sqrt(e/tw),r=n/2,i=n*Jx,a=r,o=n*Jx+n,s=-a,c=o;t.moveTo(r,i),t.lineTo(a,o),t.lineTo(s,c),t.lineTo(Qx*r-Kx*i,Kx*r+Qx*i),t.lineTo(Qx*a-Kx*o,Kx*a+Qx*o),t.lineTo(Qx*s-Kx*c,Kx*s+Qx*c),t.lineTo(Qx*r+Kx*i,Qx*i-Kx*r),t.lineTo(Qx*a+Kx*o,Qx*o-Kx*a),t.lineTo(Qx*s+Kx*c,Qx*c-Kx*s),t.closePath()}};var nw=[jx,Yx,qx,Gx,Vx,Zx,ew];function rw(){var t=$_(jx),e=$_(64),n=null;function r(){var r;if(n||(n=r=Wi()),t.apply(this,arguments).draw(n,+e.apply(this,arguments)),r)return n=null,r+""||null}return r.type=function(e){return arguments.length?(t="function"==typeof e?e:$_(e),r):t},r.size=function(t){return arguments.length?(e="function"==typeof t?t:$_(+t),r):e},r.context=function(t){return arguments.length?(n=null==t?null:t,r):n},r}function iw(){}function aw(t,e,n){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+n)/6)}function ow(t){this._context=t}function sw(t){return new ow(t)}function cw(t){this._context=t}function uw(t){return new cw(t)}function lw(t){this._context=t}function hw(t){return new lw(t)}function fw(t,e){this._basis=new ow(t),this._beta=e}ow.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:aw(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:aw(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},cw.prototype={areaStart:iw,areaEnd:iw,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:aw(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},lw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(n,r):this._context.moveTo(n,r);break;case 3:this._point=4;default:aw(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},fw.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,e=this._y,n=t.length-1;if(n>0)for(var r,i=t[0],a=e[0],o=t[n]-i,s=e[n]-a,c=-1;++c<=n;)r=c/n,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*o),this._beta*e[c]+(1-this._beta)*(a+r*s));this._x=this._y=null,this._basis.lineEnd()},point:function(t,e){this._x.push(+t),this._y.push(+e)}};const dw=function t(e){function n(t){return 1===e?new ow(t):new fw(t,e)}return n.beta=function(e){return t(+e)},n}(.85);function pw(t,e,n){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-e),t._y2+t._k*(t._y1-n),t._x2,t._y2)}function yw(t,e){this._context=t,this._k=(1-e)/6}yw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:pw(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2,this._x1=t,this._y1=e;break;case 2:this._point=3;default:pw(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const gw=function t(e){function n(t){return new yw(t,e)}return n.tension=function(e){return t(+e)},n}(0);function mw(t,e){this._context=t,this._k=(1-e)/6}mw.prototype={areaStart:iw,areaEnd:iw,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:pw(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const vw=function t(e){function n(t){return new mw(t,e)}return n.tension=function(e){return t(+e)},n}(0);function bw(t,e){this._context=t,this._k=(1-e)/6}bw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:pw(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const _w=function t(e){function n(t){return new bw(t,e)}return n.tension=function(e){return t(+e)},n}(0);function xw(t,e,n){var r=t._x1,i=t._y1,a=t._x2,o=t._y2;if(t._l01_a>J_){var s=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*s-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*s-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>J_){var u=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,l=3*t._l23_a*(t._l23_a+t._l12_a);a=(a*u+t._x1*t._l23_2a-e*t._l12_2a)/l,o=(o*u+t._y1*t._l23_2a-n*t._l12_2a)/l}t._context.bezierCurveTo(r,i,a,o,t._x2,t._y2)}function ww(t,e){this._context=t,this._alpha=e}ww.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3;default:xw(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const kw=function t(e){function n(t){return e?new ww(t,e):new yw(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function Tw(t,e){this._context=t,this._alpha=e}Tw.prototype={areaStart:iw,areaEnd:iw,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:xw(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const Ew=function t(e){function n(t){return e?new Tw(t,e):new mw(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function Cw(t,e){this._context=t,this._alpha=e}Cw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:xw(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const Sw=function t(e){function n(t){return e?new Cw(t,e):new bw(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function Aw(t){this._context=t}function Mw(t){return new Aw(t)}function Nw(t){return t<0?-1:1}function Dw(t,e,n){var r=t._x1-t._x0,i=e-t._x1,a=(t._y1-t._y0)/(r||i<0&&-0),o=(n-t._y1)/(i||r<0&&-0),s=(a*i+o*r)/(r+i);return(Nw(a)+Nw(o))*Math.min(Math.abs(a),Math.abs(o),.5*Math.abs(s))||0}function Ow(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}function Bw(t,e,n){var r=t._x0,i=t._y0,a=t._x1,o=t._y1,s=(a-r)/3;t._context.bezierCurveTo(r+s,i+s*e,a-s,o-s*n,a,o)}function Lw(t){this._context=t}function Iw(t){this._context=new Rw(t)}function Rw(t){this._context=t}function Fw(t){return new Lw(t)}function Pw(t){return new Iw(t)}function jw(t){this._context=t}function Yw(t){var e,n,r=t.length-1,i=new Array(r),a=new Array(r),o=new Array(r);for(i[0]=0,a[0]=2,o[0]=t[0]+2*t[1],e=1;e<r-1;++e)i[e]=1,a[e]=4,o[e]=4*t[e]+2*t[e+1];for(i[r-1]=2,a[r-1]=7,o[r-1]=8*t[r-1]+t[r],e=1;e<r;++e)n=i[e]/a[e-1],a[e]-=n,o[e]-=n*o[e-1];for(i[r-1]=o[r-1]/a[r-1],e=r-2;e>=0;--e)i[e]=(o[e]-i[e+1])/a[e];for(a[r-1]=(t[r]+i[r-1])/2,e=0;e<r-1;++e)a[e]=2*t[e+1]-i[e+1];return[i,a]}function zw(t){return new jw(t)}function Uw(t,e){this._context=t,this._t=e}function qw(t){return new Uw(t,.5)}function Hw(t){return new Uw(t,0)}function $w(t){return new Uw(t,1)}function Ww(t,e){if((i=t.length)>1)for(var n,r,i,a=1,o=t[e[0]],s=o.length;a<i;++a)for(r=o,o=t[e[a]],n=0;n<s;++n)o[n][1]+=o[n][0]=isNaN(r[n][1])?r[n][0]:r[n][1]}function Vw(t){for(var e=t.length,n=new Array(e);--e>=0;)n[e]=e;return n}function Gw(t,e){return t[e]}function Xw(){var t=$_([]),e=Vw,n=Ww,r=Gw;function i(i){var a,o,s=t.apply(this,arguments),c=i.length,u=s.length,l=new Array(u);for(a=0;a<u;++a){for(var h,f=s[a],d=l[a]=new Array(c),p=0;p<c;++p)d[p]=h=[0,+r(i[p],f,p,i)],h.data=i[p];d.key=f}for(a=0,o=e(l);a<u;++a)l[o[a]].index=a;return n(l,o),l}return i.keys=function(e){return arguments.length?(t="function"==typeof e?e:$_(Mx.call(e)),i):t},i.value=function(t){return arguments.length?(r="function"==typeof t?t:$_(+t),i):r},i.order=function(t){return arguments.length?(e=null==t?Vw:"function"==typeof t?t:$_(Mx.call(t)),i):e},i.offset=function(t){return arguments.length?(n=null==t?Ww:t,i):n},i}function Zw(t,e){if((r=t.length)>0){for(var n,r,i,a=0,o=t[0].length;a<o;++a){for(i=n=0;n<r;++n)i+=t[n][a][1]||0;if(i)for(n=0;n<r;++n)t[n][a][1]/=i}Ww(t,e)}}function Qw(t,e){if((s=t.length)>0)for(var n,r,i,a,o,s,c=0,u=t[e[0]].length;c<u;++c)for(a=o=0,n=0;n<s;++n)(i=(r=t[e[n]][c])[1]-r[0])>0?(r[0]=a,r[1]=a+=i):i<0?(r[1]=o,r[0]=o+=i):(r[0]=0,r[1]=i)}function Kw(t,e){if((n=t.length)>0){for(var n,r=0,i=t[e[0]],a=i.length;r<a;++r){for(var o=0,s=0;o<n;++o)s+=t[o][r][1]||0;i[r][1]+=i[r][0]=-s/2}Ww(t,e)}}function Jw(t,e){if((i=t.length)>0&&(r=(n=t[e[0]]).length)>0){for(var n,r,i,a=0,o=1;o<r;++o){for(var s=0,c=0,u=0;s<i;++s){for(var l=t[e[s]],h=l[o][1]||0,f=(h-(l[o-1][1]||0))/2,d=0;d<s;++d){var p=t[e[d]];f+=(p[o][1]||0)-(p[o-1][1]||0)}c+=h,u+=f*h}n[o-1][1]+=n[o-1][0]=a,c&&(a-=u/c)}n[o-1][1]+=n[o-1][0]=a,Ww(t,e)}}function tk(t){var e=t.map(ek);return Vw(t).sort((function(t,n){return e[t]-e[n]}))}function ek(t){for(var e,n=-1,r=0,i=t.length,a=-1/0;++n<i;)(e=+t[n][1])>a&&(a=e,r=n);return r}function nk(t){var e=t.map(rk);return Vw(t).sort((function(t,n){return e[t]-e[n]}))}function rk(t){for(var e,n=0,r=-1,i=t.length;++r<i;)(e=+t[r][1])&&(n+=e);return n}function ik(t){return nk(t).reverse()}function ak(t){var e,n,r=t.length,i=t.map(rk),a=tk(t),o=0,s=0,c=[],u=[];for(e=0;e<r;++e)n=a[e],o<s?(o+=i[n],c.push(n)):(s+=i[n],u.push(n));return u.reverse().concat(c)}function ok(t){return Vw(t).reverse()}Aw.prototype={areaStart:iw,areaEnd:iw,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))}},Lw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:Bw(this,this._t0,Ow(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){var n=NaN;if(e=+e,(t=+t)!==this._x1||e!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,Bw(this,Ow(this,n=Dw(this,t,e)),n);break;default:Bw(this,this._t0,n=Dw(this,t,e))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=n}}},(Iw.prototype=Object.create(Lw.prototype)).point=function(t,e){Lw.prototype.point.call(this,e,t)},Rw.prototype={moveTo:function(t,e){this._context.moveTo(e,t)},closePath:function(){this._context.closePath()},lineTo:function(t,e){this._context.lineTo(e,t)},bezierCurveTo:function(t,e,n,r,i,a){this._context.bezierCurveTo(e,t,r,n,a,i)}},jw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,e=this._y,n=t.length;if(n)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),2===n)this._context.lineTo(t[1],e[1]);else for(var r=Yw(t),i=Yw(e),a=0,o=1;o<n;++a,++o)this._context.bezierCurveTo(r[0][a],i[0][a],r[1][a],i[1][a],t[o],e[o]);(this._line||0!==this._line&&1===n)&&this._context.closePath(),this._line=1-this._line,this._x=this._y=null},point:function(t,e){this._x.push(+t),this._y.push(+e)}},Uw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=this._y=NaN,this._point=0},lineEnd:function(){0<this._t&&this._t<1&&2===this._point&&this._context.lineTo(this._x,this._y),(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line>=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var n=this._x*(1-this._t)+t*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,e)}}this._x=t,this._y=e}};var sk="%Y-%m-%dT%H:%M:%S.%LZ",ck=Date.prototype.toISOString?function(t){return t.toISOString()}:gm(sk);const uk=ck;var lk=+new Date("2000-01-01T00:00:00.000Z")?function(t){var e=new Date(t);return isNaN(e)?null:e}:mm(sk);const hk=lk;function fk(t,e,n){var r=new Wn,i=e;return null==e?(r.restart(t,e,n),r):(e=+e,n=null==n?Hn():+n,r.restart((function a(o){o+=i,r.restart(a,i+=e,n),t(o)}),e,n),r)}function dk(t){return function(){return t}}function pk(t){return t[0]}function yk(t){return t[1]}function gk(){this._=null}function mk(t){t.U=t.C=t.L=t.R=t.P=t.N=null}function vk(t,e){var n=e,r=e.R,i=n.U;i?i.L===n?i.L=r:i.R=r:t._=r,r.U=i,n.U=r,n.R=r.L,n.R&&(n.R.U=n),r.L=n}function bk(t,e){var n=e,r=e.L,i=n.U;i?i.L===n?i.L=r:i.R=r:t._=r,r.U=i,n.U=r,n.L=r.R,n.L&&(n.L.U=n),r.R=n}function _k(t){for(;t.L;)t=t.L;return t}gk.prototype={constructor:gk,insert:function(t,e){var n,r,i;if(t){if(e.P=t,e.N=t.N,t.N&&(t.N.P=e),t.N=e,t.R){for(t=t.R;t.L;)t=t.L;t.L=e}else t.R=e;n=t}else this._?(t=_k(this._),e.P=null,e.N=t,t.P=t.L=e,n=t):(e.P=e.N=null,this._=e,n=null);for(e.L=e.R=null,e.U=n,e.C=!0,t=e;n&&n.C;)n===(r=n.U).L?(i=r.R)&&i.C?(n.C=i.C=!1,r.C=!0,t=r):(t===n.R&&(vk(this,n),n=(t=n).U),n.C=!1,r.C=!0,bk(this,r)):(i=r.L)&&i.C?(n.C=i.C=!1,r.C=!0,t=r):(t===n.L&&(bk(this,n),n=(t=n).U),n.C=!1,r.C=!0,vk(this,r)),n=t.U;this._.C=!1},remove:function(t){t.N&&(t.N.P=t.P),t.P&&(t.P.N=t.N),t.N=t.P=null;var e,n,r,i=t.U,a=t.L,o=t.R;if(n=a?o?_k(o):a:o,i?i.L===t?i.L=n:i.R=n:this._=n,a&&o?(r=n.C,n.C=t.C,n.L=a,a.U=n,n!==o?(i=n.U,n.U=t.U,t=n.R,i.L=t,n.R=o,o.U=n):(n.U=i,i=n,t=n.R)):(r=t.C,t=n),t&&(t.U=i),!r)if(t&&t.C)t.C=!1;else{do{if(t===this._)break;if(t===i.L){if((e=i.R).C&&(e.C=!1,i.C=!0,vk(this,i),e=i.R),e.L&&e.L.C||e.R&&e.R.C){e.R&&e.R.C||(e.L.C=!1,e.C=!0,bk(this,e),e=i.R),e.C=i.C,i.C=e.R.C=!1,vk(this,i),t=this._;break}}else if((e=i.L).C&&(e.C=!1,i.C=!0,bk(this,i),e=i.L),e.L&&e.L.C||e.R&&e.R.C){e.L&&e.L.C||(e.R.C=!1,e.C=!0,vk(this,e),e=i.L),e.C=i.C,i.C=e.L.C=!1,bk(this,i),t=this._;break}e.C=!0,t=i,i=i.U}while(!t.C);t&&(t.C=!1)}}};const xk=gk;function wk(t,e,n,r){var i=[null,null],a=Wk.push(i)-1;return i.left=t,i.right=e,n&&Tk(i,t,e,n),r&&Tk(i,e,t,r),Hk[t.index].halfedges.push(a),Hk[e.index].halfedges.push(a),i}function kk(t,e,n){var r=[e,n];return r.left=t,r}function Tk(t,e,n,r){t[0]||t[1]?t.left===n?t[1]=r:t[0]=r:(t[0]=r,t.left=e,t.right=n)}function Ek(t,e,n,r,i){var a,o=t[0],s=t[1],c=o[0],u=o[1],l=0,h=1,f=s[0]-c,d=s[1]-u;if(a=e-c,f||!(a>0)){if(a/=f,f<0){if(a<l)return;a<h&&(h=a)}else if(f>0){if(a>h)return;a>l&&(l=a)}if(a=r-c,f||!(a<0)){if(a/=f,f<0){if(a>h)return;a>l&&(l=a)}else if(f>0){if(a<l)return;a<h&&(h=a)}if(a=n-u,d||!(a>0)){if(a/=d,d<0){if(a<l)return;a<h&&(h=a)}else if(d>0){if(a>h)return;a>l&&(l=a)}if(a=i-u,d||!(a<0)){if(a/=d,d<0){if(a>h)return;a>l&&(l=a)}else if(d>0){if(a<l)return;a<h&&(h=a)}return!(l>0||h<1)||(l>0&&(t[0]=[c+l*f,u+l*d]),h<1&&(t[1]=[c+h*f,u+h*d]),!0)}}}}}function Ck(t,e,n,r,i){var a=t[1];if(a)return!0;var o,s,c=t[0],u=t.left,l=t.right,h=u[0],f=u[1],d=l[0],p=l[1],y=(h+d)/2,g=(f+p)/2;if(p===f){if(y<e||y>=r)return;if(h>d){if(c){if(c[1]>=i)return}else c=[y,n];a=[y,i]}else{if(c){if(c[1]<n)return}else c=[y,i];a=[y,n]}}else if(s=g-(o=(h-d)/(p-f))*y,o<-1||o>1)if(h>d){if(c){if(c[1]>=i)return}else c=[(n-s)/o,n];a=[(i-s)/o,i]}else{if(c){if(c[1]<n)return}else c=[(i-s)/o,i];a=[(n-s)/o,n]}else if(f<p){if(c){if(c[0]>=r)return}else c=[e,o*e+s];a=[r,o*r+s]}else{if(c){if(c[0]<e)return}else c=[r,o*r+s];a=[e,o*e+s]}return t[0]=c,t[1]=a,!0}function Sk(t,e){var n=t.site,r=e.left,i=e.right;return n===i&&(i=r,r=n),i?Math.atan2(i[1]-r[1],i[0]-r[0]):(n===r?(r=e[1],i=e[0]):(r=e[0],i=e[1]),Math.atan2(r[0]-i[0],i[1]-r[1]))}function Ak(t,e){return e[+(e.left!==t.site)]}function Mk(t,e){return e[+(e.left===t.site)]}var Nk,Dk=[];function Ok(){mk(this),this.x=this.y=this.arc=this.site=this.cy=null}function Bk(t){var e=t.P,n=t.N;if(e&&n){var r=e.site,i=t.site,a=n.site;if(r!==a){var o=i[0],s=i[1],c=r[0]-o,u=r[1]-s,l=a[0]-o,h=a[1]-s,f=2*(c*h-u*l);if(!(f>=-Gk)){var d=c*c+u*u,p=l*l+h*h,y=(h*d-u*p)/f,g=(c*p-l*d)/f,m=Dk.pop()||new Ok;m.arc=t,m.site=i,m.x=y+o,m.y=(m.cy=g+s)+Math.sqrt(y*y+g*g),t.circle=m;for(var v=null,b=$k._;b;)if(m.y<b.y||m.y===b.y&&m.x<=b.x){if(!b.L){v=b.P;break}b=b.L}else{if(!b.R){v=b;break}b=b.R}$k.insert(v,m),v||(Nk=m)}}}}function Lk(t){var e=t.circle;e&&(e.P||(Nk=e.N),$k.remove(e),Dk.push(e),mk(e),t.circle=null)}var Ik=[];function Rk(){mk(this),this.edge=this.site=this.circle=null}function Fk(t){var e=Ik.pop()||new Rk;return e.site=t,e}function Pk(t){Lk(t),qk.remove(t),Ik.push(t),mk(t)}function jk(t){var e=t.circle,n=e.x,r=e.cy,i=[n,r],a=t.P,o=t.N,s=[t];Pk(t);for(var c=a;c.circle&&Math.abs(n-c.circle.x)<Vk&&Math.abs(r-c.circle.cy)<Vk;)a=c.P,s.unshift(c),Pk(c),c=a;s.unshift(c),Lk(c);for(var u=o;u.circle&&Math.abs(n-u.circle.x)<Vk&&Math.abs(r-u.circle.cy)<Vk;)o=u.N,s.push(u),Pk(u),u=o;s.push(u),Lk(u);var l,h=s.length;for(l=1;l<h;++l)u=s[l],c=s[l-1],Tk(u.edge,c.site,u.site,i);c=s[0],(u=s[h-1]).edge=wk(c.site,u.site,null,i),Bk(c),Bk(u)}function Yk(t){for(var e,n,r,i,a=t[0],o=t[1],s=qk._;s;)if((r=zk(s,o)-a)>Vk)s=s.L;else{if(!((i=a-Uk(s,o))>Vk)){r>-Vk?(e=s.P,n=s):i>-Vk?(e=s,n=s.N):e=n=s;break}if(!s.R){e=s;break}s=s.R}!function(t){Hk[t.index]={site:t,halfedges:[]}}(t);var c=Fk(t);if(qk.insert(e,c),e||n){if(e===n)return Lk(e),n=Fk(e.site),qk.insert(c,n),c.edge=n.edge=wk(e.site,c.site),Bk(e),void Bk(n);if(n){Lk(e),Lk(n);var u=e.site,l=u[0],h=u[1],f=t[0]-l,d=t[1]-h,p=n.site,y=p[0]-l,g=p[1]-h,m=2*(f*g-d*y),v=f*f+d*d,b=y*y+g*g,_=[(g*v-d*b)/m+l,(f*b-y*v)/m+h];Tk(n.edge,u,p,_),c.edge=wk(u,t,null,_),n.edge=wk(t,p,null,_),Bk(e),Bk(n)}else c.edge=wk(e.site,c.site)}}function zk(t,e){var n=t.site,r=n[0],i=n[1],a=i-e;if(!a)return r;var o=t.P;if(!o)return-1/0;var s=(n=o.site)[0],c=n[1],u=c-e;if(!u)return s;var l=s-r,h=1/a-1/u,f=l/u;return h?(-f+Math.sqrt(f*f-2*h*(l*l/(-2*u)-c+u/2+i-a/2)))/h+r:(r+s)/2}function Uk(t,e){var n=t.N;if(n)return zk(n,e);var r=t.site;return r[1]===e?r[0]:1/0}var qk,Hk,$k,Wk,Vk=1e-6,Gk=1e-12;function Xk(t,e,n){return(t[0]-n[0])*(e[1]-t[1])-(t[0]-e[0])*(n[1]-t[1])}function Zk(t,e){return e[1]-t[1]||e[0]-t[0]}function Qk(t,e){var n,r,i,a=t.sort(Zk).pop();for(Wk=[],Hk=new Array(t.length),qk=new xk,$k=new xk;;)if(i=Nk,a&&(!i||a[1]<i.y||a[1]===i.y&&a[0]<i.x))a[0]===n&&a[1]===r||(Yk(a),n=a[0],r=a[1]),a=t.pop();else{if(!i)break;jk(i.arc)}if(function(){for(var t,e,n,r,i=0,a=Hk.length;i<a;++i)if((t=Hk[i])&&(r=(e=t.halfedges).length)){var o=new Array(r),s=new Array(r);for(n=0;n<r;++n)o[n]=n,s[n]=Sk(t,Wk[e[n]]);for(o.sort((function(t,e){return s[e]-s[t]})),n=0;n<r;++n)s[n]=e[o[n]];for(n=0;n<r;++n)e[n]=s[n]}}(),e){var o=+e[0][0],s=+e[0][1],c=+e[1][0],u=+e[1][1];!function(t,e,n,r){for(var i,a=Wk.length;a--;)Ck(i=Wk[a],t,e,n,r)&&Ek(i,t,e,n,r)&&(Math.abs(i[0][0]-i[1][0])>Vk||Math.abs(i[0][1]-i[1][1])>Vk)||delete Wk[a]}(o,s,c,u),function(t,e,n,r){var i,a,o,s,c,u,l,h,f,d,p,y,g=Hk.length,m=!0;for(i=0;i<g;++i)if(a=Hk[i]){for(o=a.site,s=(c=a.halfedges).length;s--;)Wk[c[s]]||c.splice(s,1);for(s=0,u=c.length;s<u;)p=(d=Mk(a,Wk[c[s]]))[0],y=d[1],h=(l=Ak(a,Wk[c[++s%u]]))[0],f=l[1],(Math.abs(p-h)>Vk||Math.abs(y-f)>Vk)&&(c.splice(s,0,Wk.push(kk(o,d,Math.abs(p-t)<Vk&&r-y>Vk?[t,Math.abs(h-t)<Vk?f:r]:Math.abs(y-r)<Vk&&n-p>Vk?[Math.abs(f-r)<Vk?h:n,r]:Math.abs(p-n)<Vk&&y-e>Vk?[n,Math.abs(h-n)<Vk?f:e]:Math.abs(y-e)<Vk&&p-t>Vk?[Math.abs(f-e)<Vk?h:t,e]:null))-1),++u);u&&(m=!1)}if(m){var v,b,_,x=1/0;for(i=0,m=null;i<g;++i)(a=Hk[i])&&(_=(v=(o=a.site)[0]-t)*v+(b=o[1]-e)*b)<x&&(x=_,m=a);if(m){var w=[t,e],k=[t,r],T=[n,r],E=[n,e];m.halfedges.push(Wk.push(kk(o=m.site,w,k))-1,Wk.push(kk(o,k,T))-1,Wk.push(kk(o,T,E))-1,Wk.push(kk(o,E,w))-1)}}for(i=0;i<g;++i)(a=Hk[i])&&(a.halfedges.length||delete Hk[i])}(o,s,c,u)}this.edges=Wk,this.cells=Hk,qk=$k=Wk=Hk=null}function Kk(){var t=pk,e=yk,n=null;function r(r){return new Qk(r.map((function(n,i){var a=[Math.round(t(n,i,r)/Vk)*Vk,Math.round(e(n,i,r)/Vk)*Vk];return a.index=i,a.data=n,a})),n)}return r.polygons=function(t){return r(t).polygons()},r.links=function(t){return r(t).links()},r.triangles=function(t){return r(t).triangles()},r.x=function(e){return arguments.length?(t="function"==typeof e?e:dk(+e),r):t},r.y=function(t){return arguments.length?(e="function"==typeof t?t:dk(+t),r):e},r.extent=function(t){return arguments.length?(n=null==t?null:[[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]],r):n&&[[n[0][0],n[0][1]],[n[1][0],n[1][1]]]},r.size=function(t){return arguments.length?(n=null==t?null:[[0,0],[+t[0],+t[1]]],r):n&&[n[1][0]-n[0][0],n[1][1]-n[0][1]]},r}function Jk(t){return function(){return t}}function tT(t,e,n){this.target=t,this.type=e,this.transform=n}function eT(t,e,n){this.k=t,this.x=e,this.y=n}Qk.prototype={constructor:Qk,polygons:function(){var t=this.edges;return this.cells.map((function(e){var n=e.halfedges.map((function(n){return Ak(e,t[n])}));return n.data=e.site.data,n}))},triangles:function(){var t=[],e=this.edges;return this.cells.forEach((function(n,r){if(a=(i=n.halfedges).length)for(var i,a,o,s=n.site,c=-1,u=e[i[a-1]],l=u.left===s?u.right:u.left;++c<a;)o=l,l=(u=e[i[c]]).left===s?u.right:u.left,o&&l&&r<o.index&&r<l.index&&Xk(s,o,l)<0&&t.push([s.data,o.data,l.data])})),t},links:function(){return this.edges.filter((function(t){return t.right})).map((function(t){return{source:t.left.data,target:t.right.data}}))},find:function(t,e,n){for(var r,i,a=this,o=a._found||0,s=a.cells.length;!(i=a.cells[o]);)if(++o>=s)return null;var c=t-i.site[0],u=e-i.site[1],l=c*c+u*u;do{i=a.cells[r=o],o=null,i.halfedges.forEach((function(n){var r=a.edges[n],s=r.left;if(s!==i.site&&s||(s=r.right)){var c=t-s[0],u=e-s[1],h=c*c+u*u;h<l&&(l=h,o=s.index)}}))}while(null!==o);return a._found=r,null==n||l<=n*n?i.site:null}},eT.prototype={constructor:eT,scale:function(t){return 1===t?this:new eT(this.k*t,this.x,this.y)},translate:function(t,e){return 0===t&0===e?this:new eT(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var nT=new eT(1,0,0);function rT(t){for(;!t.__zoom;)if(!(t=t.parentNode))return nT;return t.__zoom}function iT(){le.stopImmediatePropagation()}function aT(){le.preventDefault(),le.stopImmediatePropagation()}function oT(){return!le.ctrlKey&&!le.button}function sT(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function cT(){return this.__zoom||nT}function uT(){return-le.deltaY*(1===le.deltaMode?.05:le.deltaMode?1:.002)}function lT(){return navigator.maxTouchPoints||"ontouchstart"in this}function hT(t,e,n){var r=t.invertX(e[0][0])-n[0][0],i=t.invertX(e[1][0])-n[1][0],a=t.invertY(e[0][1])-n[0][1],o=t.invertY(e[1][1])-n[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),o>a?(a+o)/2:Math.min(0,a)||Math.max(0,o))}function fT(){var t,e,n=oT,r=sT,i=hT,a=uT,o=lT,s=[0,1/0],c=[[-1/0,-1/0],[1/0,1/0]],u=250,l=Op,h=ft("start","zoom","end"),f=500,d=0;function p(t){t.property("__zoom",cT).on("wheel.zoom",x).on("mousedown.zoom",w).on("dblclick.zoom",k).filter(o).on("touchstart.zoom",T).on("touchmove.zoom",E).on("touchend.zoom touchcancel.zoom",C).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function y(t,e){return(e=Math.max(s[0],Math.min(s[1],e)))===t.k?t:new eT(e,t.x,t.y)}function g(t,e,n){var r=e[0]-n[0]*t.k,i=e[1]-n[1]*t.k;return r===t.x&&i===t.y?t:new eT(t.k,r,i)}function m(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function v(t,e,n){t.on("start.zoom",(function(){b(this,arguments).start()})).on("interrupt.zoom end.zoom",(function(){b(this,arguments).end()})).tween("zoom",(function(){var t=this,i=arguments,a=b(t,i),o=r.apply(t,i),s=null==n?m(o):"function"==typeof n?n.apply(t,i):n,c=Math.max(o[1][0]-o[0][0],o[1][1]-o[0][1]),u=t.__zoom,h="function"==typeof e?e.apply(t,i):e,f=l(u.invert(s).concat(c/u.k),h.invert(s).concat(c/h.k));return function(t){if(1===t)t=h;else{var e=f(t),n=c/e[2];t=new eT(n,s[0]-e[0]*n,s[1]-e[1]*n)}a.zoom(null,t)}}))}function b(t,e,n){return!n&&t.__zooming||new _(t,e)}function _(t,e){this.that=t,this.args=e,this.active=0,this.extent=r.apply(t,e),this.taps=0}function x(){if(n.apply(this,arguments)){var t=b(this,arguments),e=this.__zoom,r=Math.max(s[0],Math.min(s[1],e.k*Math.pow(2,a.apply(this,arguments)))),o=Bn(this);if(t.wheel)t.mouse[0][0]===o[0]&&t.mouse[0][1]===o[1]||(t.mouse[1]=e.invert(t.mouse[0]=o)),clearTimeout(t.wheel);else{if(e.k===r)return;t.mouse=[o,e.invert(o)],ar(this),t.start()}aT(),t.wheel=setTimeout(u,150),t.zoom("mouse",i(g(y(e,r),t.mouse[0],t.mouse[1]),t.extent,c))}function u(){t.wheel=null,t.end()}}function w(){if(!e&&n.apply(this,arguments)){var t=b(this,arguments,!0),r=Te(le.view).on("mousemove.zoom",u,!0).on("mouseup.zoom",l,!0),a=Bn(this),o=le.clientX,s=le.clientY;Se(le.view),iT(),t.mouse=[a,this.__zoom.invert(a)],ar(this),t.start()}function u(){if(aT(),!t.moved){var e=le.clientX-o,n=le.clientY-s;t.moved=e*e+n*n>d}t.zoom("mouse",i(g(t.that.__zoom,t.mouse[0]=Bn(t.that),t.mouse[1]),t.extent,c))}function l(){r.on("mousemove.zoom mouseup.zoom",null),Ae(le.view,t.moved),aT(),t.end()}}function k(){if(n.apply(this,arguments)){var t=this.__zoom,e=Bn(this),a=t.invert(e),o=t.k*(le.shiftKey?.5:2),s=i(g(y(t,o),e,a),r.apply(this,arguments),c);aT(),u>0?Te(this).transition().duration(u).call(v,s,e):Te(this).call(p.transform,s)}}function T(){if(n.apply(this,arguments)){var e,r,i,a,o=le.touches,s=o.length,c=b(this,arguments,le.changedTouches.length===s);for(iT(),r=0;r<s;++r)a=[a=On(this,o,(i=o[r]).identifier),this.__zoom.invert(a),i.identifier],c.touch0?c.touch1||c.touch0[2]===a[2]||(c.touch1=a,c.taps=0):(c.touch0=a,e=!0,c.taps=1+!!t);t&&(t=clearTimeout(t)),e&&(c.taps<2&&(t=setTimeout((function(){t=null}),f)),ar(this),c.start())}}function E(){if(this.__zooming){var e,n,r,a,o=b(this,arguments),s=le.changedTouches,u=s.length;for(aT(),t&&(t=clearTimeout(t)),o.taps=0,e=0;e<u;++e)r=On(this,s,(n=s[e]).identifier),o.touch0&&o.touch0[2]===n.identifier?o.touch0[0]=r:o.touch1&&o.touch1[2]===n.identifier&&(o.touch1[0]=r);if(n=o.that.__zoom,o.touch1){var l=o.touch0[0],h=o.touch0[1],f=o.touch1[0],d=o.touch1[1],p=(p=f[0]-l[0])*p+(p=f[1]-l[1])*p,m=(m=d[0]-h[0])*m+(m=d[1]-h[1])*m;n=y(n,Math.sqrt(p/m)),r=[(l[0]+f[0])/2,(l[1]+f[1])/2],a=[(h[0]+d[0])/2,(h[1]+d[1])/2]}else{if(!o.touch0)return;r=o.touch0[0],a=o.touch0[1]}o.zoom("touch",i(g(n,r,a),o.extent,c))}}function C(){if(this.__zooming){var t,n,r=b(this,arguments),i=le.changedTouches,a=i.length;for(iT(),e&&clearTimeout(e),e=setTimeout((function(){e=null}),f),t=0;t<a;++t)n=i[t],r.touch0&&r.touch0[2]===n.identifier?delete r.touch0:r.touch1&&r.touch1[2]===n.identifier&&delete r.touch1;if(r.touch1&&!r.touch0&&(r.touch0=r.touch1,delete r.touch1),r.touch0)r.touch0[1]=this.__zoom.invert(r.touch0[0]);else if(r.end(),2===r.taps){var o=Te(this).on("dblclick.zoom");o&&o.apply(this,arguments)}}}return p.transform=function(t,e,n){var r=t.selection?t.selection():t;r.property("__zoom",cT),t!==r?v(t,e,n):r.interrupt().each((function(){b(this,arguments).start().zoom(null,"function"==typeof e?e.apply(this,arguments):e).end()}))},p.scaleBy=function(t,e,n){p.scaleTo(t,(function(){var t=this.__zoom.k,n="function"==typeof e?e.apply(this,arguments):e;return t*n}),n)},p.scaleTo=function(t,e,n){p.transform(t,(function(){var t=r.apply(this,arguments),a=this.__zoom,o=null==n?m(t):"function"==typeof n?n.apply(this,arguments):n,s=a.invert(o),u="function"==typeof e?e.apply(this,arguments):e;return i(g(y(a,u),o,s),t,c)}),n)},p.translateBy=function(t,e,n){p.transform(t,(function(){return i(this.__zoom.translate("function"==typeof e?e.apply(this,arguments):e,"function"==typeof n?n.apply(this,arguments):n),r.apply(this,arguments),c)}))},p.translateTo=function(t,e,n,a){p.transform(t,(function(){var t=r.apply(this,arguments),o=this.__zoom,s=null==a?m(t):"function"==typeof a?a.apply(this,arguments):a;return i(nT.translate(s[0],s[1]).scale(o.k).translate("function"==typeof e?-e.apply(this,arguments):-e,"function"==typeof n?-n.apply(this,arguments):-n),t,c)}),a)},_.prototype={start:function(){return 1==++this.active&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(t,e){return this.mouse&&"mouse"!==t&&(this.mouse[1]=e.invert(this.mouse[0])),this.touch0&&"touch"!==t&&(this.touch0[1]=e.invert(this.touch0[0])),this.touch1&&"touch"!==t&&(this.touch1[1]=e.invert(this.touch1[0])),this.that.__zoom=e,this.emit("zoom"),this},end:function(){return 0==--this.active&&(delete this.that.__zooming,this.emit("end")),this},emit:function(t){ge(new tT(p,t,this.that.__zoom),h.apply,h,[t,this.that,this.args])}},p.wheelDelta=function(t){return arguments.length?(a="function"==typeof t?t:Jk(+t),p):a},p.filter=function(t){return arguments.length?(n="function"==typeof t?t:Jk(!!t),p):n},p.touchable=function(t){return arguments.length?(o="function"==typeof t?t:Jk(!!t),p):o},p.extent=function(t){return arguments.length?(r="function"==typeof t?t:Jk([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),p):r},p.scaleExtent=function(t){return arguments.length?(s[0]=+t[0],s[1]=+t[1],p):[s[0],s[1]]},p.translateExtent=function(t){return arguments.length?(c[0][0]=+t[0][0],c[1][0]=+t[1][0],c[0][1]=+t[0][1],c[1][1]=+t[1][1],p):[[c[0][0],c[0][1]],[c[1][0],c[1][1]]]},p.constrain=function(t){return arguments.length?(i=t,p):i},p.duration=function(t){return arguments.length?(u=+t,p):u},p.interpolate=function(t){return arguments.length?(l=t,p):l},p.on=function(){var t=h.on.apply(h,arguments);return t===h?p:t},p.clickDistance=function(t){return arguments.length?(d=(t=+t)*t,p):Math.sqrt(d)},p}rT.prototype=eT.prototype},681:(t,e,n)=>{t.exports={graphlib:n(574),layout:n(8123),debug:n(7570),util:{time:n(1138).time,notime:n(1138).notime},version:n(8177)}},1207:(t,e,n)=>{"use strict";var r=n(8436),i=n(4079);t.exports={run:function(t){var e="greedy"===t.graph().acyclicer?i(t,function(t){return function(e){return t.edge(e).weight}}(t)):function(t){var e=[],n={},i={};return r.forEach(t.nodes(),(function a(o){r.has(i,o)||(i[o]=!0,n[o]=!0,r.forEach(t.outEdges(o),(function(t){r.has(n,t.w)?e.push(t):a(t.w)})),delete n[o])})),e}(t);r.forEach(e,(function(e){var n=t.edge(e);t.removeEdge(e),n.forwardName=e.name,n.reversed=!0,t.setEdge(e.w,e.v,n,r.uniqueId("rev"))}))},undo:function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(n.reversed){t.removeEdge(e);var r=n.forwardName;delete n.reversed,delete n.forwardName,t.setEdge(e.w,e.v,n,r)}}))}}},1133:(t,e,n)=>{var r=n(8436),i=n(1138);function a(t,e,n,r,a,o){var s={width:0,height:0,rank:o,borderType:e},c=a[e][o-1],u=i.addDummyNode(t,"border",s,n);a[e][o]=u,t.setParent(u,r),c&&t.setEdge(c,u,{weight:1})}t.exports=function(t){r.forEach(t.children(),(function e(n){var i=t.children(n),o=t.node(n);if(i.length&&r.forEach(i,e),r.has(o,"minRank")){o.borderLeft=[],o.borderRight=[];for(var s=o.minRank,c=o.maxRank+1;s<c;++s)a(t,"borderLeft","_bl",n,o,s),a(t,"borderRight","_br",n,o,s)}}))}},3258:(t,e,n)=>{"use strict";var r=n(8436);function i(t){r.forEach(t.nodes(),(function(e){a(t.node(e))})),r.forEach(t.edges(),(function(e){a(t.edge(e))}))}function a(t){var e=t.width;t.width=t.height,t.height=e}function o(t){t.y=-t.y}function s(t){var e=t.x;t.x=t.y,t.y=e}t.exports={adjust:function(t){var e=t.graph().rankdir.toLowerCase();"lr"!==e&&"rl"!==e||i(t)},undo:function(t){var e=t.graph().rankdir.toLowerCase();"bt"!==e&&"rl"!==e||function(t){r.forEach(t.nodes(),(function(e){o(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.forEach(n.points,o),r.has(n,"y")&&o(n)}))}(t),"lr"!==e&&"rl"!==e||(function(t){r.forEach(t.nodes(),(function(e){s(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.forEach(n.points,s),r.has(n,"x")&&s(n)}))}(t),i(t))}}},7822:t=>{function e(){var t={};t._next=t._prev=t,this._sentinel=t}function n(t){t._prev._next=t._next,t._next._prev=t._prev,delete t._next,delete t._prev}function r(t,e){if("_next"!==t&&"_prev"!==t)return e}t.exports=e,e.prototype.dequeue=function(){var t=this._sentinel,e=t._prev;if(e!==t)return n(e),e},e.prototype.enqueue=function(t){var e=this._sentinel;t._prev&&t._next&&n(t),t._next=e._next,e._next._prev=t,e._next=t,t._prev=e},e.prototype.toString=function(){for(var t=[],e=this._sentinel,n=e._prev;n!==e;)t.push(JSON.stringify(n,r)),n=n._prev;return"["+t.join(", ")+"]"}},7570:(t,e,n)=>{var r=n(8436),i=n(1138),a=n(574).Graph;t.exports={debugOrdering:function(t){var e=i.buildLayerMatrix(t),n=new a({compound:!0,multigraph:!0}).setGraph({});return r.forEach(t.nodes(),(function(e){n.setNode(e,{label:e}),n.setParent(e,"layer"+t.node(e).rank)})),r.forEach(t.edges(),(function(t){n.setEdge(t.v,t.w,{},t.name)})),r.forEach(e,(function(t,e){var i="layer"+e;n.setNode(i,{rank:"same"}),r.reduce(t,(function(t,e){return n.setEdge(t,e,{style:"invis"}),e}))})),n}}},574:(t,e,n)=>{var r;try{r=n(8282)}catch(t){}r||(r=window.graphlib),t.exports=r},4079:(t,e,n)=>{var r=n(8436),i=n(574).Graph,a=n(7822);t.exports=function(t,e){if(t.nodeCount()<=1)return[];var n=function(t,e){var n=new i,o=0,s=0;r.forEach(t.nodes(),(function(t){n.setNode(t,{v:t,in:0,out:0})})),r.forEach(t.edges(),(function(t){var r=n.edge(t.v,t.w)||0,i=e(t),a=r+i;n.setEdge(t.v,t.w,a),s=Math.max(s,n.node(t.v).out+=i),o=Math.max(o,n.node(t.w).in+=i)}));var u=r.range(s+o+3).map((function(){return new a})),l=o+1;return r.forEach(n.nodes(),(function(t){c(u,l,n.node(t))})),{graph:n,buckets:u,zeroIdx:l}}(t,e||o),u=function(t,e,n){for(var r,i=[],a=e[e.length-1],o=e[0];t.nodeCount();){for(;r=o.dequeue();)s(t,e,n,r);for(;r=a.dequeue();)s(t,e,n,r);if(t.nodeCount())for(var c=e.length-2;c>0;--c)if(r=e[c].dequeue()){i=i.concat(s(t,e,n,r,!0));break}}return i}(n.graph,n.buckets,n.zeroIdx);return r.flatten(r.map(u,(function(e){return t.outEdges(e.v,e.w)})),!0)};var o=r.constant(1);function s(t,e,n,i,a){var o=a?[]:void 0;return r.forEach(t.inEdges(i.v),(function(r){var i=t.edge(r),s=t.node(r.v);a&&o.push({v:r.v,w:r.w}),s.out-=i,c(e,n,s)})),r.forEach(t.outEdges(i.v),(function(r){var i=t.edge(r),a=r.w,o=t.node(a);o.in-=i,c(e,n,o)})),t.removeNode(i.v),o}function c(t,e,n){n.out?n.in?t[n.out-n.in+e].enqueue(n):t[t.length-1].enqueue(n):t[0].enqueue(n)}},8123:(t,e,n)=>{"use strict";var r=n(8436),i=n(1207),a=n(5995),o=n(8093),s=n(1138).normalizeRanks,c=n(4219),u=n(1138).removeEmptyRanks,l=n(2981),h=n(1133),f=n(3258),d=n(3408),p=n(7873),y=n(1138),g=n(574).Graph;t.exports=function(t,e){var n=e&&e.debugTiming?y.time:y.notime;n("layout",(function(){var e=n(" buildLayoutGraph",(function(){return function(t){var e=new g({multigraph:!0,compound:!0}),n=C(t.graph());return e.setGraph(r.merge({},v,E(n,m),r.pick(n,b))),r.forEach(t.nodes(),(function(n){var i=C(t.node(n));e.setNode(n,r.defaults(E(i,_),x)),e.setParent(n,t.parent(n))})),r.forEach(t.edges(),(function(n){var i=C(t.edge(n));e.setEdge(n,r.merge({},k,E(i,w),r.pick(i,T)))})),e}(t)}));n(" runLayout",(function(){!function(t,e){e(" makeSpaceForEdgeLabels",(function(){!function(t){var e=t.graph();e.ranksep/=2,r.forEach(t.edges(),(function(n){var r=t.edge(n);r.minlen*=2,"c"!==r.labelpos.toLowerCase()&&("TB"===e.rankdir||"BT"===e.rankdir?r.width+=r.labeloffset:r.height+=r.labeloffset)}))}(t)})),e(" removeSelfEdges",(function(){!function(t){r.forEach(t.edges(),(function(e){if(e.v===e.w){var n=t.node(e.v);n.selfEdges||(n.selfEdges=[]),n.selfEdges.push({e,label:t.edge(e)}),t.removeEdge(e)}}))}(t)})),e(" acyclic",(function(){i.run(t)})),e(" nestingGraph.run",(function(){l.run(t)})),e(" rank",(function(){o(y.asNonCompoundGraph(t))})),e(" injectEdgeLabelProxies",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(n.width&&n.height){var r=t.node(e.v),i={rank:(t.node(e.w).rank-r.rank)/2+r.rank,e};y.addDummyNode(t,"edge-proxy",i,"_ep")}}))}(t)})),e(" removeEmptyRanks",(function(){u(t)})),e(" nestingGraph.cleanup",(function(){l.cleanup(t)})),e(" normalizeRanks",(function(){s(t)})),e(" assignRankMinMax",(function(){!function(t){var e=0;r.forEach(t.nodes(),(function(n){var i=t.node(n);i.borderTop&&(i.minRank=t.node(i.borderTop).rank,i.maxRank=t.node(i.borderBottom).rank,e=r.max(e,i.maxRank))})),t.graph().maxRank=e}(t)})),e(" removeEdgeLabelProxies",(function(){!function(t){r.forEach(t.nodes(),(function(e){var n=t.node(e);"edge-proxy"===n.dummy&&(t.edge(n.e).labelRank=n.rank,t.removeNode(e))}))}(t)})),e(" normalize.run",(function(){a.run(t)})),e(" parentDummyChains",(function(){c(t)})),e(" addBorderSegments",(function(){h(t)})),e(" order",(function(){d(t)})),e(" insertSelfEdges",(function(){!function(t){var e=y.buildLayerMatrix(t);r.forEach(e,(function(e){var n=0;r.forEach(e,(function(e,i){var a=t.node(e);a.order=i+n,r.forEach(a.selfEdges,(function(e){y.addDummyNode(t,"selfedge",{width:e.label.width,height:e.label.height,rank:a.rank,order:i+ ++n,e:e.e,label:e.label},"_se")})),delete a.selfEdges}))}))}(t)})),e(" adjustCoordinateSystem",(function(){f.adjust(t)})),e(" position",(function(){p(t)})),e(" positionSelfEdges",(function(){!function(t){r.forEach(t.nodes(),(function(e){var n=t.node(e);if("selfedge"===n.dummy){var r=t.node(n.e.v),i=r.x+r.width/2,a=r.y,o=n.x-i,s=r.height/2;t.setEdge(n.e,n.label),t.removeNode(e),n.label.points=[{x:i+2*o/3,y:a-s},{x:i+5*o/6,y:a-s},{x:i+o,y:a},{x:i+5*o/6,y:a+s},{x:i+2*o/3,y:a+s}],n.label.x=n.x,n.label.y=n.y}}))}(t)})),e(" removeBorderNodes",(function(){!function(t){r.forEach(t.nodes(),(function(e){if(t.children(e).length){var n=t.node(e),i=t.node(n.borderTop),a=t.node(n.borderBottom),o=t.node(r.last(n.borderLeft)),s=t.node(r.last(n.borderRight));n.width=Math.abs(s.x-o.x),n.height=Math.abs(a.y-i.y),n.x=o.x+n.width/2,n.y=i.y+n.height/2}})),r.forEach(t.nodes(),(function(e){"border"===t.node(e).dummy&&t.removeNode(e)}))}(t)})),e(" normalize.undo",(function(){a.undo(t)})),e(" fixupEdgeLabelCoords",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(r.has(n,"x"))switch("l"!==n.labelpos&&"r"!==n.labelpos||(n.width-=n.labeloffset),n.labelpos){case"l":n.x-=n.width/2+n.labeloffset;break;case"r":n.x+=n.width/2+n.labeloffset}}))}(t)})),e(" undoCoordinateSystem",(function(){f.undo(t)})),e(" translateGraph",(function(){!function(t){var e=Number.POSITIVE_INFINITY,n=0,i=Number.POSITIVE_INFINITY,a=0,o=t.graph(),s=o.marginx||0,c=o.marginy||0;function u(t){var r=t.x,o=t.y,s=t.width,c=t.height;e=Math.min(e,r-s/2),n=Math.max(n,r+s/2),i=Math.min(i,o-c/2),a=Math.max(a,o+c/2)}r.forEach(t.nodes(),(function(e){u(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.has(n,"x")&&u(n)})),e-=s,i-=c,r.forEach(t.nodes(),(function(n){var r=t.node(n);r.x-=e,r.y-=i})),r.forEach(t.edges(),(function(n){var a=t.edge(n);r.forEach(a.points,(function(t){t.x-=e,t.y-=i})),r.has(a,"x")&&(a.x-=e),r.has(a,"y")&&(a.y-=i)})),o.width=n-e+s,o.height=a-i+c}(t)})),e(" assignNodeIntersects",(function(){!function(t){r.forEach(t.edges(),(function(e){var n,r,i=t.edge(e),a=t.node(e.v),o=t.node(e.w);i.points?(n=i.points[0],r=i.points[i.points.length-1]):(i.points=[],n=o,r=a),i.points.unshift(y.intersectRect(a,n)),i.points.push(y.intersectRect(o,r))}))}(t)})),e(" reversePoints",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);n.reversed&&n.points.reverse()}))}(t)})),e(" acyclic.undo",(function(){i.undo(t)}))}(e,n)})),n(" updateInputGraph",(function(){!function(t,e){r.forEach(t.nodes(),(function(n){var r=t.node(n),i=e.node(n);r&&(r.x=i.x,r.y=i.y,e.children(n).length&&(r.width=i.width,r.height=i.height))})),r.forEach(t.edges(),(function(n){var i=t.edge(n),a=e.edge(n);i.points=a.points,r.has(a,"x")&&(i.x=a.x,i.y=a.y)})),t.graph().width=e.graph().width,t.graph().height=e.graph().height}(t,e)}))}))};var m=["nodesep","edgesep","ranksep","marginx","marginy"],v={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},b=["acyclicer","ranker","rankdir","align"],_=["width","height"],x={width:0,height:0},w=["minlen","weight","width","height","labeloffset"],k={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},T=["labelpos"];function E(t,e){return r.mapValues(r.pick(t,e),Number)}function C(t){var e={};return r.forEach(t,(function(t,n){e[n.toLowerCase()]=t})),e}},8436:(t,e,n)=>{var r;try{r={cloneDeep:n(361),constant:n(5703),defaults:n(1747),each:n(6073),filter:n(3105),find:n(3311),flatten:n(5564),forEach:n(4486),forIn:n(2620),has:n(8721),isUndefined:n(2353),last:n(928),map:n(5161),mapValues:n(6604),max:n(6162),merge:n(3857),min:n(3632),minBy:n(2762),now:n(7771),pick:n(9722),range:n(6026),reduce:n(4061),sortBy:n(9734),uniqueId:n(3955),values:n(2628),zipObject:n(7287)}}catch(t){}r||(r=window._),t.exports=r},2981:(t,e,n)=>{var r=n(8436),i=n(1138);function a(t,e,n,o,s,c,u){var l=t.children(u);if(l.length){var h=i.addBorderNode(t,"_bt"),f=i.addBorderNode(t,"_bb"),d=t.node(u);t.setParent(h,u),d.borderTop=h,t.setParent(f,u),d.borderBottom=f,r.forEach(l,(function(r){a(t,e,n,o,s,c,r);var i=t.node(r),l=i.borderTop?i.borderTop:r,d=i.borderBottom?i.borderBottom:r,p=i.borderTop?o:2*o,y=l!==d?1:s-c[u]+1;t.setEdge(h,l,{weight:p,minlen:y,nestingEdge:!0}),t.setEdge(d,f,{weight:p,minlen:y,nestingEdge:!0})})),t.parent(u)||t.setEdge(e,h,{weight:0,minlen:s+c[u]})}else u!==e&&t.setEdge(e,u,{weight:0,minlen:n})}t.exports={run:function(t){var e=i.addDummyNode(t,"root",{},"_root"),n=function(t){var e={};function n(i,a){var o=t.children(i);o&&o.length&&r.forEach(o,(function(t){n(t,a+1)})),e[i]=a}return r.forEach(t.children(),(function(t){n(t,1)})),e}(t),o=r.max(r.values(n))-1,s=2*o+1;t.graph().nestingRoot=e,r.forEach(t.edges(),(function(e){t.edge(e).minlen*=s}));var c=function(t){return r.reduce(t.edges(),(function(e,n){return e+t.edge(n).weight}),0)}(t)+1;r.forEach(t.children(),(function(r){a(t,e,s,c,o,n,r)})),t.graph().nodeRankFactor=s},cleanup:function(t){var e=t.graph();t.removeNode(e.nestingRoot),delete e.nestingRoot,r.forEach(t.edges(),(function(e){t.edge(e).nestingEdge&&t.removeEdge(e)}))}}},5995:(t,e,n)=>{"use strict";var r=n(8436),i=n(1138);t.exports={run:function(t){t.graph().dummyChains=[],r.forEach(t.edges(),(function(e){!function(t,e){var n,r,a,o=e.v,s=t.node(o).rank,c=e.w,u=t.node(c).rank,l=e.name,h=t.edge(e),f=h.labelRank;if(u!==s+1){for(t.removeEdge(e),a=0,++s;s<u;++a,++s)h.points=[],r={width:0,height:0,edgeLabel:h,edgeObj:e,rank:s},n=i.addDummyNode(t,"edge",r,"_d"),s===f&&(r.width=h.width,r.height=h.height,r.dummy="edge-label",r.labelpos=h.labelpos),t.setEdge(o,n,{weight:h.weight},l),0===a&&t.graph().dummyChains.push(n),o=n;t.setEdge(o,c,{weight:h.weight},l)}}(t,e)}))},undo:function(t){r.forEach(t.graph().dummyChains,(function(e){var n,r=t.node(e),i=r.edgeLabel;for(t.setEdge(r.edgeObj,i);r.dummy;)n=t.successors(e)[0],t.removeNode(e),i.points.push({x:r.x,y:r.y}),"edge-label"===r.dummy&&(i.x=r.x,i.y=r.y,i.width=r.width,i.height=r.height),e=n,r=t.node(e)}))}}},5093:(t,e,n)=>{var r=n(8436);t.exports=function(t,e,n){var i,a={};r.forEach(n,(function(n){for(var r,o,s=t.parent(n);s;){if((r=t.parent(s))?(o=a[r],a[r]=s):(o=i,i=s),o&&o!==s)return void e.setEdge(o,s);s=r}}))}},5439:(t,e,n)=>{var r=n(8436);t.exports=function(t,e){return r.map(e,(function(e){var n=t.inEdges(e);if(n.length){var i=r.reduce(n,(function(e,n){var r=t.edge(n),i=t.node(n.v);return{sum:e.sum+r.weight*i.order,weight:e.weight+r.weight}}),{sum:0,weight:0});return{v:e,barycenter:i.sum/i.weight,weight:i.weight}}return{v:e}}))}},3128:(t,e,n)=>{var r=n(8436),i=n(574).Graph;t.exports=function(t,e,n){var a=function(t){for(var e;t.hasNode(e=r.uniqueId("_root")););return e}(t),o=new i({compound:!0}).setGraph({root:a}).setDefaultNodeLabel((function(e){return t.node(e)}));return r.forEach(t.nodes(),(function(i){var s=t.node(i),c=t.parent(i);(s.rank===e||s.minRank<=e&&e<=s.maxRank)&&(o.setNode(i),o.setParent(i,c||a),r.forEach(t[n](i),(function(e){var n=e.v===i?e.w:e.v,a=o.edge(n,i),s=r.isUndefined(a)?0:a.weight;o.setEdge(n,i,{weight:t.edge(e).weight+s})})),r.has(s,"minRank")&&o.setNode(i,{borderLeft:s.borderLeft[e],borderRight:s.borderRight[e]}))})),o}},6630:(t,e,n)=>{"use strict";var r=n(8436);function i(t,e,n){for(var i=r.zipObject(n,r.map(n,(function(t,e){return e}))),a=r.flatten(r.map(e,(function(e){return r.sortBy(r.map(t.outEdges(e),(function(e){return{pos:i[e.w],weight:t.edge(e).weight}})),"pos")})),!0),o=1;o<n.length;)o<<=1;var s=2*o-1;o-=1;var c=r.map(new Array(s),(function(){return 0})),u=0;return r.forEach(a.forEach((function(t){var e=t.pos+o;c[e]+=t.weight;for(var n=0;e>0;)e%2&&(n+=c[e+1]),c[e=e-1>>1]+=t.weight;u+=t.weight*n}))),u}t.exports=function(t,e){for(var n=0,r=1;r<e.length;++r)n+=i(t,e[r-1],e[r]);return n}},3408:(t,e,n)=>{"use strict";var r=n(8436),i=n(2588),a=n(6630),o=n(1026),s=n(3128),c=n(5093),u=n(574).Graph,l=n(1138);function h(t,e,n){return r.map(e,(function(e){return s(t,e,n)}))}function f(t,e){var n=new u;r.forEach(t,(function(t){var i=t.graph().root,a=o(t,i,n,e);r.forEach(a.vs,(function(e,n){t.node(e).order=n})),c(t,n,a.vs)}))}function d(t,e){r.forEach(e,(function(e){r.forEach(e,(function(e,n){t.node(e).order=n}))}))}t.exports=function(t){var e=l.maxRank(t),n=h(t,r.range(1,e+1),"inEdges"),o=h(t,r.range(e-1,-1,-1),"outEdges"),s=i(t);d(t,s);for(var c,u=Number.POSITIVE_INFINITY,p=0,y=0;y<4;++p,++y){f(p%2?n:o,p%4>=2),s=l.buildLayerMatrix(t);var g=a(t,s);g<u&&(y=0,c=r.cloneDeep(s),u=g)}d(t,c)}},2588:(t,e,n)=>{"use strict";var r=n(8436);t.exports=function(t){var e={},n=r.filter(t.nodes(),(function(e){return!t.children(e).length})),i=r.max(r.map(n,(function(e){return t.node(e).rank}))),a=r.map(r.range(i+1),(function(){return[]})),o=r.sortBy(n,(function(e){return t.node(e).rank}));return r.forEach(o,(function n(i){if(!r.has(e,i)){e[i]=!0;var o=t.node(i);a[o.rank].push(i),r.forEach(t.successors(i),n)}})),a}},9567:(t,e,n)=>{"use strict";var r=n(8436);t.exports=function(t,e){var n={};return r.forEach(t,(function(t,e){var i=n[t.v]={indegree:0,in:[],out:[],vs:[t.v],i:e};r.isUndefined(t.barycenter)||(i.barycenter=t.barycenter,i.weight=t.weight)})),r.forEach(e.edges(),(function(t){var e=n[t.v],i=n[t.w];r.isUndefined(e)||r.isUndefined(i)||(i.indegree++,e.out.push(n[t.w]))})),function(t){var e=[];function n(t){return function(e){var n,i,a,o;e.merged||(r.isUndefined(e.barycenter)||r.isUndefined(t.barycenter)||e.barycenter>=t.barycenter)&&(i=e,a=0,o=0,(n=t).weight&&(a+=n.barycenter*n.weight,o+=n.weight),i.weight&&(a+=i.barycenter*i.weight,o+=i.weight),n.vs=i.vs.concat(n.vs),n.barycenter=a/o,n.weight=o,n.i=Math.min(i.i,n.i),i.merged=!0)}}function i(e){return function(n){n.in.push(e),0==--n.indegree&&t.push(n)}}for(;t.length;){var a=t.pop();e.push(a),r.forEach(a.in.reverse(),n(a)),r.forEach(a.out,i(a))}return r.map(r.filter(e,(function(t){return!t.merged})),(function(t){return r.pick(t,["vs","i","barycenter","weight"])}))}(r.filter(n,(function(t){return!t.indegree})))}},1026:(t,e,n)=>{var r=n(8436),i=n(5439),a=n(9567),o=n(7304);t.exports=function t(e,n,s,c){var u=e.children(n),l=e.node(n),h=l?l.borderLeft:void 0,f=l?l.borderRight:void 0,d={};h&&(u=r.filter(u,(function(t){return t!==h&&t!==f})));var p=i(e,u);r.forEach(p,(function(n){if(e.children(n.v).length){var i=t(e,n.v,s,c);d[n.v]=i,r.has(i,"barycenter")&&(a=n,o=i,r.isUndefined(a.barycenter)?(a.barycenter=o.barycenter,a.weight=o.weight):(a.barycenter=(a.barycenter*a.weight+o.barycenter*o.weight)/(a.weight+o.weight),a.weight+=o.weight))}var a,o}));var y=a(p,s);!function(t,e){r.forEach(t,(function(t){t.vs=r.flatten(t.vs.map((function(t){return e[t]?e[t].vs:t})),!0)}))}(y,d);var g=o(y,c);if(h&&(g.vs=r.flatten([h,g.vs,f],!0),e.predecessors(h).length)){var m=e.node(e.predecessors(h)[0]),v=e.node(e.predecessors(f)[0]);r.has(g,"barycenter")||(g.barycenter=0,g.weight=0),g.barycenter=(g.barycenter*g.weight+m.order+v.order)/(g.weight+2),g.weight+=2}return g}},7304:(t,e,n)=>{var r=n(8436),i=n(1138);function a(t,e,n){for(var i;e.length&&(i=r.last(e)).i<=n;)e.pop(),t.push(i.vs),n++;return n}t.exports=function(t,e){var n,o=i.partition(t,(function(t){return r.has(t,"barycenter")})),s=o.lhs,c=r.sortBy(o.rhs,(function(t){return-t.i})),u=[],l=0,h=0,f=0;s.sort((n=!!e,function(t,e){return t.barycenter<e.barycenter?-1:t.barycenter>e.barycenter?1:n?e.i-t.i:t.i-e.i})),f=a(u,c,f),r.forEach(s,(function(t){f+=t.vs.length,u.push(t.vs),l+=t.barycenter*t.weight,h+=t.weight,f=a(u,c,f)}));var d={vs:r.flatten(u,!0)};return h&&(d.barycenter=l/h,d.weight=h),d}},4219:(t,e,n)=>{var r=n(8436);t.exports=function(t){var e=function(t){var e={},n=0;return r.forEach(t.children(),(function i(a){var o=n;r.forEach(t.children(a),i),e[a]={low:o,lim:n++}})),e}(t);r.forEach(t.graph().dummyChains,(function(n){for(var r=t.node(n),i=r.edgeObj,a=function(t,e,n,r){var i,a,o=[],s=[],c=Math.min(e[n].low,e[r].low),u=Math.max(e[n].lim,e[r].lim);i=n;do{i=t.parent(i),o.push(i)}while(i&&(e[i].low>c||u>e[i].lim));for(a=i,i=r;(i=t.parent(i))!==a;)s.push(i);return{path:o.concat(s.reverse()),lca:a}}(t,e,i.v,i.w),o=a.path,s=a.lca,c=0,u=o[c],l=!0;n!==i.w;){if(r=t.node(n),l){for(;(u=o[c])!==s&&t.node(u).maxRank<r.rank;)c++;u===s&&(l=!1)}if(!l){for(;c<o.length-1&&t.node(u=o[c+1]).minRank<=r.rank;)c++;u=o[c]}t.setParent(n,u),n=t.successors(n)[0]}}))}},3573:(t,e,n)=>{"use strict";var r=n(8436),i=n(574).Graph,a=n(1138);function o(t,e){var n={};return r.reduce(e,(function(e,i){var a=0,o=0,s=e.length,u=r.last(i);return r.forEach(i,(function(e,l){var h=function(t,e){if(t.node(e).dummy)return r.find(t.predecessors(e),(function(e){return t.node(e).dummy}))}(t,e),f=h?t.node(h).order:s;(h||e===u)&&(r.forEach(i.slice(o,l+1),(function(e){r.forEach(t.predecessors(e),(function(r){var i=t.node(r),o=i.order;!(o<a||f<o)||i.dummy&&t.node(e).dummy||c(n,r,e)}))})),o=l+1,a=f)})),i})),n}function s(t,e){var n={};function i(e,i,a,o,s){var u;r.forEach(r.range(i,a),(function(i){u=e[i],t.node(u).dummy&&r.forEach(t.predecessors(u),(function(e){var r=t.node(e);r.dummy&&(r.order<o||r.order>s)&&c(n,e,u)}))}))}return r.reduce(e,(function(e,n){var a,o=-1,s=0;return r.forEach(n,(function(r,c){if("border"===t.node(r).dummy){var u=t.predecessors(r);u.length&&(a=t.node(u[0]).order,i(n,s,c,o,a),s=c,o=a)}i(n,s,n.length,a,e.length)})),n})),n}function c(t,e,n){if(e>n){var r=e;e=n,n=r}var i=t[e];i||(t[e]=i={}),i[n]=!0}function u(t,e,n){if(e>n){var i=e;e=n,n=i}return r.has(t[e],n)}function l(t,e,n,i){var a={},o={},s={};return r.forEach(e,(function(t){r.forEach(t,(function(t,e){a[t]=t,o[t]=t,s[t]=e}))})),r.forEach(e,(function(t){var e=-1;r.forEach(t,(function(t){var c=i(t);if(c.length){c=r.sortBy(c,(function(t){return s[t]}));for(var l=(c.length-1)/2,h=Math.floor(l),f=Math.ceil(l);h<=f;++h){var d=c[h];o[t]===t&&e<s[d]&&!u(n,t,d)&&(o[d]=t,o[t]=a[t]=a[d],e=s[d])}}}))})),{root:a,align:o}}function h(t,e,n,a,o){var s={},c=function(t,e,n,a){var o=new i,s=t.graph(),c=function(t,e,n){return function(i,a,o){var s,c=i.node(a),u=i.node(o),l=0;if(l+=c.width/2,r.has(c,"labelpos"))switch(c.labelpos.toLowerCase()){case"l":s=-c.width/2;break;case"r":s=c.width/2}if(s&&(l+=n?s:-s),s=0,l+=(c.dummy?e:t)/2,l+=(u.dummy?e:t)/2,l+=u.width/2,r.has(u,"labelpos"))switch(u.labelpos.toLowerCase()){case"l":s=u.width/2;break;case"r":s=-u.width/2}return s&&(l+=n?s:-s),s=0,l}}(s.nodesep,s.edgesep,a);return r.forEach(e,(function(e){var i;r.forEach(e,(function(e){var r=n[e];if(o.setNode(r),i){var a=n[i],s=o.edge(a,r);o.setEdge(a,r,Math.max(c(t,e,i),s||0))}i=e}))})),o}(t,e,n,o),u=o?"borderLeft":"borderRight";function l(t,e){for(var n=c.nodes(),r=n.pop(),i={};r;)i[r]?t(r):(i[r]=!0,n.push(r),n=n.concat(e(r))),r=n.pop()}return l((function(t){s[t]=c.inEdges(t).reduce((function(t,e){return Math.max(t,s[e.v]+c.edge(e))}),0)}),c.predecessors.bind(c)),l((function(e){var n=c.outEdges(e).reduce((function(t,e){return Math.min(t,s[e.w]-c.edge(e))}),Number.POSITIVE_INFINITY),r=t.node(e);n!==Number.POSITIVE_INFINITY&&r.borderType!==u&&(s[e]=Math.max(s[e],n))}),c.successors.bind(c)),r.forEach(a,(function(t){s[t]=s[n[t]]})),s}function f(t,e){return r.minBy(r.values(e),(function(e){var n=Number.NEGATIVE_INFINITY,i=Number.POSITIVE_INFINITY;return r.forIn(e,(function(e,r){var a=function(t,e){return t.node(e).width}(t,r)/2;n=Math.max(e+a,n),i=Math.min(e-a,i)})),n-i}))}function d(t,e){var n=r.values(e),i=r.min(n),a=r.max(n);r.forEach(["u","d"],(function(n){r.forEach(["l","r"],(function(o){var s,c=n+o,u=t[c];if(u!==e){var l=r.values(u);(s="l"===o?i-r.min(l):a-r.max(l))&&(t[c]=r.mapValues(u,(function(t){return t+s})))}}))}))}function p(t,e){return r.mapValues(t.ul,(function(n,i){if(e)return t[e.toLowerCase()][i];var a=r.sortBy(r.map(t,i));return(a[1]+a[2])/2}))}t.exports={positionX:function(t){var e,n=a.buildLayerMatrix(t),i=r.merge(o(t,n),s(t,n)),c={};r.forEach(["u","d"],(function(a){e="u"===a?n:r.values(n).reverse(),r.forEach(["l","r"],(function(n){"r"===n&&(e=r.map(e,(function(t){return r.values(t).reverse()})));var o=("u"===a?t.predecessors:t.successors).bind(t),s=l(0,e,i,o),u=h(t,e,s.root,s.align,"r"===n);"r"===n&&(u=r.mapValues(u,(function(t){return-t}))),c[a+n]=u}))}));var u=f(t,c);return d(c,u),p(c,t.graph().align)},findType1Conflicts:o,findType2Conflicts:s,addConflict:c,hasConflict:u,verticalAlignment:l,horizontalCompaction:h,alignCoordinates:d,findSmallestWidthAlignment:f,balance:p}},7873:(t,e,n)=>{"use strict";var r=n(8436),i=n(1138),a=n(3573).positionX;t.exports=function(t){(function(t){var e=i.buildLayerMatrix(t),n=t.graph().ranksep,a=0;r.forEach(e,(function(e){var i=r.max(r.map(e,(function(e){return t.node(e).height})));r.forEach(e,(function(e){t.node(e).y=a+i/2})),a+=i+n}))})(t=i.asNonCompoundGraph(t)),r.forEach(a(t),(function(e,n){t.node(n).x=e}))}},300:(t,e,n)=>{"use strict";var r=n(8436),i=n(574).Graph,a=n(6681).slack;function o(t,e){return r.forEach(t.nodes(),(function n(i){r.forEach(e.nodeEdges(i),(function(r){var o=r.v,s=i===o?r.w:o;t.hasNode(s)||a(e,r)||(t.setNode(s,{}),t.setEdge(i,s,{}),n(s))}))})),t.nodeCount()}function s(t,e){return r.minBy(e.edges(),(function(n){if(t.hasNode(n.v)!==t.hasNode(n.w))return a(e,n)}))}function c(t,e,n){r.forEach(t.nodes(),(function(t){e.node(t).rank+=n}))}t.exports=function(t){var e,n,r=new i({directed:!1}),u=t.nodes()[0],l=t.nodeCount();for(r.setNode(u,{});o(r,t)<l;)e=s(r,t),n=r.hasNode(e.v)?a(t,e):-a(t,e),c(r,t,n);return r}},8093:(t,e,n)=>{"use strict";var r=n(6681).longestPath,i=n(300),a=n(2472);t.exports=function(t){switch(t.graph().ranker){case"network-simplex":default:!function(t){a(t)}(t);break;case"tight-tree":!function(t){r(t),i(t)}(t);break;case"longest-path":o(t)}};var o=r},2472:(t,e,n)=>{"use strict";var r=n(8436),i=n(300),a=n(6681).slack,o=n(6681).longestPath,s=n(574).alg.preorder,c=n(574).alg.postorder,u=n(1138).simplify;function l(t){t=u(t),o(t);var e,n=i(t);for(d(n),h(n,t);e=y(n);)m(n,t,e,g(n,t,e))}function h(t,e){var n=c(t,t.nodes());n=n.slice(0,n.length-1),r.forEach(n,(function(n){!function(t,e,n){var r=t.node(n).parent;t.edge(n,r).cutvalue=f(t,e,n)}(t,e,n)}))}function f(t,e,n){var i=t.node(n).parent,a=!0,o=e.edge(n,i),s=0;return o||(a=!1,o=e.edge(i,n)),s=o.weight,r.forEach(e.nodeEdges(n),(function(r){var o,c,u=r.v===n,l=u?r.w:r.v;if(l!==i){var h=u===a,f=e.edge(r).weight;if(s+=h?f:-f,o=n,c=l,t.hasEdge(o,c)){var d=t.edge(n,l).cutvalue;s+=h?-d:d}}})),s}function d(t,e){arguments.length<2&&(e=t.nodes()[0]),p(t,{},1,e)}function p(t,e,n,i,a){var o=n,s=t.node(i);return e[i]=!0,r.forEach(t.neighbors(i),(function(a){r.has(e,a)||(n=p(t,e,n,a,i))})),s.low=o,s.lim=n++,a?s.parent=a:delete s.parent,n}function y(t){return r.find(t.edges(),(function(e){return t.edge(e).cutvalue<0}))}function g(t,e,n){var i=n.v,o=n.w;e.hasEdge(i,o)||(i=n.w,o=n.v);var s=t.node(i),c=t.node(o),u=s,l=!1;s.lim>c.lim&&(u=c,l=!0);var h=r.filter(e.edges(),(function(e){return l===v(0,t.node(e.v),u)&&l!==v(0,t.node(e.w),u)}));return r.minBy(h,(function(t){return a(e,t)}))}function m(t,e,n,i){var a=n.v,o=n.w;t.removeEdge(a,o),t.setEdge(i.v,i.w,{}),d(t),h(t,e),function(t,e){var n=r.find(t.nodes(),(function(t){return!e.node(t).parent})),i=s(t,n);i=i.slice(1),r.forEach(i,(function(n){var r=t.node(n).parent,i=e.edge(n,r),a=!1;i||(i=e.edge(r,n),a=!0),e.node(n).rank=e.node(r).rank+(a?i.minlen:-i.minlen)}))}(t,e)}function v(t,e,n){return n.low<=e.lim&&e.lim<=n.lim}t.exports=l,l.initLowLimValues=d,l.initCutValues=h,l.calcCutValue=f,l.leaveEdge=y,l.enterEdge=g,l.exchangeEdges=m},6681:(t,e,n)=>{"use strict";var r=n(8436);t.exports={longestPath:function(t){var e={};r.forEach(t.sources(),(function n(i){var a=t.node(i);if(r.has(e,i))return a.rank;e[i]=!0;var o=r.min(r.map(t.outEdges(i),(function(e){return n(e.w)-t.edge(e).minlen})));return o!==Number.POSITIVE_INFINITY&&null!=o||(o=0),a.rank=o}))},slack:function(t,e){return t.node(e.w).rank-t.node(e.v).rank-t.edge(e).minlen}}},1138:(t,e,n)=>{"use strict";var r=n(8436),i=n(574).Graph;function a(t,e,n,i){var a;do{a=r.uniqueId(i)}while(t.hasNode(a));return n.dummy=e,t.setNode(a,n),a}function o(t){return r.max(r.map(t.nodes(),(function(e){var n=t.node(e).rank;if(!r.isUndefined(n))return n})))}t.exports={addDummyNode:a,simplify:function(t){var e=(new i).setGraph(t.graph());return r.forEach(t.nodes(),(function(n){e.setNode(n,t.node(n))})),r.forEach(t.edges(),(function(n){var r=e.edge(n.v,n.w)||{weight:0,minlen:1},i=t.edge(n);e.setEdge(n.v,n.w,{weight:r.weight+i.weight,minlen:Math.max(r.minlen,i.minlen)})})),e},asNonCompoundGraph:function(t){var e=new i({multigraph:t.isMultigraph()}).setGraph(t.graph());return r.forEach(t.nodes(),(function(n){t.children(n).length||e.setNode(n,t.node(n))})),r.forEach(t.edges(),(function(n){e.setEdge(n,t.edge(n))})),e},successorWeights:function(t){var e=r.map(t.nodes(),(function(e){var n={};return r.forEach(t.outEdges(e),(function(e){n[e.w]=(n[e.w]||0)+t.edge(e).weight})),n}));return r.zipObject(t.nodes(),e)},predecessorWeights:function(t){var e=r.map(t.nodes(),(function(e){var n={};return r.forEach(t.inEdges(e),(function(e){n[e.v]=(n[e.v]||0)+t.edge(e).weight})),n}));return r.zipObject(t.nodes(),e)},intersectRect:function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;if(!o&&!s)throw new Error("Not possible to find intersection inside of the rectangle");return Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=u*o/s,r=u):(o<0&&(c=-c),n=c,r=c*s/o),{x:i+n,y:a+r}},buildLayerMatrix:function(t){var e=r.map(r.range(o(t)+1),(function(){return[]}));return r.forEach(t.nodes(),(function(n){var i=t.node(n),a=i.rank;r.isUndefined(a)||(e[a][i.order]=n)})),e},normalizeRanks:function(t){var e=r.min(r.map(t.nodes(),(function(e){return t.node(e).rank})));r.forEach(t.nodes(),(function(n){var i=t.node(n);r.has(i,"rank")&&(i.rank-=e)}))},removeEmptyRanks:function(t){var e=r.min(r.map(t.nodes(),(function(e){return t.node(e).rank}))),n=[];r.forEach(t.nodes(),(function(r){var i=t.node(r).rank-e;n[i]||(n[i]=[]),n[i].push(r)}));var i=0,a=t.graph().nodeRankFactor;r.forEach(n,(function(e,n){r.isUndefined(e)&&n%a!=0?--i:i&&r.forEach(e,(function(e){t.node(e).rank+=i}))}))},addBorderNode:function(t,e,n,r){var i={width:0,height:0};return arguments.length>=4&&(i.rank=n,i.order=r),a(t,"border",i,e)},maxRank:o,partition:function(t,e){var n={lhs:[],rhs:[]};return r.forEach(t,(function(t){e(t)?n.lhs.push(t):n.rhs.push(t)})),n},time:function(t,e){var n=r.now();try{return e()}finally{console.log(t+" time: "+(r.now()-n)+"ms")}},notime:function(t,e){return e()}}},8177:t=>{t.exports="0.8.5"},7856:function(t){t.exports=function(){"use strict";var t=Object.hasOwnProperty,e=Object.setPrototypeOf,n=Object.isFrozen,r=Object.getPrototypeOf,i=Object.getOwnPropertyDescriptor,a=Object.freeze,o=Object.seal,s=Object.create,c="undefined"!=typeof Reflect&&Reflect,u=c.apply,l=c.construct;u||(u=function(t,e,n){return t.apply(e,n)}),a||(a=function(t){return t}),o||(o=function(t){return t}),l||(l=function(t,e){return new(Function.prototype.bind.apply(t,[null].concat(function(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e<t.length;e++)n[e]=t[e];return n}return Array.from(t)}(e))))});var h,f=w(Array.prototype.forEach),d=w(Array.prototype.pop),p=w(Array.prototype.push),y=w(String.prototype.toLowerCase),g=w(String.prototype.match),m=w(String.prototype.replace),v=w(String.prototype.indexOf),b=w(String.prototype.trim),_=w(RegExp.prototype.test),x=(h=TypeError,function(){for(var t=arguments.length,e=Array(t),n=0;n<t;n++)e[n]=arguments[n];return l(h,e)});function w(t){return function(e){for(var n=arguments.length,r=Array(n>1?n-1:0),i=1;i<n;i++)r[i-1]=arguments[i];return u(t,e,r)}}function k(t,r){e&&e(t,null);for(var i=r.length;i--;){var a=r[i];if("string"==typeof a){var o=y(a);o!==a&&(n(r)||(r[i]=o),a=o)}t[a]=!0}return t}function T(e){var n=s(null),r=void 0;for(r in e)u(t,e,[r])&&(n[r]=e[r]);return n}function E(t,e){for(;null!==t;){var n=i(t,e);if(n){if(n.get)return w(n.get);if("function"==typeof n.value)return w(n.value)}t=r(t)}return function(t){return console.warn("fallback value for",t),null}}var C=a(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),S=a(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),A=a(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),M=a(["animate","color-profile","cursor","discard","fedropshadow","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),N=a(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover"]),D=a(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),O=a(["#text"]),B=a(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","controls","controlslist","coords","crossorigin","datetime","decoding","default","dir","disabled","disablepictureinpicture","disableremoteplayback","download","draggable","enctype","enterkeyhint","face","for","headers","height","hidden","high","href","hreflang","id","inputmode","integrity","ismap","kind","label","lang","list","loading","loop","low","max","maxlength","media","method","min","minlength","multiple","muted","name","nonce","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","playsinline","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","translate","type","usemap","valign","value","width","xmlns","slot"]),L=a(["accent-height","accumulate","additive","alignment-baseline","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clippathunits","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dur","edgemode","elevation","end","fill","fill-opacity","fill-rule","filter","filterunits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","preserveaspectratio","primitiveunits","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","specularconstant","specularexponent","spreadmethod","startoffset","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","systemlanguage","tabindex","targetx","targety","transform","text-anchor","text-decoration","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","version","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"]),I=a(["accent","accentunder","align","bevelled","close","columnsalign","columnlines","columnspan","denomalign","depth","dir","display","displaystyle","encoding","fence","frame","height","href","id","largeop","length","linethickness","lspace","lquote","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"]),R=a(["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"]),F=o(/\{\{[\s\S]*|[\s\S]*\}\}/gm),P=o(/<%[\s\S]*|[\s\S]*%>/gm),j=o(/^data-[\-\w.\u00B7-\uFFFF]/),Y=o(/^aria-[\-\w]+$/),z=o(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),U=o(/^(?:\w+script|data):/i),q=o(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),H="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};function $(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e<t.length;e++)n[e]=t[e];return n}return Array.from(t)}var W=function(){return"undefined"==typeof window?null:window},V=function(t,e){if("object"!==(void 0===t?"undefined":H(t))||"function"!=typeof t.createPolicy)return null;var n=null,r="data-tt-policy-suffix";e.currentScript&&e.currentScript.hasAttribute(r)&&(n=e.currentScript.getAttribute(r));var i="dompurify"+(n?"#"+n:"");try{return t.createPolicy(i,{createHTML:function(t){return t}})}catch(t){return console.warn("TrustedTypes policy "+i+" could not be created."),null}};return function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:W(),n=function(e){return t(e)};if(n.version="2.3.4",n.removed=[],!e||!e.document||9!==e.document.nodeType)return n.isSupported=!1,n;var r=e.document,i=e.document,o=e.DocumentFragment,s=e.HTMLTemplateElement,c=e.Node,u=e.Element,l=e.NodeFilter,h=e.NamedNodeMap,w=void 0===h?e.NamedNodeMap||e.MozNamedAttrMap:h,G=e.HTMLFormElement,X=e.DOMParser,Z=e.trustedTypes,Q=u.prototype,K=E(Q,"cloneNode"),J=E(Q,"nextSibling"),tt=E(Q,"childNodes"),et=E(Q,"parentNode");if("function"==typeof s){var nt=i.createElement("template");nt.content&&nt.content.ownerDocument&&(i=nt.content.ownerDocument)}var rt=V(Z,r),it=rt&&Rt?rt.createHTML(""):"",at=i,ot=at.implementation,st=at.createNodeIterator,ct=at.createDocumentFragment,ut=at.getElementsByTagName,lt=r.importNode,ht={};try{ht=T(i).documentMode?i.documentMode:{}}catch(t){}var ft={};n.isSupported="function"==typeof et&&ot&&void 0!==ot.createHTMLDocument&&9!==ht;var dt=F,pt=P,yt=j,gt=Y,mt=U,vt=q,bt=z,_t=null,xt=k({},[].concat($(C),$(S),$(A),$(N),$(O))),wt=null,kt=k({},[].concat($(B),$(L),$(I),$(R))),Tt=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Et=null,Ct=null,St=!0,At=!0,Mt=!1,Nt=!1,Dt=!1,Ot=!1,Bt=!1,Lt=!1,It=!1,Rt=!1,Ft=!0,Pt=!0,jt=!1,Yt={},zt=null,Ut=k({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),qt=null,Ht=k({},["audio","video","img","source","image","track"]),$t=null,Wt=k({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Vt="http://www.w3.org/1998/Math/MathML",Gt="http://www.w3.org/2000/svg",Xt="http://www.w3.org/1999/xhtml",Zt=Xt,Qt=!1,Kt=void 0,Jt=["application/xhtml+xml","text/html"],te="text/html",ee=void 0,ne=null,re=i.createElement("form"),ie=function(t){return t instanceof RegExp||t instanceof Function},ae=function(t){ne&&ne===t||(t&&"object"===(void 0===t?"undefined":H(t))||(t={}),t=T(t),_t="ALLOWED_TAGS"in t?k({},t.ALLOWED_TAGS):xt,wt="ALLOWED_ATTR"in t?k({},t.ALLOWED_ATTR):kt,$t="ADD_URI_SAFE_ATTR"in t?k(T(Wt),t.ADD_URI_SAFE_ATTR):Wt,qt="ADD_DATA_URI_TAGS"in t?k(T(Ht),t.ADD_DATA_URI_TAGS):Ht,zt="FORBID_CONTENTS"in t?k({},t.FORBID_CONTENTS):Ut,Et="FORBID_TAGS"in t?k({},t.FORBID_TAGS):{},Ct="FORBID_ATTR"in t?k({},t.FORBID_ATTR):{},Yt="USE_PROFILES"in t&&t.USE_PROFILES,St=!1!==t.ALLOW_ARIA_ATTR,At=!1!==t.ALLOW_DATA_ATTR,Mt=t.ALLOW_UNKNOWN_PROTOCOLS||!1,Nt=t.SAFE_FOR_TEMPLATES||!1,Dt=t.WHOLE_DOCUMENT||!1,Lt=t.RETURN_DOM||!1,It=t.RETURN_DOM_FRAGMENT||!1,Rt=t.RETURN_TRUSTED_TYPE||!1,Bt=t.FORCE_BODY||!1,Ft=!1!==t.SANITIZE_DOM,Pt=!1!==t.KEEP_CONTENT,jt=t.IN_PLACE||!1,bt=t.ALLOWED_URI_REGEXP||bt,Zt=t.NAMESPACE||Xt,t.CUSTOM_ELEMENT_HANDLING&&ie(t.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(Tt.tagNameCheck=t.CUSTOM_ELEMENT_HANDLING.tagNameCheck),t.CUSTOM_ELEMENT_HANDLING&&ie(t.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(Tt.attributeNameCheck=t.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),t.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof t.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(Tt.allowCustomizedBuiltInElements=t.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Kt=Kt=-1===Jt.indexOf(t.PARSER_MEDIA_TYPE)?te:t.PARSER_MEDIA_TYPE,ee="application/xhtml+xml"===Kt?function(t){return t}:y,Nt&&(At=!1),It&&(Lt=!0),Yt&&(_t=k({},[].concat($(O))),wt=[],!0===Yt.html&&(k(_t,C),k(wt,B)),!0===Yt.svg&&(k(_t,S),k(wt,L),k(wt,R)),!0===Yt.svgFilters&&(k(_t,A),k(wt,L),k(wt,R)),!0===Yt.mathMl&&(k(_t,N),k(wt,I),k(wt,R))),t.ADD_TAGS&&(_t===xt&&(_t=T(_t)),k(_t,t.ADD_TAGS)),t.ADD_ATTR&&(wt===kt&&(wt=T(wt)),k(wt,t.ADD_ATTR)),t.ADD_URI_SAFE_ATTR&&k($t,t.ADD_URI_SAFE_ATTR),t.FORBID_CONTENTS&&(zt===Ut&&(zt=T(zt)),k(zt,t.FORBID_CONTENTS)),Pt&&(_t["#text"]=!0),Dt&&k(_t,["html","head","body"]),_t.table&&(k(_t,["tbody"]),delete Et.tbody),a&&a(t),ne=t)},oe=k({},["mi","mo","mn","ms","mtext"]),se=k({},["foreignobject","desc","title","annotation-xml"]),ce=k({},S);k(ce,A),k(ce,M);var ue=k({},N);k(ue,D);var le=function(t){var e=et(t);e&&e.tagName||(e={namespaceURI:Xt,tagName:"template"});var n=y(t.tagName),r=y(e.tagName);if(t.namespaceURI===Gt)return e.namespaceURI===Xt?"svg"===n:e.namespaceURI===Vt?"svg"===n&&("annotation-xml"===r||oe[r]):Boolean(ce[n]);if(t.namespaceURI===Vt)return e.namespaceURI===Xt?"math"===n:e.namespaceURI===Gt?"math"===n&&se[r]:Boolean(ue[n]);if(t.namespaceURI===Xt){if(e.namespaceURI===Gt&&!se[r])return!1;if(e.namespaceURI===Vt&&!oe[r])return!1;var i=k({},["title","style","font","a","script"]);return!ue[n]&&(i[n]||!ce[n])}return!1},he=function(t){p(n.removed,{element:t});try{t.parentNode.removeChild(t)}catch(e){try{t.outerHTML=it}catch(e){t.remove()}}},fe=function(t,e){try{p(n.removed,{attribute:e.getAttributeNode(t),from:e})}catch(t){p(n.removed,{attribute:null,from:e})}if(e.removeAttribute(t),"is"===t&&!wt[t])if(Lt||It)try{he(e)}catch(t){}else try{e.setAttribute(t,"")}catch(t){}},de=function(t){var e=void 0,n=void 0;if(Bt)t="<remove></remove>"+t;else{var r=g(t,/^[\r\n\t ]+/);n=r&&r[0]}"application/xhtml+xml"===Kt&&(t='<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>'+t+"</body></html>");var a=rt?rt.createHTML(t):t;if(Zt===Xt)try{e=(new X).parseFromString(a,Kt)}catch(t){}if(!e||!e.documentElement){e=ot.createDocument(Zt,"template",null);try{e.documentElement.innerHTML=Qt?"":a}catch(t){}}var o=e.body||e.documentElement;return t&&n&&o.insertBefore(i.createTextNode(n),o.childNodes[0]||null),Zt===Xt?ut.call(e,Dt?"html":"body")[0]:Dt?e.documentElement:o},pe=function(t){return st.call(t.ownerDocument||t,t,l.SHOW_ELEMENT|l.SHOW_COMMENT|l.SHOW_TEXT,null,!1)},ye=function(t){return t instanceof G&&("string"!=typeof t.nodeName||"string"!=typeof t.textContent||"function"!=typeof t.removeChild||!(t.attributes instanceof w)||"function"!=typeof t.removeAttribute||"function"!=typeof t.setAttribute||"string"!=typeof t.namespaceURI||"function"!=typeof t.insertBefore)},ge=function(t){return"object"===(void 0===c?"undefined":H(c))?t instanceof c:t&&"object"===(void 0===t?"undefined":H(t))&&"number"==typeof t.nodeType&&"string"==typeof t.nodeName},me=function(t,e,r){ft[t]&&f(ft[t],(function(t){t.call(n,e,r,ne)}))},ve=function(t){var e=void 0;if(me("beforeSanitizeElements",t,null),ye(t))return he(t),!0;if(g(t.nodeName,/[\u0080-\uFFFF]/))return he(t),!0;var r=ee(t.nodeName);if(me("uponSanitizeElement",t,{tagName:r,allowedTags:_t}),!ge(t.firstElementChild)&&(!ge(t.content)||!ge(t.content.firstElementChild))&&_(/<[/\w]/g,t.innerHTML)&&_(/<[/\w]/g,t.textContent))return he(t),!0;if("select"===r&&_(/<template/i,t.innerHTML))return he(t),!0;if(!_t[r]||Et[r]){if(Pt&&!zt[r]){var i=et(t)||t.parentNode,a=tt(t)||t.childNodes;if(a&&i)for(var o=a.length-1;o>=0;--o)i.insertBefore(K(a[o],!0),J(t))}if(!Et[r]&&_e(r)){if(Tt.tagNameCheck instanceof RegExp&&_(Tt.tagNameCheck,r))return!1;if(Tt.tagNameCheck instanceof Function&&Tt.tagNameCheck(r))return!1}return he(t),!0}return t instanceof u&&!le(t)?(he(t),!0):"noscript"!==r&&"noembed"!==r||!_(/<\/no(script|embed)/i,t.innerHTML)?(Nt&&3===t.nodeType&&(e=t.textContent,e=m(e,dt," "),e=m(e,pt," "),t.textContent!==e&&(p(n.removed,{element:t.cloneNode()}),t.textContent=e)),me("afterSanitizeElements",t,null),!1):(he(t),!0)},be=function(t,e,n){if(Ft&&("id"===e||"name"===e)&&(n in i||n in re))return!1;if(At&&!Ct[e]&&_(yt,e));else if(St&&_(gt,e));else if(!wt[e]||Ct[e]){if(!(_e(t)&&(Tt.tagNameCheck instanceof RegExp&&_(Tt.tagNameCheck,t)||Tt.tagNameCheck instanceof Function&&Tt.tagNameCheck(t))&&(Tt.attributeNameCheck instanceof RegExp&&_(Tt.attributeNameCheck,e)||Tt.attributeNameCheck instanceof Function&&Tt.attributeNameCheck(e))||"is"===e&&Tt.allowCustomizedBuiltInElements&&(Tt.tagNameCheck instanceof RegExp&&_(Tt.tagNameCheck,n)||Tt.tagNameCheck instanceof Function&&Tt.tagNameCheck(n))))return!1}else if($t[e]);else if(_(bt,m(n,vt,"")));else if("src"!==e&&"xlink:href"!==e&&"href"!==e||"script"===t||0!==v(n,"data:")||!qt[t])if(Mt&&!_(mt,m(n,vt,"")));else if(n)return!1;return!0},_e=function(t){return t.indexOf("-")>0},xe=function(t){var e=void 0,r=void 0,i=void 0,a=void 0;me("beforeSanitizeAttributes",t,null);var o=t.attributes;if(o){var s={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:wt};for(a=o.length;a--;){var c=e=o[a],u=c.name,l=c.namespaceURI;if(r=b(e.value),i=ee(u),s.attrName=i,s.attrValue=r,s.keepAttr=!0,s.forceKeepAttr=void 0,me("uponSanitizeAttribute",t,s),r=s.attrValue,!s.forceKeepAttr&&(fe(u,t),s.keepAttr))if(_(/\/>/i,r))fe(u,t);else{Nt&&(r=m(r,dt," "),r=m(r,pt," "));var h=ee(t.nodeName);if(be(h,i,r))try{l?t.setAttributeNS(l,u,r):t.setAttribute(u,r),d(n.removed)}catch(t){}}}me("afterSanitizeAttributes",t,null)}},we=function t(e){var n=void 0,r=pe(e);for(me("beforeSanitizeShadowDOM",e,null);n=r.nextNode();)me("uponSanitizeShadowNode",n,null),ve(n)||(n.content instanceof o&&t(n.content),xe(n));me("afterSanitizeShadowDOM",e,null)};return n.sanitize=function(t,i){var a=void 0,s=void 0,u=void 0,l=void 0,h=void 0;if((Qt=!t)&&(t="\x3c!--\x3e"),"string"!=typeof t&&!ge(t)){if("function"!=typeof t.toString)throw x("toString is not a function");if("string"!=typeof(t=t.toString()))throw x("dirty is not a string, aborting")}if(!n.isSupported){if("object"===H(e.toStaticHTML)||"function"==typeof e.toStaticHTML){if("string"==typeof t)return e.toStaticHTML(t);if(ge(t))return e.toStaticHTML(t.outerHTML)}return t}if(Ot||ae(i),n.removed=[],"string"==typeof t&&(jt=!1),jt);else if(t instanceof c)1===(s=(a=de("\x3c!----\x3e")).ownerDocument.importNode(t,!0)).nodeType&&"BODY"===s.nodeName||"HTML"===s.nodeName?a=s:a.appendChild(s);else{if(!Lt&&!Nt&&!Dt&&-1===t.indexOf("<"))return rt&&Rt?rt.createHTML(t):t;if(!(a=de(t)))return Lt?null:it}a&&Bt&&he(a.firstChild);for(var f=pe(jt?t:a);u=f.nextNode();)3===u.nodeType&&u===l||ve(u)||(u.content instanceof o&&we(u.content),xe(u),l=u);if(l=null,jt)return t;if(Lt){if(It)for(h=ct.call(a.ownerDocument);a.firstChild;)h.appendChild(a.firstChild);else h=a;return wt.shadowroot&&(h=lt.call(r,h,!0)),h}var d=Dt?a.outerHTML:a.innerHTML;return Nt&&(d=m(d,dt," "),d=m(d,pt," ")),rt&&Rt?rt.createHTML(d):d},n.setConfig=function(t){ae(t),Ot=!0},n.clearConfig=function(){ne=null,Ot=!1},n.isValidAttribute=function(t,e,n){ne||ae({});var r=ee(t),i=ee(e);return be(r,i,n)},n.addHook=function(t,e){"function"==typeof e&&(ft[t]=ft[t]||[],p(ft[t],e))},n.removeHook=function(t){ft[t]&&d(ft[t])},n.removeHooks=function(t){ft[t]&&(ft[t]=[])},n.removeAllHooks=function(){ft={}},n}()}()},8282:(t,e,n)=>{var r=n(2354);t.exports={Graph:r.Graph,json:n(8974),alg:n(2440),version:r.version}},2842:(t,e,n)=>{var r=n(9126);t.exports=function(t){var e,n={},i=[];function a(i){r.has(n,i)||(n[i]=!0,e.push(i),r.each(t.successors(i),a),r.each(t.predecessors(i),a))}return r.each(t.nodes(),(function(t){e=[],a(t),e.length&&i.push(e)})),i}},3984:(t,e,n)=>{var r=n(9126);function i(t,e,n,a,o,s){r.has(a,e)||(a[e]=!0,n||s.push(e),r.each(o(e),(function(e){i(t,e,n,a,o,s)})),n&&s.push(e))}t.exports=function(t,e,n){r.isArray(e)||(e=[e]);var a=(t.isDirected()?t.successors:t.neighbors).bind(t),o=[],s={};return r.each(e,(function(e){if(!t.hasNode(e))throw new Error("Graph does not have node: "+e);i(t,e,"post"===n,s,a,o)})),o}},4847:(t,e,n)=>{var r=n(3763),i=n(9126);t.exports=function(t,e,n){return i.transform(t.nodes(),(function(i,a){i[a]=r(t,a,e,n)}),{})}},3763:(t,e,n)=>{var r=n(9126),i=n(9675);t.exports=function(t,e,n,r){return function(t,e,n,r){var a,o,s={},c=new i,u=function(t){var e=t.v!==a?t.v:t.w,r=s[e],i=n(t),u=o.distance+i;if(i<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+t+" Weight: "+i);u<r.distance&&(r.distance=u,r.predecessor=a,c.decrease(e,u))};for(t.nodes().forEach((function(t){var n=t===e?0:Number.POSITIVE_INFINITY;s[t]={distance:n},c.add(t,n)}));c.size()>0&&(a=c.removeMin(),(o=s[a]).distance!==Number.POSITIVE_INFINITY);)r(a).forEach(u);return s}(t,String(e),n||a,r||function(e){return t.outEdges(e)})};var a=r.constant(1)},9096:(t,e,n)=>{var r=n(9126),i=n(5023);t.exports=function(t){return r.filter(i(t),(function(e){return e.length>1||1===e.length&&t.hasEdge(e[0],e[0])}))}},8924:(t,e,n)=>{var r=n(9126);t.exports=function(t,e,n){return function(t,e,n){var r={},i=t.nodes();return i.forEach((function(t){r[t]={},r[t][t]={distance:0},i.forEach((function(e){t!==e&&(r[t][e]={distance:Number.POSITIVE_INFINITY})})),n(t).forEach((function(n){var i=n.v===t?n.w:n.v,a=e(n);r[t][i]={distance:a,predecessor:t}}))})),i.forEach((function(t){var e=r[t];i.forEach((function(n){var a=r[n];i.forEach((function(n){var r=a[t],i=e[n],o=a[n],s=r.distance+i.distance;s<o.distance&&(o.distance=s,o.predecessor=i.predecessor)}))}))})),r}(t,e||i,n||function(e){return t.outEdges(e)})};var i=r.constant(1)},2440:(t,e,n)=>{t.exports={components:n(2842),dijkstra:n(3763),dijkstraAll:n(4847),findCycles:n(9096),floydWarshall:n(8924),isAcyclic:n(2707),postorder:n(8828),preorder:n(2648),prim:n(514),tarjan:n(5023),topsort:n(2166)}},2707:(t,e,n)=>{var r=n(2166);t.exports=function(t){try{r(t)}catch(t){if(t instanceof r.CycleException)return!1;throw t}return!0}},8828:(t,e,n)=>{var r=n(3984);t.exports=function(t,e){return r(t,e,"post")}},2648:(t,e,n)=>{var r=n(3984);t.exports=function(t,e){return r(t,e,"pre")}},514:(t,e,n)=>{var r=n(9126),i=n(771),a=n(9675);t.exports=function(t,e){var n,o=new i,s={},c=new a;function u(t){var r=t.v===n?t.w:t.v,i=c.priority(r);if(void 0!==i){var a=e(t);a<i&&(s[r]=n,c.decrease(r,a))}}if(0===t.nodeCount())return o;r.each(t.nodes(),(function(t){c.add(t,Number.POSITIVE_INFINITY),o.setNode(t)})),c.decrease(t.nodes()[0],0);for(var l=!1;c.size()>0;){if(n=c.removeMin(),r.has(s,n))o.setEdge(n,s[n]);else{if(l)throw new Error("Input graph is not connected: "+t);l=!0}t.nodeEdges(n).forEach(u)}return o}},5023:(t,e,n)=>{var r=n(9126);t.exports=function(t){var e=0,n=[],i={},a=[];function o(s){var c=i[s]={onStack:!0,lowlink:e,index:e++};if(n.push(s),t.successors(s).forEach((function(t){r.has(i,t)?i[t].onStack&&(c.lowlink=Math.min(c.lowlink,i[t].index)):(o(t),c.lowlink=Math.min(c.lowlink,i[t].lowlink))})),c.lowlink===c.index){var u,l=[];do{u=n.pop(),i[u].onStack=!1,l.push(u)}while(s!==u);a.push(l)}}return t.nodes().forEach((function(t){r.has(i,t)||o(t)})),a}},2166:(t,e,n)=>{var r=n(9126);function i(t){var e={},n={},i=[];if(r.each(t.sinks(),(function o(s){if(r.has(n,s))throw new a;r.has(e,s)||(n[s]=!0,e[s]=!0,r.each(t.predecessors(s),o),delete n[s],i.push(s))})),r.size(e)!==t.nodeCount())throw new a;return i}function a(){}t.exports=i,i.CycleException=a,a.prototype=new Error},9675:(t,e,n)=>{var r=n(9126);function i(){this._arr=[],this._keyIndices={}}t.exports=i,i.prototype.size=function(){return this._arr.length},i.prototype.keys=function(){return this._arr.map((function(t){return t.key}))},i.prototype.has=function(t){return r.has(this._keyIndices,t)},i.prototype.priority=function(t){var e=this._keyIndices[t];if(void 0!==e)return this._arr[e].priority},i.prototype.min=function(){if(0===this.size())throw new Error("Queue underflow");return this._arr[0].key},i.prototype.add=function(t,e){var n=this._keyIndices;if(t=String(t),!r.has(n,t)){var i=this._arr,a=i.length;return n[t]=a,i.push({key:t,priority:e}),this._decrease(a),!0}return!1},i.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key},i.prototype.decrease=function(t,e){var n=this._keyIndices[t];if(e>this._arr[n].priority)throw new Error("New priority is greater than current priority. Key: "+t+" Old: "+this._arr[n].priority+" New: "+e);this._arr[n].priority=e,this._decrease(n)},i.prototype._heapify=function(t){var e=this._arr,n=2*t,r=n+1,i=t;n<e.length&&(i=e[n].priority<e[i].priority?n:i,r<e.length&&(i=e[r].priority<e[i].priority?r:i),i!==t&&(this._swap(t,i),this._heapify(i)))},i.prototype._decrease=function(t){for(var e,n=this._arr,r=n[t].priority;0!==t&&!(n[e=t>>1].priority<r);)this._swap(t,e),t=e},i.prototype._swap=function(t,e){var n=this._arr,r=this._keyIndices,i=n[t],a=n[e];n[t]=a,n[e]=i,r[a.key]=t,r[i.key]=e}},771:(t,e,n)=>{"use strict";var r=n(9126);t.exports=a;var i="\0";function a(t){this._isDirected=!r.has(t,"directed")||t.directed,this._isMultigraph=!!r.has(t,"multigraph")&&t.multigraph,this._isCompound=!!r.has(t,"compound")&&t.compound,this._label=void 0,this._defaultNodeLabelFn=r.constant(void 0),this._defaultEdgeLabelFn=r.constant(void 0),this._nodes={},this._isCompound&&(this._parent={},this._children={},this._children["\0"]={}),this._in={},this._preds={},this._out={},this._sucs={},this._edgeObjs={},this._edgeLabels={}}function o(t,e){t[e]?t[e]++:t[e]=1}function s(t,e){--t[e]||delete t[e]}function c(t,e,n,i){var a=""+e,o=""+n;if(!t&&a>o){var s=a;a=o,o=s}return a+""+o+""+(r.isUndefined(i)?"\0":i)}function u(t,e,n,r){var i=""+e,a=""+n;if(!t&&i>a){var o=i;i=a,a=o}var s={v:i,w:a};return r&&(s.name=r),s}function l(t,e){return c(t,e.v,e.w,e.name)}a.prototype._nodeCount=0,a.prototype._edgeCount=0,a.prototype.isDirected=function(){return this._isDirected},a.prototype.isMultigraph=function(){return this._isMultigraph},a.prototype.isCompound=function(){return this._isCompound},a.prototype.setGraph=function(t){return this._label=t,this},a.prototype.graph=function(){return this._label},a.prototype.setDefaultNodeLabel=function(t){return r.isFunction(t)||(t=r.constant(t)),this._defaultNodeLabelFn=t,this},a.prototype.nodeCount=function(){return this._nodeCount},a.prototype.nodes=function(){return r.keys(this._nodes)},a.prototype.sources=function(){var t=this;return r.filter(this.nodes(),(function(e){return r.isEmpty(t._in[e])}))},a.prototype.sinks=function(){var t=this;return r.filter(this.nodes(),(function(e){return r.isEmpty(t._out[e])}))},a.prototype.setNodes=function(t,e){var n=arguments,i=this;return r.each(t,(function(t){n.length>1?i.setNode(t,e):i.setNode(t)})),this},a.prototype.setNode=function(t,e){return r.has(this._nodes,t)?(arguments.length>1&&(this._nodes[t]=e),this):(this._nodes[t]=arguments.length>1?e:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]=i,this._children[t]={},this._children["\0"][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)},a.prototype.node=function(t){return this._nodes[t]},a.prototype.hasNode=function(t){return r.has(this._nodes,t)},a.prototype.removeNode=function(t){var e=this;if(r.has(this._nodes,t)){var n=function(t){e.removeEdge(e._edgeObjs[t])};delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],r.each(this.children(t),(function(t){e.setParent(t)})),delete this._children[t]),r.each(r.keys(this._in[t]),n),delete this._in[t],delete this._preds[t],r.each(r.keys(this._out[t]),n),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this},a.prototype.setParent=function(t,e){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(r.isUndefined(e))e=i;else{for(var n=e+="";!r.isUndefined(n);n=this.parent(n))if(n===t)throw new Error("Setting "+e+" as parent of "+t+" would create a cycle");this.setNode(e)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=e,this._children[e][t]=!0,this},a.prototype._removeFromParentsChildList=function(t){delete this._children[this._parent[t]][t]},a.prototype.parent=function(t){if(this._isCompound){var e=this._parent[t];if(e!==i)return e}},a.prototype.children=function(t){if(r.isUndefined(t)&&(t=i),this._isCompound){var e=this._children[t];if(e)return r.keys(e)}else{if(t===i)return this.nodes();if(this.hasNode(t))return[]}},a.prototype.predecessors=function(t){var e=this._preds[t];if(e)return r.keys(e)},a.prototype.successors=function(t){var e=this._sucs[t];if(e)return r.keys(e)},a.prototype.neighbors=function(t){var e=this.predecessors(t);if(e)return r.union(e,this.successors(t))},a.prototype.isLeaf=function(t){return 0===(this.isDirected()?this.successors(t):this.neighbors(t)).length},a.prototype.filterNodes=function(t){var e=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});e.setGraph(this.graph());var n=this;r.each(this._nodes,(function(n,r){t(r)&&e.setNode(r,n)})),r.each(this._edgeObjs,(function(t){e.hasNode(t.v)&&e.hasNode(t.w)&&e.setEdge(t,n.edge(t))}));var i={};function a(t){var r=n.parent(t);return void 0===r||e.hasNode(r)?(i[t]=r,r):r in i?i[r]:a(r)}return this._isCompound&&r.each(e.nodes(),(function(t){e.setParent(t,a(t))})),e},a.prototype.setDefaultEdgeLabel=function(t){return r.isFunction(t)||(t=r.constant(t)),this._defaultEdgeLabelFn=t,this},a.prototype.edgeCount=function(){return this._edgeCount},a.prototype.edges=function(){return r.values(this._edgeObjs)},a.prototype.setPath=function(t,e){var n=this,i=arguments;return r.reduce(t,(function(t,r){return i.length>1?n.setEdge(t,r,e):n.setEdge(t,r),r})),this},a.prototype.setEdge=function(){var t,e,n,i,a=!1,s=arguments[0];"object"==typeof s&&null!==s&&"v"in s?(t=s.v,e=s.w,n=s.name,2===arguments.length&&(i=arguments[1],a=!0)):(t=s,e=arguments[1],n=arguments[3],arguments.length>2&&(i=arguments[2],a=!0)),t=""+t,e=""+e,r.isUndefined(n)||(n=""+n);var l=c(this._isDirected,t,e,n);if(r.has(this._edgeLabels,l))return a&&(this._edgeLabels[l]=i),this;if(!r.isUndefined(n)&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(t),this.setNode(e),this._edgeLabels[l]=a?i:this._defaultEdgeLabelFn(t,e,n);var h=u(this._isDirected,t,e,n);return t=h.v,e=h.w,Object.freeze(h),this._edgeObjs[l]=h,o(this._preds[e],t),o(this._sucs[t],e),this._in[e][l]=h,this._out[t][l]=h,this._edgeCount++,this},a.prototype.edge=function(t,e,n){var r=1===arguments.length?l(this._isDirected,arguments[0]):c(this._isDirected,t,e,n);return this._edgeLabels[r]},a.prototype.hasEdge=function(t,e,n){var i=1===arguments.length?l(this._isDirected,arguments[0]):c(this._isDirected,t,e,n);return r.has(this._edgeLabels,i)},a.prototype.removeEdge=function(t,e,n){var r=1===arguments.length?l(this._isDirected,arguments[0]):c(this._isDirected,t,e,n),i=this._edgeObjs[r];return i&&(t=i.v,e=i.w,delete this._edgeLabels[r],delete this._edgeObjs[r],s(this._preds[e],t),s(this._sucs[t],e),delete this._in[e][r],delete this._out[t][r],this._edgeCount--),this},a.prototype.inEdges=function(t,e){var n=this._in[t];if(n){var i=r.values(n);return e?r.filter(i,(function(t){return t.v===e})):i}},a.prototype.outEdges=function(t,e){var n=this._out[t];if(n){var i=r.values(n);return e?r.filter(i,(function(t){return t.w===e})):i}},a.prototype.nodeEdges=function(t,e){var n=this.inEdges(t,e);if(n)return n.concat(this.outEdges(t,e))}},2354:(t,e,n)=>{t.exports={Graph:n(771),version:n(9631)}},8974:(t,e,n)=>{var r=n(9126),i=n(771);function a(t){return r.map(t.nodes(),(function(e){var n=t.node(e),i=t.parent(e),a={v:e};return r.isUndefined(n)||(a.value=n),r.isUndefined(i)||(a.parent=i),a}))}function o(t){return r.map(t.edges(),(function(e){var n=t.edge(e),i={v:e.v,w:e.w};return r.isUndefined(e.name)||(i.name=e.name),r.isUndefined(n)||(i.value=n),i}))}t.exports={write:function(t){var e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:a(t),edges:o(t)};return r.isUndefined(t.graph())||(e.value=r.clone(t.graph())),e},read:function(t){var e=new i(t.options).setGraph(t.value);return r.each(t.nodes,(function(t){e.setNode(t.v,t.value),t.parent&&e.setParent(t.v,t.parent)})),r.each(t.edges,(function(t){e.setEdge({v:t.v,w:t.w,name:t.name},t.value)})),e}}},9126:(t,e,n)=>{var r;try{r={clone:n(6678),constant:n(5703),each:n(6073),filter:n(3105),has:n(8721),isArray:n(1469),isEmpty:n(1609),isFunction:n(3560),isUndefined:n(2353),keys:n(3674),map:n(5161),reduce:n(4061),size:n(4238),transform:n(8718),union:n(3386),values:n(2628)}}catch(t){}r||(r=window._),t.exports=r},9631:t=>{t.exports="2.1.8"},1773:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(7628),a=function(){function t(t,e){this.color=e,this.changed=!1,this.data=t,this.type=new i.default}return t.prototype.set=function(t,e){return this.color=e,this.changed=!1,this.data=t,this.type.type=0,this},t.prototype._ensureHSL=function(){var t=this.data,e=t.h,n=t.s,i=t.l;void 0===e&&(t.h=r.default.channel.rgb2hsl(t,"h")),void 0===n&&(t.s=r.default.channel.rgb2hsl(t,"s")),void 0===i&&(t.l=r.default.channel.rgb2hsl(t,"l"))},t.prototype._ensureRGB=function(){var t=this.data,e=t.r,n=t.g,i=t.b;void 0===e&&(t.r=r.default.channel.hsl2rgb(t,"r")),void 0===n&&(t.g=r.default.channel.hsl2rgb(t,"g")),void 0===i&&(t.b=r.default.channel.hsl2rgb(t,"b"))},Object.defineProperty(t.prototype,"r",{get:function(){var t=this.data,e=t.r;return this.type.is(2)||void 0===e?(this._ensureHSL(),r.default.channel.hsl2rgb(t,"r")):e},set:function(t){this.type.set(1),this.changed=!0,this.data.r=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"g",{get:function(){var t=this.data,e=t.g;return this.type.is(2)||void 0===e?(this._ensureHSL(),r.default.channel.hsl2rgb(t,"g")):e},set:function(t){this.type.set(1),this.changed=!0,this.data.g=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"b",{get:function(){var t=this.data,e=t.b;return this.type.is(2)||void 0===e?(this._ensureHSL(),r.default.channel.hsl2rgb(t,"b")):e},set:function(t){this.type.set(1),this.changed=!0,this.data.b=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"h",{get:function(){var t=this.data,e=t.h;return this.type.is(1)||void 0===e?(this._ensureRGB(),r.default.channel.rgb2hsl(t,"h")):e},set:function(t){this.type.set(2),this.changed=!0,this.data.h=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"s",{get:function(){var t=this.data,e=t.s;return this.type.is(1)||void 0===e?(this._ensureRGB(),r.default.channel.rgb2hsl(t,"s")):e},set:function(t){this.type.set(2),this.changed=!0,this.data.s=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"l",{get:function(){var t=this.data,e=t.l;return this.type.is(1)||void 0===e?(this._ensureRGB(),r.default.channel.rgb2hsl(t,"l")):e},set:function(t){this.type.set(2),this.changed=!0,this.data.l=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"a",{get:function(){return this.data.a},set:function(t){this.changed=!0,this.data.a=t},enumerable:!0,configurable:!0}),t}();e.default=a},8167:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=new(n(1773).default)({r:0,g:0,b:0,a:0},"transparent");e.default=r},7628:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(){this.type=0}return t.prototype.get=function(){return this.type},t.prototype.set=function(t){if(this.type&&this.type!==t)throw new Error("Cannot change both RGB and HSL channels at the same time");this.type=t},t.prototype.reset=function(){this.type=0},t.prototype.is=function(t){return this.type===t},t}();e.default=n},1655:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(8167),i=n(6061),a={re:/^#((?:[a-f0-9]{2}){2,4}|[a-f0-9]{3})$/i,parse:function(t){if(35===t.charCodeAt(0)){var e=t.match(a.re);if(e){var n=e[1],i=parseInt(n,16),o=n.length,s=o%4==0,c=o>4,u=c?1:17,l=c?8:4,h=s?0:-1,f=c?255:15;return r.default.set({r:(i>>l*(h+3)&f)*u,g:(i>>l*(h+2)&f)*u,b:(i>>l*(h+1)&f)*u,a:s?(i&f)*u/255:1},t)}}},stringify:function(t){var e=t.r,n=t.g,r=t.b,a=t.a;return a<1?"#"+i.DEC2HEX[Math.round(e)]+i.DEC2HEX[Math.round(n)]+i.DEC2HEX[Math.round(r)]+i.DEC2HEX[Math.round(255*a)]:"#"+i.DEC2HEX[Math.round(e)]+i.DEC2HEX[Math.round(n)]+i.DEC2HEX[Math.round(r)]}};e.default=a},8589:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(8167),a={re:/^hsla?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(?:deg|grad|rad|turn)?)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(%)?))?\s*?\)$/i,hueRe:/^(.+?)(deg|grad|rad|turn)$/i,_hue2deg:function(t){var e=t.match(a.hueRe);if(e){var n=e[1];switch(e[2]){case"grad":return r.default.channel.clamp.h(.9*parseFloat(n));case"rad":return r.default.channel.clamp.h(180*parseFloat(n)/Math.PI);case"turn":return r.default.channel.clamp.h(360*parseFloat(n))}}return r.default.channel.clamp.h(parseFloat(t))},parse:function(t){var e=t.charCodeAt(0);if(104===e||72===e){var n=t.match(a.re);if(n){var o=n[1],s=n[2],c=n[3],u=n[4],l=n[5];return i.default.set({h:a._hue2deg(o),s:r.default.channel.clamp.s(parseFloat(s)),l:r.default.channel.clamp.l(parseFloat(c)),a:u?r.default.channel.clamp.a(l?parseFloat(u)/100:parseFloat(u)):1},t)}}},stringify:function(t){var e=t.h,n=t.s,i=t.l,a=t.a;return a<1?"hsla("+r.default.lang.round(e)+", "+r.default.lang.round(n)+"%, "+r.default.lang.round(i)+"%, "+a+")":"hsl("+r.default.lang.round(e)+", "+r.default.lang.round(n)+"%, "+r.default.lang.round(i)+"%)"}};e.default=a},2191:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1655),i=n(7538),a=n(6762),o=n(8589),s={format:{keyword:i.default,hex:r.default,rgb:a.default,rgba:a.default,hsl:o.default,hsla:o.default},parse:function(t){if("string"!=typeof t)return t;var e=r.default.parse(t)||a.default.parse(t)||o.default.parse(t)||i.default.parse(t);if(e)return e;throw new Error('Unsupported color format: "'+t+'"')},stringify:function(t){return!t.changed&&t.color?t.color:t.type.is(2)||void 0===t.data.r?o.default.stringify(t):t.a<1||!Number.isInteger(t.r)||!Number.isInteger(t.g)||!Number.isInteger(t.b)?a.default.stringify(t):r.default.stringify(t)}};e.default=s},7538:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1655),i={colors:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyanaqua:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",rebeccapurple:"#663399",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",transparent:"#00000000",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},parse:function(t){t=t.toLowerCase();var e=i.colors[t];if(e)return r.default.parse(e)},stringify:function(t){var e=r.default.stringify(t);for(var n in i.colors)if(i.colors[n]===e)return n}};e.default=i},6762:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(8167),a={re:/^rgba?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?)))?\s*?\)$/i,parse:function(t){var e=t.charCodeAt(0);if(114===e||82===e){var n=t.match(a.re);if(n){var o=n[1],s=n[2],c=n[3],u=n[4],l=n[5],h=n[6],f=n[7],d=n[8];return i.default.set({r:r.default.channel.clamp.r(s?2.55*parseFloat(o):parseFloat(o)),g:r.default.channel.clamp.g(u?2.55*parseFloat(c):parseFloat(c)),b:r.default.channel.clamp.b(h?2.55*parseFloat(l):parseFloat(l)),a:f?r.default.channel.clamp.a(d?parseFloat(f)/100:parseFloat(f)):1},t)}}},stringify:function(t){var e=t.r,n=t.g,i=t.b,a=t.a;return a<1?"rgba("+r.default.lang.round(e)+", "+r.default.lang.round(n)+", "+r.default.lang.round(i)+", "+r.default.lang.round(a)+")":"rgb("+r.default.lang.round(e)+", "+r.default.lang.round(n)+", "+r.default.lang.round(i)+")"}};e.default=a},6061:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i={};e.DEC2HEX=i;for(var a=0;a<=255;a++)i[a]=r.default.unit.dec2hex(a)},8613:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),function(t){for(var n in t)e.hasOwnProperty(n)||(e[n]=t[n])}(n(1203))},5371:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191),i=n(418);e.default=function(t,e){var n=r.default.parse(t),a={};for(var o in e)e[o]&&(a[o]=n[o]+e[o]);return i.default(t,a)}},1416:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(2191);e.default=function(t,e,n){var a=i.default.parse(t),o=a[e],s=r.default.channel.clamp[e](o+n);return o!==s&&(a[e]=s),i.default.stringify(a)}},9353:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"a")}},3394:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"b")}},418:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(2191);e.default=function(t,e){var n=i.default.parse(t);for(var a in e)n[a]=r.default.channel.clamp[a](e[a]);return i.default.stringify(n)}},6197:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(2191);e.default=function(t,e){return r.default.lang.round(i.default.parse(t)[e])}},7361:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t){return r.default(t,"h",180)}},3042:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(8346);e.default=function(t,e){var n=i.default(t),a=i.default(e),o=Math.max(n,a),s=Math.min(n,a),c=(o+Number.EPSILON)/(s+Number.EPSILON);return r.default.lang.round(r.default.lang.clamp(c,1,10))}},1364:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"l",-e)}},9610:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"s",-e)}},572:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(418);e.default=function(t){return r.default(t,{s:0})}},2299:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"g")}},3116:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(8167),a=n(2191);e.default=function(t,e,n,o){void 0===o&&(o=1);var s=i.default.set({h:r.default.channel.clamp.h(t),s:r.default.channel.clamp.s(e),l:r.default.channel.clamp.l(n),a:r.default.channel.clamp.a(o)});return a.default.stringify(s)}},3008:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"h")}},1203:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6538);e.hex=r.default;var i=n(6538);e.rgb=i.default;var a=n(6538);e.rgba=a.default;var o=n(3116);e.hsl=o.default;var s=n(3116);e.hsla=s.default;var c=n(2619);e.toKeyword=c.default;var u=n(4307);e.toHex=u.default;var l=n(4125);e.toRgba=l.default;var h=n(7329);e.toHsla=h.default;var f=n(6197);e.channel=f.default;var d=n(9640);e.red=d.default;var p=n(2299);e.green=p.default;var y=n(3394);e.blue=y.default;var g=n(3008);e.hue=g.default;var m=n(6451);e.saturation=m.default;var v=n(9235);e.lightness=v.default;var b=n(9353);e.alpha=b.default;var _=n(9353);e.opacity=_.default;var x=n(3042);e.contrast=x.default;var w=n(8346);e.luminance=w.default;var k=n(4117);e.isDark=k.default;var T=n(2224);e.isLight=T.default;var E=n(585);e.isValid=E.default;var C=n(1080);e.saturate=C.default;var S=n(9610);e.desaturate=S.default;var A=n(3235);e.lighten=A.default;var M=n(1364);e.darken=M.default;var N=n(7189);e.opacify=N.default;var D=n(7189);e.fadeIn=D.default;var O=n(4989);e.transparentize=O.default;var B=n(4989);e.fadeOut=B.default;var L=n(7361);e.complement=L.default;var I=n(572);e.grayscale=I.default;var R=n(5371);e.adjust=R.default;var F=n(418);e.change=F.default;var P=n(566);e.invert=P.default;var j=n(1861);e.mix=j.default;var Y=n(7081);e.scale=Y.default},566:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191),i=n(1861);e.default=function(t,e){void 0===e&&(e=100);var n=r.default.parse(t);return n.r=255-n.r,n.g=255-n.g,n.b=255-n.b,i.default(n,t,e)}},4117:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2224);e.default=function(t){return!r.default(t)}},2224:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(8346);e.default=function(t){return r.default(t)>=.5}},585:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191);e.default=function(t){try{return r.default.parse(t),!0}catch(t){return!1}}},3235:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"l",e)}},9235:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"l")}},8346:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(2191);e.default=function(t){var e=i.default.parse(t),n=e.r,a=e.g,o=e.b,s=.2126*r.default.channel.toLinear(n)+.7152*r.default.channel.toLinear(a)+.0722*r.default.channel.toLinear(o);return r.default.lang.round(s)}},1861:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191),i=n(6538);e.default=function(t,e,n){void 0===n&&(n=50);var a=r.default.parse(t),o=a.r,s=a.g,c=a.b,u=a.a,l=r.default.parse(e),h=l.r,f=l.g,d=l.b,p=l.a,y=n/100,g=2*y-1,m=u-p,v=((g*m==-1?g:(g+m)/(1+g*m))+1)/2,b=1-v,_=o*v+h*b,x=s*v+f*b,w=c*v+d*b,k=u*y+p*(1-y);return i.default(_,x,w,k)}},7189:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"a",e)}},9640:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"r")}},6538:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(8167),a=n(2191),o=n(418);e.default=function(t,e,n,s){if(void 0===n&&(n=0),void 0===s&&(s=1),"number"!=typeof t)return o.default(t,{a:e});var c=i.default.set({r:r.default.channel.clamp.r(t),g:r.default.channel.clamp.g(e),b:r.default.channel.clamp.b(n),a:r.default.channel.clamp.a(s)});return a.default.stringify(c)}},1080:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"s",e)}},6451:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"s")}},7081:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(2191),a=n(5371);e.default=function(t,e){var n,o,s,c=i.default.parse(t),u={};for(var l in e)u[l]=(n=c[l],o=e[l],s=r.default.channel.max[l],o>0?(s-n)*o/100:n*o/100);return a.default(t,u)}},4307:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191);e.default=function(t){return r.default.format.hex.stringify(r.default.parse(t))}},7329:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191);e.default=function(t){return r.default.format.hsla.stringify(r.default.parse(t))}},2619:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191);e.default=function(t){return r.default.format.keyword.stringify(r.default.parse(t))}},4125:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191);e.default=function(t){return r.default.format.rgba.stringify(r.default.parse(t))}},4989:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"a",-e)}},7994:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n={min:{r:0,g:0,b:0,s:0,l:0,a:0},max:{r:255,g:255,b:255,h:360,s:100,l:100,a:1},clamp:{r:function(t){return t>=255?255:t<0?0:t},g:function(t){return t>=255?255:t<0?0:t},b:function(t){return t>=255?255:t<0?0:t},h:function(t){return t%360},s:function(t){return t>=100?100:t<0?0:t},l:function(t){return t>=100?100:t<0?0:t},a:function(t){return t>=1?1:t<0?0:t}},toLinear:function(t){var e=t/255;return t>.03928?Math.pow((e+.055)/1.055,2.4):e/12.92},hue2rgb:function(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t},hsl2rgb:function(t,e){var r=t.h,i=t.s,a=t.l;if(!i)return 2.55*a;r/=360,i/=100;var o=(a/=100)<.5?a*(1+i):a+i-a*i,s=2*a-o;switch(e){case"r":return 255*n.hue2rgb(s,o,r+1/3);case"g":return 255*n.hue2rgb(s,o,r);case"b":return 255*n.hue2rgb(s,o,r-1/3)}},rgb2hsl:function(t,e){var n=t.r,r=t.g,i=t.b;n/=255,r/=255,i/=255;var a=Math.max(n,r,i),o=Math.min(n,r,i),s=(a+o)/2;if("l"===e)return 100*s;if(a===o)return 0;var c=a-o;if("s"===e)return 100*(s>.5?c/(2-a-o):c/(a+o));switch(a){case n:return 60*((r-i)/c+(r<i?6:0));case r:return 60*((i-n)/c+2);case i:return 60*((n-r)/c+4);default:return-1}}};e.default=n},1083:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(7994),i=n(4027),a=n(318),o={channel:r.default,lang:i.default,unit:a.default};e.default=o},4027:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n={clamp:function(t,e,n){return e>n?Math.min(e,Math.max(n,t)):Math.min(n,Math.max(e,t))},round:function(t){return Math.round(1e10*t)/1e10}};e.default=n},318:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n={dec2hex:function(t){var e=Math.round(t).toString(16);return e.length>1?e:"0"+e}};e.default=n},8552:(t,e,n)=>{var r=n(852)(n(5639),"DataView");t.exports=r},1989:(t,e,n)=>{var r=n(1789),i=n(401),a=n(7667),o=n(1327),s=n(1866);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},8407:(t,e,n)=>{var r=n(7040),i=n(2188),a=n(2117),o=n(7518),s=n(4705);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},7071:(t,e,n)=>{var r=n(852)(n(5639),"Map");t.exports=r},3369:(t,e,n)=>{var r=n(4785),i=n(1285),a=n(6e3),o=n(9916),s=n(5265);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},3818:(t,e,n)=>{var r=n(852)(n(5639),"Promise");t.exports=r},8525:(t,e,n)=>{var r=n(852)(n(5639),"Set");t.exports=r},8668:(t,e,n)=>{var r=n(3369),i=n(619),a=n(2385);function o(t){var e=-1,n=null==t?0:t.length;for(this.__data__=new r;++e<n;)this.add(t[e])}o.prototype.add=o.prototype.push=i,o.prototype.has=a,t.exports=o},6384:(t,e,n)=>{var r=n(8407),i=n(7465),a=n(3779),o=n(7599),s=n(4758),c=n(4309);function u(t){var e=this.__data__=new r(t);this.size=e.size}u.prototype.clear=i,u.prototype.delete=a,u.prototype.get=o,u.prototype.has=s,u.prototype.set=c,t.exports=u},2705:(t,e,n)=>{var r=n(5639).Symbol;t.exports=r},1149:(t,e,n)=>{var r=n(5639).Uint8Array;t.exports=r},577:(t,e,n)=>{var r=n(852)(n(5639),"WeakMap");t.exports=r},6874:t=>{t.exports=function(t,e,n){switch(n.length){case 0:return t.call(e);case 1:return t.call(e,n[0]);case 2:return t.call(e,n[0],n[1]);case 3:return t.call(e,n[0],n[1],n[2])}return t.apply(e,n)}},7412:t=>{t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length;++n<r&&!1!==e(t[n],n,t););return t}},4963:t=>{t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,i=0,a=[];++n<r;){var o=t[n];e(o,n,t)&&(a[i++]=o)}return a}},7443:(t,e,n)=>{var r=n(2118);t.exports=function(t,e){return!(null==t||!t.length)&&r(t,e,0)>-1}},1196:t=>{t.exports=function(t,e,n){for(var r=-1,i=null==t?0:t.length;++r<i;)if(n(e,t[r]))return!0;return!1}},4636:(t,e,n)=>{var r=n(2545),i=n(5694),a=n(1469),o=n(4144),s=n(5776),c=n(6719),u=Object.prototype.hasOwnProperty;t.exports=function(t,e){var n=a(t),l=!n&&i(t),h=!n&&!l&&o(t),f=!n&&!l&&!h&&c(t),d=n||l||h||f,p=d?r(t.length,String):[],y=p.length;for(var g in t)!e&&!u.call(t,g)||d&&("length"==g||h&&("offset"==g||"parent"==g)||f&&("buffer"==g||"byteLength"==g||"byteOffset"==g)||s(g,y))||p.push(g);return p}},9932:t=>{t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,i=Array(r);++n<r;)i[n]=e(t[n],n,t);return i}},2488:t=>{t.exports=function(t,e){for(var n=-1,r=e.length,i=t.length;++n<r;)t[i+n]=e[n];return t}},2663:t=>{t.exports=function(t,e,n,r){var i=-1,a=null==t?0:t.length;for(r&&a&&(n=t[++i]);++i<a;)n=e(n,t[i],i,t);return n}},2908:t=>{t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length;++n<r;)if(e(t[n],n,t))return!0;return!1}},8983:(t,e,n)=>{var r=n(371)("length");t.exports=r},6556:(t,e,n)=>{var r=n(9465),i=n(7813);t.exports=function(t,e,n){(void 0!==n&&!i(t[e],n)||void 0===n&&!(e in t))&&r(t,e,n)}},4865:(t,e,n)=>{var r=n(9465),i=n(7813),a=Object.prototype.hasOwnProperty;t.exports=function(t,e,n){var o=t[e];a.call(t,e)&&i(o,n)&&(void 0!==n||e in t)||r(t,e,n)}},8470:(t,e,n)=>{var r=n(7813);t.exports=function(t,e){for(var n=t.length;n--;)if(r(t[n][0],e))return n;return-1}},4037:(t,e,n)=>{var r=n(8363),i=n(3674);t.exports=function(t,e){return t&&r(e,i(e),t)}},3886:(t,e,n)=>{var r=n(8363),i=n(1704);t.exports=function(t,e){return t&&r(e,i(e),t)}},9465:(t,e,n)=>{var r=n(8777);t.exports=function(t,e,n){"__proto__"==e&&r?r(t,e,{configurable:!0,enumerable:!0,value:n,writable:!0}):t[e]=n}},5990:(t,e,n)=>{var r=n(6384),i=n(7412),a=n(4865),o=n(4037),s=n(3886),c=n(4626),u=n(278),l=n(8805),h=n(1911),f=n(8234),d=n(6904),p=n(4160),y=n(3824),g=n(9148),m=n(8517),v=n(1469),b=n(4144),_=n(6688),x=n(3218),w=n(2928),k=n(3674),T=n(1704),E="[object Arguments]",C="[object Function]",S="[object Object]",A={};A[E]=A["[object Array]"]=A["[object ArrayBuffer]"]=A["[object DataView]"]=A["[object Boolean]"]=A["[object Date]"]=A["[object Float32Array]"]=A["[object Float64Array]"]=A["[object Int8Array]"]=A["[object Int16Array]"]=A["[object Int32Array]"]=A["[object Map]"]=A["[object Number]"]=A[S]=A["[object RegExp]"]=A["[object Set]"]=A["[object String]"]=A["[object Symbol]"]=A["[object Uint8Array]"]=A["[object Uint8ClampedArray]"]=A["[object Uint16Array]"]=A["[object Uint32Array]"]=!0,A["[object Error]"]=A[C]=A["[object WeakMap]"]=!1,t.exports=function t(e,n,M,N,D,O){var B,L=1&n,I=2&n,R=4&n;if(M&&(B=D?M(e,N,D,O):M(e)),void 0!==B)return B;if(!x(e))return e;var F=v(e);if(F){if(B=y(e),!L)return u(e,B)}else{var P=p(e),j=P==C||"[object GeneratorFunction]"==P;if(b(e))return c(e,L);if(P==S||P==E||j&&!D){if(B=I||j?{}:m(e),!L)return I?h(e,s(B,e)):l(e,o(B,e))}else{if(!A[P])return D?e:{};B=g(e,P,L)}}O||(O=new r);var Y=O.get(e);if(Y)return Y;O.set(e,B),w(e)?e.forEach((function(r){B.add(t(r,n,M,r,e,O))})):_(e)&&e.forEach((function(r,i){B.set(i,t(r,n,M,i,e,O))}));var z=F?void 0:(R?I?d:f:I?T:k)(e);return i(z||e,(function(r,i){z&&(r=e[i=r]),a(B,i,t(r,n,M,i,e,O))})),B}},3118:(t,e,n)=>{var r=n(3218),i=Object.create,a=function(){function t(){}return function(e){if(!r(e))return{};if(i)return i(e);t.prototype=e;var n=new t;return t.prototype=void 0,n}}();t.exports=a},9881:(t,e,n)=>{var r=n(7816),i=n(9291)(r);t.exports=i},6029:(t,e,n)=>{var r=n(3448);t.exports=function(t,e,n){for(var i=-1,a=t.length;++i<a;){var o=t[i],s=e(o);if(null!=s&&(void 0===c?s==s&&!r(s):n(s,c)))var c=s,u=o}return u}},760:(t,e,n)=>{var r=n(9881);t.exports=function(t,e){var n=[];return r(t,(function(t,r,i){e(t,r,i)&&n.push(t)})),n}},1848:t=>{t.exports=function(t,e,n,r){for(var i=t.length,a=n+(r?1:-1);r?a--:++a<i;)if(e(t[a],a,t))return a;return-1}},1078:(t,e,n)=>{var r=n(2488),i=n(7285);t.exports=function t(e,n,a,o,s){var c=-1,u=e.length;for(a||(a=i),s||(s=[]);++c<u;){var l=e[c];n>0&&a(l)?n>1?t(l,n-1,a,o,s):r(s,l):o||(s[s.length]=l)}return s}},8483:(t,e,n)=>{var r=n(5063)();t.exports=r},7816:(t,e,n)=>{var r=n(8483),i=n(3674);t.exports=function(t,e){return t&&r(t,e,i)}},7786:(t,e,n)=>{var r=n(1811),i=n(327);t.exports=function(t,e){for(var n=0,a=(e=r(e,t)).length;null!=t&&n<a;)t=t[i(e[n++])];return n&&n==a?t:void 0}},8866:(t,e,n)=>{var r=n(2488),i=n(1469);t.exports=function(t,e,n){var a=e(t);return i(t)?a:r(a,n(t))}},4239:(t,e,n)=>{var r=n(2705),i=n(9607),a=n(2333),o=r?r.toStringTag:void 0;t.exports=function(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":o&&o in Object(t)?i(t):a(t)}},3325:t=>{t.exports=function(t,e){return t>e}},8565:t=>{var e=Object.prototype.hasOwnProperty;t.exports=function(t,n){return null!=t&&e.call(t,n)}},13:t=>{t.exports=function(t,e){return null!=t&&e in Object(t)}},2118:(t,e,n)=>{var r=n(1848),i=n(2722),a=n(2351);t.exports=function(t,e,n){return e==e?a(t,e,n):r(t,i,n)}},9454:(t,e,n)=>{var r=n(4239),i=n(7005);t.exports=function(t){return i(t)&&"[object Arguments]"==r(t)}},939:(t,e,n)=>{var r=n(2492),i=n(7005);t.exports=function t(e,n,a,o,s){return e===n||(null==e||null==n||!i(e)&&!i(n)?e!=e&&n!=n:r(e,n,a,o,t,s))}},2492:(t,e,n)=>{var r=n(6384),i=n(7114),a=n(8351),o=n(6096),s=n(4160),c=n(1469),u=n(4144),l=n(6719),h="[object Arguments]",f="[object Array]",d="[object Object]",p=Object.prototype.hasOwnProperty;t.exports=function(t,e,n,y,g,m){var v=c(t),b=c(e),_=v?f:s(t),x=b?f:s(e),w=(_=_==h?d:_)==d,k=(x=x==h?d:x)==d,T=_==x;if(T&&u(t)){if(!u(e))return!1;v=!0,w=!1}if(T&&!w)return m||(m=new r),v||l(t)?i(t,e,n,y,g,m):a(t,e,_,n,y,g,m);if(!(1&n)){var E=w&&p.call(t,"__wrapped__"),C=k&&p.call(e,"__wrapped__");if(E||C){var S=E?t.value():t,A=C?e.value():e;return m||(m=new r),g(S,A,n,y,m)}}return!!T&&(m||(m=new r),o(t,e,n,y,g,m))}},5588:(t,e,n)=>{var r=n(4160),i=n(7005);t.exports=function(t){return i(t)&&"[object Map]"==r(t)}},2958:(t,e,n)=>{var r=n(6384),i=n(939);t.exports=function(t,e,n,a){var o=n.length,s=o,c=!a;if(null==t)return!s;for(t=Object(t);o--;){var u=n[o];if(c&&u[2]?u[1]!==t[u[0]]:!(u[0]in t))return!1}for(;++o<s;){var l=(u=n[o])[0],h=t[l],f=u[1];if(c&&u[2]){if(void 0===h&&!(l in t))return!1}else{var d=new r;if(a)var p=a(h,f,l,t,e,d);if(!(void 0===p?i(f,h,3,a,d):p))return!1}}return!0}},2722:t=>{t.exports=function(t){return t!=t}},8458:(t,e,n)=>{var r=n(3560),i=n(5346),a=n(3218),o=n(346),s=/^\[object .+?Constructor\]$/,c=Function.prototype,u=Object.prototype,l=c.toString,h=u.hasOwnProperty,f=RegExp("^"+l.call(h).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=function(t){return!(!a(t)||i(t))&&(r(t)?f:s).test(o(t))}},9221:(t,e,n)=>{var r=n(4160),i=n(7005);t.exports=function(t){return i(t)&&"[object Set]"==r(t)}},8749:(t,e,n)=>{var r=n(4239),i=n(1780),a=n(7005),o={};o["[object Float32Array]"]=o["[object Float64Array]"]=o["[object Int8Array]"]=o["[object Int16Array]"]=o["[object Int32Array]"]=o["[object Uint8Array]"]=o["[object Uint8ClampedArray]"]=o["[object Uint16Array]"]=o["[object Uint32Array]"]=!0,o["[object Arguments]"]=o["[object Array]"]=o["[object ArrayBuffer]"]=o["[object Boolean]"]=o["[object DataView]"]=o["[object Date]"]=o["[object Error]"]=o["[object Function]"]=o["[object Map]"]=o["[object Number]"]=o["[object Object]"]=o["[object RegExp]"]=o["[object Set]"]=o["[object String]"]=o["[object WeakMap]"]=!1,t.exports=function(t){return a(t)&&i(t.length)&&!!o[r(t)]}},7206:(t,e,n)=>{var r=n(1573),i=n(6432),a=n(6557),o=n(1469),s=n(9601);t.exports=function(t){return"function"==typeof t?t:null==t?a:"object"==typeof t?o(t)?i(t[0],t[1]):r(t):s(t)}},280:(t,e,n)=>{var r=n(5726),i=n(6916),a=Object.prototype.hasOwnProperty;t.exports=function(t){if(!r(t))return i(t);var e=[];for(var n in Object(t))a.call(t,n)&&"constructor"!=n&&e.push(n);return e}},313:(t,e,n)=>{var r=n(3218),i=n(5726),a=n(3498),o=Object.prototype.hasOwnProperty;t.exports=function(t){if(!r(t))return a(t);var e=i(t),n=[];for(var s in t)("constructor"!=s||!e&&o.call(t,s))&&n.push(s);return n}},433:t=>{t.exports=function(t,e){return t<e}},9199:(t,e,n)=>{var r=n(9881),i=n(8612);t.exports=function(t,e){var n=-1,a=i(t)?Array(t.length):[];return r(t,(function(t,r,i){a[++n]=e(t,r,i)})),a}},1573:(t,e,n)=>{var r=n(2958),i=n(1499),a=n(2634);t.exports=function(t){var e=i(t);return 1==e.length&&e[0][2]?a(e[0][0],e[0][1]):function(n){return n===t||r(n,t,e)}}},6432:(t,e,n)=>{var r=n(939),i=n(1917),a=n(9095),o=n(5403),s=n(9162),c=n(2634),u=n(327);t.exports=function(t,e){return o(t)&&s(e)?c(u(t),e):function(n){var o=i(n,t);return void 0===o&&o===e?a(n,t):r(e,o,3)}}},2980:(t,e,n)=>{var r=n(6384),i=n(6556),a=n(8483),o=n(9783),s=n(3218),c=n(1704),u=n(6390);t.exports=function t(e,n,l,h,f){e!==n&&a(n,(function(a,c){if(f||(f=new r),s(a))o(e,n,c,l,t,h,f);else{var d=h?h(u(e,c),a,c+"",e,n,f):void 0;void 0===d&&(d=a),i(e,c,d)}}),c)}},9783:(t,e,n)=>{var r=n(6556),i=n(4626),a=n(7133),o=n(278),s=n(8517),c=n(5694),u=n(1469),l=n(9246),h=n(4144),f=n(3560),d=n(3218),p=n(8630),y=n(6719),g=n(6390),m=n(3678);t.exports=function(t,e,n,v,b,_,x){var w=g(t,n),k=g(e,n),T=x.get(k);if(T)r(t,n,T);else{var E=_?_(w,k,n+"",t,e,x):void 0,C=void 0===E;if(C){var S=u(k),A=!S&&h(k),M=!S&&!A&&y(k);E=k,S||A||M?u(w)?E=w:l(w)?E=o(w):A?(C=!1,E=i(k,!0)):M?(C=!1,E=a(k,!0)):E=[]:p(k)||c(k)?(E=w,c(w)?E=m(w):d(w)&&!f(w)||(E=s(k))):C=!1}C&&(x.set(k,E),b(E,k,v,_,x),x.delete(k)),r(t,n,E)}}},9556:(t,e,n)=>{var r=n(9932),i=n(7786),a=n(7206),o=n(9199),s=n(1131),c=n(1717),u=n(5022),l=n(6557),h=n(1469);t.exports=function(t,e,n){e=e.length?r(e,(function(t){return h(t)?function(e){return i(e,1===t.length?t[0]:t)}:t})):[l];var f=-1;e=r(e,c(a));var d=o(t,(function(t,n,i){return{criteria:r(e,(function(e){return e(t)})),index:++f,value:t}}));return s(d,(function(t,e){return u(t,e,n)}))}},5970:(t,e,n)=>{var r=n(3012),i=n(9095);t.exports=function(t,e){return r(t,e,(function(e,n){return i(t,n)}))}},3012:(t,e,n)=>{var r=n(7786),i=n(611),a=n(1811);t.exports=function(t,e,n){for(var o=-1,s=e.length,c={};++o<s;){var u=e[o],l=r(t,u);n(l,u)&&i(c,a(u,t),l)}return c}},371:t=>{t.exports=function(t){return function(e){return null==e?void 0:e[t]}}},9152:(t,e,n)=>{var r=n(7786);t.exports=function(t){return function(e){return r(e,t)}}},98:t=>{var e=Math.ceil,n=Math.max;t.exports=function(t,r,i,a){for(var o=-1,s=n(e((r-t)/(i||1)),0),c=Array(s);s--;)c[a?s:++o]=t,t+=i;return c}},107:t=>{t.exports=function(t,e,n,r,i){return i(t,(function(t,i,a){n=r?(r=!1,t):e(n,t,i,a)})),n}},5976:(t,e,n)=>{var r=n(6557),i=n(5357),a=n(61);t.exports=function(t,e){return a(i(t,e,r),t+"")}},611:(t,e,n)=>{var r=n(4865),i=n(1811),a=n(5776),o=n(3218),s=n(327);t.exports=function(t,e,n,c){if(!o(t))return t;for(var u=-1,l=(e=i(e,t)).length,h=l-1,f=t;null!=f&&++u<l;){var d=s(e[u]),p=n;if("__proto__"===d||"constructor"===d||"prototype"===d)return t;if(u!=h){var y=f[d];void 0===(p=c?c(y,d,f):void 0)&&(p=o(y)?y:a(e[u+1])?[]:{})}r(f,d,p),f=f[d]}return t}},6560:(t,e,n)=>{var r=n(5703),i=n(8777),a=n(6557),o=i?function(t,e){return i(t,"toString",{configurable:!0,enumerable:!1,value:r(e),writable:!0})}:a;t.exports=o},1131:t=>{t.exports=function(t,e){var n=t.length;for(t.sort(e);n--;)t[n]=t[n].value;return t}},2545:t=>{t.exports=function(t,e){for(var n=-1,r=Array(t);++n<t;)r[n]=e(n);return r}},531:(t,e,n)=>{var r=n(2705),i=n(9932),a=n(1469),o=n(3448),s=r?r.prototype:void 0,c=s?s.toString:void 0;t.exports=function t(e){if("string"==typeof e)return e;if(a(e))return i(e,t)+"";if(o(e))return c?c.call(e):"";var n=e+"";return"0"==n&&1/e==-1/0?"-0":n}},7561:(t,e,n)=>{var r=n(7990),i=/^\s+/;t.exports=function(t){return t?t.slice(0,r(t)+1).replace(i,""):t}},1717:t=>{t.exports=function(t){return function(e){return t(e)}}},5652:(t,e,n)=>{var r=n(8668),i=n(7443),a=n(1196),o=n(4757),s=n(3593),c=n(1814);t.exports=function(t,e,n){var u=-1,l=i,h=t.length,f=!0,d=[],p=d;if(n)f=!1,l=a;else if(h>=200){var y=e?null:s(t);if(y)return c(y);f=!1,l=o,p=new r}else p=e?[]:d;t:for(;++u<h;){var g=t[u],m=e?e(g):g;if(g=n||0!==g?g:0,f&&m==m){for(var v=p.length;v--;)if(p[v]===m)continue t;e&&p.push(m),d.push(g)}else l(p,m,n)||(p!==d&&p.push(m),d.push(g))}return d}},7415:(t,e,n)=>{var r=n(9932);t.exports=function(t,e){return r(e,(function(e){return t[e]}))}},1757:t=>{t.exports=function(t,e,n){for(var r=-1,i=t.length,a=e.length,o={};++r<i;){var s=r<a?e[r]:void 0;n(o,t[r],s)}return o}},4757:t=>{t.exports=function(t,e){return t.has(e)}},4290:(t,e,n)=>{var r=n(6557);t.exports=function(t){return"function"==typeof t?t:r}},1811:(t,e,n)=>{var r=n(1469),i=n(5403),a=n(5514),o=n(9833);t.exports=function(t,e){return r(t)?t:i(t,e)?[t]:a(o(t))}},4318:(t,e,n)=>{var r=n(1149);t.exports=function(t){var e=new t.constructor(t.byteLength);return new r(e).set(new r(t)),e}},4626:(t,e,n)=>{t=n.nmd(t);var r=n(5639),i=e&&!e.nodeType&&e,a=i&&t&&!t.nodeType&&t,o=a&&a.exports===i?r.Buffer:void 0,s=o?o.allocUnsafe:void 0;t.exports=function(t,e){if(e)return t.slice();var n=t.length,r=s?s(n):new t.constructor(n);return t.copy(r),r}},7157:(t,e,n)=>{var r=n(4318);t.exports=function(t,e){var n=e?r(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.byteLength)}},3147:t=>{var e=/\w*$/;t.exports=function(t){var n=new t.constructor(t.source,e.exec(t));return n.lastIndex=t.lastIndex,n}},419:(t,e,n)=>{var r=n(2705),i=r?r.prototype:void 0,a=i?i.valueOf:void 0;t.exports=function(t){return a?Object(a.call(t)):{}}},7133:(t,e,n)=>{var r=n(4318);t.exports=function(t,e){var n=e?r(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.length)}},6393:(t,e,n)=>{var r=n(3448);t.exports=function(t,e){if(t!==e){var n=void 0!==t,i=null===t,a=t==t,o=r(t),s=void 0!==e,c=null===e,u=e==e,l=r(e);if(!c&&!l&&!o&&t>e||o&&s&&u&&!c&&!l||i&&s&&u||!n&&u||!a)return 1;if(!i&&!o&&!l&&t<e||l&&n&&a&&!i&&!o||c&&n&&a||!s&&a||!u)return-1}return 0}},5022:(t,e,n)=>{var r=n(6393);t.exports=function(t,e,n){for(var i=-1,a=t.criteria,o=e.criteria,s=a.length,c=n.length;++i<s;){var u=r(a[i],o[i]);if(u)return i>=c?u:u*("desc"==n[i]?-1:1)}return t.index-e.index}},278:t=>{t.exports=function(t,e){var n=-1,r=t.length;for(e||(e=Array(r));++n<r;)e[n]=t[n];return e}},8363:(t,e,n)=>{var r=n(4865),i=n(9465);t.exports=function(t,e,n,a){var o=!n;n||(n={});for(var s=-1,c=e.length;++s<c;){var u=e[s],l=a?a(n[u],t[u],u,n,t):void 0;void 0===l&&(l=t[u]),o?i(n,u,l):r(n,u,l)}return n}},8805:(t,e,n)=>{var r=n(8363),i=n(9551);t.exports=function(t,e){return r(t,i(t),e)}},1911:(t,e,n)=>{var r=n(8363),i=n(1442);t.exports=function(t,e){return r(t,i(t),e)}},4429:(t,e,n)=>{var r=n(5639)["__core-js_shared__"];t.exports=r},1750:(t,e,n)=>{var r=n(5976),i=n(6612);t.exports=function(t){return r((function(e,n){var r=-1,a=n.length,o=a>1?n[a-1]:void 0,s=a>2?n[2]:void 0;for(o=t.length>3&&"function"==typeof o?(a--,o):void 0,s&&i(n[0],n[1],s)&&(o=a<3?void 0:o,a=1),e=Object(e);++r<a;){var c=n[r];c&&t(e,c,r,o)}return e}))}},9291:(t,e,n)=>{var r=n(8612);t.exports=function(t,e){return function(n,i){if(null==n)return n;if(!r(n))return t(n,i);for(var a=n.length,o=e?a:-1,s=Object(n);(e?o--:++o<a)&&!1!==i(s[o],o,s););return n}}},5063:t=>{t.exports=function(t){return function(e,n,r){for(var i=-1,a=Object(e),o=r(e),s=o.length;s--;){var c=o[t?s:++i];if(!1===n(a[c],c,a))break}return e}}},7740:(t,e,n)=>{var r=n(7206),i=n(8612),a=n(3674);t.exports=function(t){return function(e,n,o){var s=Object(e);if(!i(e)){var c=r(n,3);e=a(e),n=function(t){return c(s[t],t,s)}}var u=t(e,n,o);return u>-1?s[c?e[u]:u]:void 0}}},7445:(t,e,n)=>{var r=n(98),i=n(6612),a=n(8601);t.exports=function(t){return function(e,n,o){return o&&"number"!=typeof o&&i(e,n,o)&&(n=o=void 0),e=a(e),void 0===n?(n=e,e=0):n=a(n),o=void 0===o?e<n?1:-1:a(o),r(e,n,o,t)}}},3593:(t,e,n)=>{var r=n(8525),i=n(308),a=n(1814),o=r&&1/a(new r([,-0]))[1]==1/0?function(t){return new r(t)}:i;t.exports=o},8777:(t,e,n)=>{var r=n(852),i=function(){try{var t=r(Object,"defineProperty");return t({},"",{}),t}catch(t){}}();t.exports=i},7114:(t,e,n)=>{var r=n(8668),i=n(2908),a=n(4757);t.exports=function(t,e,n,o,s,c){var u=1&n,l=t.length,h=e.length;if(l!=h&&!(u&&h>l))return!1;var f=c.get(t),d=c.get(e);if(f&&d)return f==e&&d==t;var p=-1,y=!0,g=2&n?new r:void 0;for(c.set(t,e),c.set(e,t);++p<l;){var m=t[p],v=e[p];if(o)var b=u?o(v,m,p,e,t,c):o(m,v,p,t,e,c);if(void 0!==b){if(b)continue;y=!1;break}if(g){if(!i(e,(function(t,e){if(!a(g,e)&&(m===t||s(m,t,n,o,c)))return g.push(e)}))){y=!1;break}}else if(m!==v&&!s(m,v,n,o,c)){y=!1;break}}return c.delete(t),c.delete(e),y}},8351:(t,e,n)=>{var r=n(2705),i=n(1149),a=n(7813),o=n(7114),s=n(8776),c=n(1814),u=r?r.prototype:void 0,l=u?u.valueOf:void 0;t.exports=function(t,e,n,r,u,h,f){switch(n){case"[object DataView]":if(t.byteLength!=e.byteLength||t.byteOffset!=e.byteOffset)return!1;t=t.buffer,e=e.buffer;case"[object ArrayBuffer]":return!(t.byteLength!=e.byteLength||!h(new i(t),new i(e)));case"[object Boolean]":case"[object Date]":case"[object Number]":return a(+t,+e);case"[object Error]":return t.name==e.name&&t.message==e.message;case"[object RegExp]":case"[object String]":return t==e+"";case"[object Map]":var d=s;case"[object Set]":var p=1&r;if(d||(d=c),t.size!=e.size&&!p)return!1;var y=f.get(t);if(y)return y==e;r|=2,f.set(t,e);var g=o(d(t),d(e),r,u,h,f);return f.delete(t),g;case"[object Symbol]":if(l)return l.call(t)==l.call(e)}return!1}},6096:(t,e,n)=>{var r=n(8234),i=Object.prototype.hasOwnProperty;t.exports=function(t,e,n,a,o,s){var c=1&n,u=r(t),l=u.length;if(l!=r(e).length&&!c)return!1;for(var h=l;h--;){var f=u[h];if(!(c?f in e:i.call(e,f)))return!1}var d=s.get(t),p=s.get(e);if(d&&p)return d==e&&p==t;var y=!0;s.set(t,e),s.set(e,t);for(var g=c;++h<l;){var m=t[f=u[h]],v=e[f];if(a)var b=c?a(v,m,f,e,t,s):a(m,v,f,t,e,s);if(!(void 0===b?m===v||o(m,v,n,a,s):b)){y=!1;break}g||(g="constructor"==f)}if(y&&!g){var _=t.constructor,x=e.constructor;_==x||!("constructor"in t)||!("constructor"in e)||"function"==typeof _&&_ instanceof _&&"function"==typeof x&&x instanceof x||(y=!1)}return s.delete(t),s.delete(e),y}},9021:(t,e,n)=>{var r=n(5564),i=n(5357),a=n(61);t.exports=function(t){return a(i(t,void 0,r),t+"")}},1957:(t,e,n)=>{var r="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g;t.exports=r},8234:(t,e,n)=>{var r=n(8866),i=n(9551),a=n(3674);t.exports=function(t){return r(t,a,i)}},6904:(t,e,n)=>{var r=n(8866),i=n(1442),a=n(1704);t.exports=function(t){return r(t,a,i)}},5050:(t,e,n)=>{var r=n(7019);t.exports=function(t,e){var n=t.__data__;return r(e)?n["string"==typeof e?"string":"hash"]:n.map}},1499:(t,e,n)=>{var r=n(9162),i=n(3674);t.exports=function(t){for(var e=i(t),n=e.length;n--;){var a=e[n],o=t[a];e[n]=[a,o,r(o)]}return e}},852:(t,e,n)=>{var r=n(8458),i=n(7801);t.exports=function(t,e){var n=i(t,e);return r(n)?n:void 0}},5924:(t,e,n)=>{var r=n(5569)(Object.getPrototypeOf,Object);t.exports=r},9607:(t,e,n)=>{var r=n(2705),i=Object.prototype,a=i.hasOwnProperty,o=i.toString,s=r?r.toStringTag:void 0;t.exports=function(t){var e=a.call(t,s),n=t[s];try{t[s]=void 0;var r=!0}catch(t){}var i=o.call(t);return r&&(e?t[s]=n:delete t[s]),i}},9551:(t,e,n)=>{var r=n(4963),i=n(479),a=Object.prototype.propertyIsEnumerable,o=Object.getOwnPropertySymbols,s=o?function(t){return null==t?[]:(t=Object(t),r(o(t),(function(e){return a.call(t,e)})))}:i;t.exports=s},1442:(t,e,n)=>{var r=n(2488),i=n(5924),a=n(9551),o=n(479),s=Object.getOwnPropertySymbols?function(t){for(var e=[];t;)r(e,a(t)),t=i(t);return e}:o;t.exports=s},4160:(t,e,n)=>{var r=n(8552),i=n(7071),a=n(3818),o=n(8525),s=n(577),c=n(4239),u=n(346),l="[object Map]",h="[object Promise]",f="[object Set]",d="[object WeakMap]",p="[object DataView]",y=u(r),g=u(i),m=u(a),v=u(o),b=u(s),_=c;(r&&_(new r(new ArrayBuffer(1)))!=p||i&&_(new i)!=l||a&&_(a.resolve())!=h||o&&_(new o)!=f||s&&_(new s)!=d)&&(_=function(t){var e=c(t),n="[object Object]"==e?t.constructor:void 0,r=n?u(n):"";if(r)switch(r){case y:return p;case g:return l;case m:return h;case v:return f;case b:return d}return e}),t.exports=_},7801:t=>{t.exports=function(t,e){return null==t?void 0:t[e]}},222:(t,e,n)=>{var r=n(1811),i=n(5694),a=n(1469),o=n(5776),s=n(1780),c=n(327);t.exports=function(t,e,n){for(var u=-1,l=(e=r(e,t)).length,h=!1;++u<l;){var f=c(e[u]);if(!(h=null!=t&&n(t,f)))break;t=t[f]}return h||++u!=l?h:!!(l=null==t?0:t.length)&&s(l)&&o(f,l)&&(a(t)||i(t))}},2689:t=>{var e=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");t.exports=function(t){return e.test(t)}},1789:(t,e,n)=>{var r=n(4536);t.exports=function(){this.__data__=r?r(null):{},this.size=0}},401:t=>{t.exports=function(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}},7667:(t,e,n)=>{var r=n(4536),i=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;if(r){var n=e[t];return"__lodash_hash_undefined__"===n?void 0:n}return i.call(e,t)?e[t]:void 0}},1327:(t,e,n)=>{var r=n(4536),i=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;return r?void 0!==e[t]:i.call(e,t)}},1866:(t,e,n)=>{var r=n(4536);t.exports=function(t,e){var n=this.__data__;return this.size+=this.has(t)?0:1,n[t]=r&&void 0===e?"__lodash_hash_undefined__":e,this}},3824:t=>{var e=Object.prototype.hasOwnProperty;t.exports=function(t){var n=t.length,r=new t.constructor(n);return n&&"string"==typeof t[0]&&e.call(t,"index")&&(r.index=t.index,r.input=t.input),r}},9148:(t,e,n)=>{var r=n(4318),i=n(7157),a=n(3147),o=n(419),s=n(7133);t.exports=function(t,e,n){var c=t.constructor;switch(e){case"[object ArrayBuffer]":return r(t);case"[object Boolean]":case"[object Date]":return new c(+t);case"[object DataView]":return i(t,n);case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":case"[object Uint16Array]":case"[object Uint32Array]":return s(t,n);case"[object Map]":case"[object Set]":return new c;case"[object Number]":case"[object String]":return new c(t);case"[object RegExp]":return a(t);case"[object Symbol]":return o(t)}}},8517:(t,e,n)=>{var r=n(3118),i=n(5924),a=n(5726);t.exports=function(t){return"function"!=typeof t.constructor||a(t)?{}:r(i(t))}},7285:(t,e,n)=>{var r=n(2705),i=n(5694),a=n(1469),o=r?r.isConcatSpreadable:void 0;t.exports=function(t){return a(t)||i(t)||!!(o&&t&&t[o])}},5776:t=>{var e=/^(?:0|[1-9]\d*)$/;t.exports=function(t,n){var r=typeof t;return!!(n=null==n?9007199254740991:n)&&("number"==r||"symbol"!=r&&e.test(t))&&t>-1&&t%1==0&&t<n}},6612:(t,e,n)=>{var r=n(7813),i=n(8612),a=n(5776),o=n(3218);t.exports=function(t,e,n){if(!o(n))return!1;var s=typeof e;return!!("number"==s?i(n)&&a(e,n.length):"string"==s&&e in n)&&r(n[e],t)}},5403:(t,e,n)=>{var r=n(1469),i=n(3448),a=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,o=/^\w*$/;t.exports=function(t,e){if(r(t))return!1;var n=typeof t;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=t&&!i(t))||o.test(t)||!a.test(t)||null!=e&&t in Object(e)}},7019:t=>{t.exports=function(t){var e=typeof t;return"string"==e||"number"==e||"symbol"==e||"boolean"==e?"__proto__"!==t:null===t}},5346:(t,e,n)=>{var r,i=n(4429),a=(r=/[^.]+$/.exec(i&&i.keys&&i.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"";t.exports=function(t){return!!a&&a in t}},5726:t=>{var e=Object.prototype;t.exports=function(t){var n=t&&t.constructor;return t===("function"==typeof n&&n.prototype||e)}},9162:(t,e,n)=>{var r=n(3218);t.exports=function(t){return t==t&&!r(t)}},7040:t=>{t.exports=function(){this.__data__=[],this.size=0}},2188:(t,e,n)=>{var r=n(8470),i=Array.prototype.splice;t.exports=function(t){var e=this.__data__,n=r(e,t);return!(n<0||(n==e.length-1?e.pop():i.call(e,n,1),--this.size,0))}},2117:(t,e,n)=>{var r=n(8470);t.exports=function(t){var e=this.__data__,n=r(e,t);return n<0?void 0:e[n][1]}},7518:(t,e,n)=>{var r=n(8470);t.exports=function(t){return r(this.__data__,t)>-1}},4705:(t,e,n)=>{var r=n(8470);t.exports=function(t,e){var n=this.__data__,i=r(n,t);return i<0?(++this.size,n.push([t,e])):n[i][1]=e,this}},4785:(t,e,n)=>{var r=n(1989),i=n(8407),a=n(7071);t.exports=function(){this.size=0,this.__data__={hash:new r,map:new(a||i),string:new r}}},1285:(t,e,n)=>{var r=n(5050);t.exports=function(t){var e=r(this,t).delete(t);return this.size-=e?1:0,e}},6e3:(t,e,n)=>{var r=n(5050);t.exports=function(t){return r(this,t).get(t)}},9916:(t,e,n)=>{var r=n(5050);t.exports=function(t){return r(this,t).has(t)}},5265:(t,e,n)=>{var r=n(5050);t.exports=function(t,e){var n=r(this,t),i=n.size;return n.set(t,e),this.size+=n.size==i?0:1,this}},8776:t=>{t.exports=function(t){var e=-1,n=Array(t.size);return t.forEach((function(t,r){n[++e]=[r,t]})),n}},2634:t=>{t.exports=function(t,e){return function(n){return null!=n&&n[t]===e&&(void 0!==e||t in Object(n))}}},4523:(t,e,n)=>{var r=n(8306);t.exports=function(t){var e=r(t,(function(t){return 500===n.size&&n.clear(),t})),n=e.cache;return e}},4536:(t,e,n)=>{var r=n(852)(Object,"create");t.exports=r},6916:(t,e,n)=>{var r=n(5569)(Object.keys,Object);t.exports=r},3498:t=>{t.exports=function(t){var e=[];if(null!=t)for(var n in Object(t))e.push(n);return e}},1167:(t,e,n)=>{t=n.nmd(t);var r=n(1957),i=e&&!e.nodeType&&e,a=i&&t&&!t.nodeType&&t,o=a&&a.exports===i&&r.process,s=function(){try{return a&&a.require&&a.require("util").types||o&&o.binding&&o.binding("util")}catch(t){}}();t.exports=s},2333:t=>{var e=Object.prototype.toString;t.exports=function(t){return e.call(t)}},5569:t=>{t.exports=function(t,e){return function(n){return t(e(n))}}},5357:(t,e,n)=>{var r=n(6874),i=Math.max;t.exports=function(t,e,n){return e=i(void 0===e?t.length-1:e,0),function(){for(var a=arguments,o=-1,s=i(a.length-e,0),c=Array(s);++o<s;)c[o]=a[e+o];o=-1;for(var u=Array(e+1);++o<e;)u[o]=a[o];return u[e]=n(c),r(t,this,u)}}},5639:(t,e,n)=>{var r=n(1957),i="object"==typeof self&&self&&self.Object===Object&&self,a=r||i||Function("return this")();t.exports=a},6390:t=>{t.exports=function(t,e){if(("constructor"!==e||"function"!=typeof t[e])&&"__proto__"!=e)return t[e]}},619:t=>{t.exports=function(t){return this.__data__.set(t,"__lodash_hash_undefined__"),this}},2385:t=>{t.exports=function(t){return this.__data__.has(t)}},1814:t=>{t.exports=function(t){var e=-1,n=Array(t.size);return t.forEach((function(t){n[++e]=t})),n}},61:(t,e,n)=>{var r=n(6560),i=n(1275)(r);t.exports=i},1275:t=>{var e=Date.now;t.exports=function(t){var n=0,r=0;return function(){var i=e(),a=16-(i-r);if(r=i,a>0){if(++n>=800)return arguments[0]}else n=0;return t.apply(void 0,arguments)}}},7465:(t,e,n)=>{var r=n(8407);t.exports=function(){this.__data__=new r,this.size=0}},3779:t=>{t.exports=function(t){var e=this.__data__,n=e.delete(t);return this.size=e.size,n}},7599:t=>{t.exports=function(t){return this.__data__.get(t)}},4758:t=>{t.exports=function(t){return this.__data__.has(t)}},4309:(t,e,n)=>{var r=n(8407),i=n(7071),a=n(3369);t.exports=function(t,e){var n=this.__data__;if(n instanceof r){var o=n.__data__;if(!i||o.length<199)return o.push([t,e]),this.size=++n.size,this;n=this.__data__=new a(o)}return n.set(t,e),this.size=n.size,this}},2351:t=>{t.exports=function(t,e,n){for(var r=n-1,i=t.length;++r<i;)if(t[r]===e)return r;return-1}},8016:(t,e,n)=>{var r=n(8983),i=n(2689),a=n(1903);t.exports=function(t){return i(t)?a(t):r(t)}},5514:(t,e,n)=>{var r=n(4523),i=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,a=/\\(\\)?/g,o=r((function(t){var e=[];return 46===t.charCodeAt(0)&&e.push(""),t.replace(i,(function(t,n,r,i){e.push(r?i.replace(a,"$1"):n||t)})),e}));t.exports=o},327:(t,e,n)=>{var r=n(3448);t.exports=function(t){if("string"==typeof t||r(t))return t;var e=t+"";return"0"==e&&1/t==-1/0?"-0":e}},346:t=>{var e=Function.prototype.toString;t.exports=function(t){if(null!=t){try{return e.call(t)}catch(t){}try{return t+""}catch(t){}}return""}},7990:t=>{var e=/\s/;t.exports=function(t){for(var n=t.length;n--&&e.test(t.charAt(n)););return n}},1903:t=>{var e="[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]",n="\\ud83c[\\udffb-\\udfff]",r="[^\\ud800-\\udfff]",i="(?:\\ud83c[\\udde6-\\uddff]){2}",a="[\\ud800-\\udbff][\\udc00-\\udfff]",o="(?:"+e+"|"+n+")?",s="[\\ufe0e\\ufe0f]?",c=s+o+"(?:\\u200d(?:"+[r,i,a].join("|")+")"+s+o+")*",u="(?:"+[r+e+"?",e,i,a,"[\\ud800-\\udfff]"].join("|")+")",l=RegExp(n+"(?="+n+")|"+u+c,"g");t.exports=function(t){for(var e=l.lastIndex=0;l.test(t);)++e;return e}},6678:(t,e,n)=>{var r=n(5990);t.exports=function(t){return r(t,4)}},361:(t,e,n)=>{var r=n(5990);t.exports=function(t){return r(t,5)}},5703:t=>{t.exports=function(t){return function(){return t}}},1747:(t,e,n)=>{var r=n(5976),i=n(7813),a=n(6612),o=n(1704),s=Object.prototype,c=s.hasOwnProperty,u=r((function(t,e){t=Object(t);var n=-1,r=e.length,u=r>2?e[2]:void 0;for(u&&a(e[0],e[1],u)&&(r=1);++n<r;)for(var l=e[n],h=o(l),f=-1,d=h.length;++f<d;){var p=h[f],y=t[p];(void 0===y||i(y,s[p])&&!c.call(t,p))&&(t[p]=l[p])}return t}));t.exports=u},6073:(t,e,n)=>{t.exports=n(4486)},7813:t=>{t.exports=function(t,e){return t===e||t!=t&&e!=e}},3105:(t,e,n)=>{var r=n(4963),i=n(760),a=n(7206),o=n(1469);t.exports=function(t,e){return(o(t)?r:i)(t,a(e,3))}},3311:(t,e,n)=>{var r=n(7740)(n(998));t.exports=r},998:(t,e,n)=>{var r=n(1848),i=n(7206),a=n(554),o=Math.max;t.exports=function(t,e,n){var s=null==t?0:t.length;if(!s)return-1;var c=null==n?0:a(n);return c<0&&(c=o(s+c,0)),r(t,i(e,3),c)}},5564:(t,e,n)=>{var r=n(1078);t.exports=function(t){return null!=t&&t.length?r(t,1):[]}},4486:(t,e,n)=>{var r=n(7412),i=n(9881),a=n(4290),o=n(1469);t.exports=function(t,e){return(o(t)?r:i)(t,a(e))}},2620:(t,e,n)=>{var r=n(8483),i=n(4290),a=n(1704);t.exports=function(t,e){return null==t?t:r(t,i(e),a)}},1917:(t,e,n)=>{var r=n(7786);t.exports=function(t,e,n){var i=null==t?void 0:r(t,e);return void 0===i?n:i}},8721:(t,e,n)=>{var r=n(8565),i=n(222);t.exports=function(t,e){return null!=t&&i(t,e,r)}},9095:(t,e,n)=>{var r=n(13),i=n(222);t.exports=function(t,e){return null!=t&&i(t,e,r)}},6557:t=>{t.exports=function(t){return t}},5694:(t,e,n)=>{var r=n(9454),i=n(7005),a=Object.prototype,o=a.hasOwnProperty,s=a.propertyIsEnumerable,c=r(function(){return arguments}())?r:function(t){return i(t)&&o.call(t,"callee")&&!s.call(t,"callee")};t.exports=c},1469:t=>{var e=Array.isArray;t.exports=e},8612:(t,e,n)=>{var r=n(3560),i=n(1780);t.exports=function(t){return null!=t&&i(t.length)&&!r(t)}},9246:(t,e,n)=>{var r=n(8612),i=n(7005);t.exports=function(t){return i(t)&&r(t)}},4144:(t,e,n)=>{t=n.nmd(t);var r=n(5639),i=n(5062),a=e&&!e.nodeType&&e,o=a&&t&&!t.nodeType&&t,s=o&&o.exports===a?r.Buffer:void 0,c=(s?s.isBuffer:void 0)||i;t.exports=c},1609:(t,e,n)=>{var r=n(280),i=n(4160),a=n(5694),o=n(1469),s=n(8612),c=n(4144),u=n(5726),l=n(6719),h=Object.prototype.hasOwnProperty;t.exports=function(t){if(null==t)return!0;if(s(t)&&(o(t)||"string"==typeof t||"function"==typeof t.splice||c(t)||l(t)||a(t)))return!t.length;var e=i(t);if("[object Map]"==e||"[object Set]"==e)return!t.size;if(u(t))return!r(t).length;for(var n in t)if(h.call(t,n))return!1;return!0}},3560:(t,e,n)=>{var r=n(4239),i=n(3218);t.exports=function(t){if(!i(t))return!1;var e=r(t);return"[object Function]"==e||"[object GeneratorFunction]"==e||"[object AsyncFunction]"==e||"[object Proxy]"==e}},1780:t=>{t.exports=function(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=9007199254740991}},6688:(t,e,n)=>{var r=n(5588),i=n(1717),a=n(1167),o=a&&a.isMap,s=o?i(o):r;t.exports=s},3218:t=>{t.exports=function(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}},7005:t=>{t.exports=function(t){return null!=t&&"object"==typeof t}},8630:(t,e,n)=>{var r=n(4239),i=n(5924),a=n(7005),o=Function.prototype,s=Object.prototype,c=o.toString,u=s.hasOwnProperty,l=c.call(Object);t.exports=function(t){if(!a(t)||"[object Object]"!=r(t))return!1;var e=i(t);if(null===e)return!0;var n=u.call(e,"constructor")&&e.constructor;return"function"==typeof n&&n instanceof n&&c.call(n)==l}},2928:(t,e,n)=>{var r=n(9221),i=n(1717),a=n(1167),o=a&&a.isSet,s=o?i(o):r;t.exports=s},7037:(t,e,n)=>{var r=n(4239),i=n(1469),a=n(7005);t.exports=function(t){return"string"==typeof t||!i(t)&&a(t)&&"[object String]"==r(t)}},3448:(t,e,n)=>{var r=n(4239),i=n(7005);t.exports=function(t){return"symbol"==typeof t||i(t)&&"[object Symbol]"==r(t)}},6719:(t,e,n)=>{var r=n(8749),i=n(1717),a=n(1167),o=a&&a.isTypedArray,s=o?i(o):r;t.exports=s},2353:t=>{t.exports=function(t){return void 0===t}},3674:(t,e,n)=>{var r=n(4636),i=n(280),a=n(8612);t.exports=function(t){return a(t)?r(t):i(t)}},1704:(t,e,n)=>{var r=n(4636),i=n(313),a=n(8612);t.exports=function(t){return a(t)?r(t,!0):i(t)}},928:t=>{t.exports=function(t){var e=null==t?0:t.length;return e?t[e-1]:void 0}},5161:(t,e,n)=>{var r=n(9932),i=n(7206),a=n(9199),o=n(1469);t.exports=function(t,e){return(o(t)?r:a)(t,i(e,3))}},6604:(t,e,n)=>{var r=n(9465),i=n(7816),a=n(7206);t.exports=function(t,e){var n={};return e=a(e,3),i(t,(function(t,i,a){r(n,i,e(t,i,a))})),n}},6162:(t,e,n)=>{var r=n(6029),i=n(3325),a=n(6557);t.exports=function(t){return t&&t.length?r(t,a,i):void 0}},8306:(t,e,n)=>{var r=n(3369);function i(t,e){if("function"!=typeof t||null!=e&&"function"!=typeof e)throw new TypeError("Expected a function");var n=function(){var r=arguments,i=e?e.apply(this,r):r[0],a=n.cache;if(a.has(i))return a.get(i);var o=t.apply(this,r);return n.cache=a.set(i,o)||a,o};return n.cache=new(i.Cache||r),n}i.Cache=r,t.exports=i},3857:(t,e,n)=>{var r=n(2980),i=n(1750)((function(t,e,n){r(t,e,n)}));t.exports=i},3632:(t,e,n)=>{var r=n(6029),i=n(433),a=n(6557);t.exports=function(t){return t&&t.length?r(t,a,i):void 0}},2762:(t,e,n)=>{var r=n(6029),i=n(7206),a=n(433);t.exports=function(t,e){return t&&t.length?r(t,i(e,2),a):void 0}},308:t=>{t.exports=function(){}},7771:(t,e,n)=>{var r=n(5639);t.exports=function(){return r.Date.now()}},9722:(t,e,n)=>{var r=n(5970),i=n(9021)((function(t,e){return null==t?{}:r(t,e)}));t.exports=i},9601:(t,e,n)=>{var r=n(371),i=n(9152),a=n(5403),o=n(327);t.exports=function(t){return a(t)?r(o(t)):i(t)}},6026:(t,e,n)=>{var r=n(7445)();t.exports=r},4061:(t,e,n)=>{var r=n(2663),i=n(9881),a=n(7206),o=n(107),s=n(1469);t.exports=function(t,e,n){var c=s(t)?r:o,u=arguments.length<3;return c(t,a(e,4),n,u,i)}},4238:(t,e,n)=>{var r=n(280),i=n(4160),a=n(8612),o=n(7037),s=n(8016);t.exports=function(t){if(null==t)return 0;if(a(t))return o(t)?s(t):t.length;var e=i(t);return"[object Map]"==e||"[object Set]"==e?t.size:r(t).length}},9734:(t,e,n)=>{var r=n(1078),i=n(9556),a=n(5976),o=n(6612),s=a((function(t,e){if(null==t)return[];var n=e.length;return n>1&&o(t,e[0],e[1])?e=[]:n>2&&o(e[0],e[1],e[2])&&(e=[e[0]]),i(t,r(e,1),[])}));t.exports=s},479:t=>{t.exports=function(){return[]}},5062:t=>{t.exports=function(){return!1}},8601:(t,e,n)=>{var r=n(4841);t.exports=function(t){return t?Infinity===(t=r(t))||t===-1/0?17976931348623157e292*(t<0?-1:1):t==t?t:0:0===t?t:0}},554:(t,e,n)=>{var r=n(8601);t.exports=function(t){var e=r(t),n=e%1;return e==e?n?e-n:e:0}},4841:(t,e,n)=>{var r=n(7561),i=n(3218),a=n(3448),o=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,c=/^0o[0-7]+$/i,u=parseInt;t.exports=function(t){if("number"==typeof t)return t;if(a(t))return NaN;if(i(t)){var e="function"==typeof t.valueOf?t.valueOf():t;t=i(e)?e+"":e}if("string"!=typeof t)return 0===t?t:+t;t=r(t);var n=s.test(t);return n||c.test(t)?u(t.slice(2),n?2:8):o.test(t)?NaN:+t}},3678:(t,e,n)=>{var r=n(8363),i=n(1704);t.exports=function(t){return r(t,i(t))}},9833:(t,e,n)=>{var r=n(531);t.exports=function(t){return null==t?"":r(t)}},8718:(t,e,n)=>{var r=n(7412),i=n(3118),a=n(7816),o=n(7206),s=n(5924),c=n(1469),u=n(4144),l=n(3560),h=n(3218),f=n(6719);t.exports=function(t,e,n){var d=c(t),p=d||u(t)||f(t);if(e=o(e,4),null==n){var y=t&&t.constructor;n=p?d?new y:[]:h(t)&&l(y)?i(s(t)):{}}return(p?r:a)(t,(function(t,r,i){return e(n,t,r,i)})),n}},3386:(t,e,n)=>{var r=n(1078),i=n(5976),a=n(5652),o=n(9246),s=i((function(t){return a(r(t,1,o,!0))}));t.exports=s},3955:(t,e,n)=>{var r=n(9833),i=0;t.exports=function(t){var e=++i;return r(t)+e}},2628:(t,e,n)=>{var r=n(7415),i=n(3674);t.exports=function(t){return null==t?[]:r(t,i(t))}},7287:(t,e,n)=>{var r=n(4865),i=n(1757);t.exports=function(t,e){return i(t||[],e||[],r)}},9234:()=>{},1748:(t,e,n)=>{var r={"./locale":9234,"./locale.js":9234};function i(t){var e=a(t);return n(e)}function a(t){if(!n.o(r,t)){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}return r[t]}i.keys=function(){return Object.keys(r)},i.resolve=a,t.exports=i,i.id=1748},1941:function(t,e,n){(t=n.nmd(t)).exports=function(){"use strict";var e,r;function i(){return e.apply(null,arguments)}function a(t){return t instanceof Array||"[object Array]"===Object.prototype.toString.call(t)}function o(t){return null!=t&&"[object Object]"===Object.prototype.toString.call(t)}function s(t){return void 0===t}function c(t){return"number"==typeof t||"[object Number]"===Object.prototype.toString.call(t)}function u(t){return t instanceof Date||"[object Date]"===Object.prototype.toString.call(t)}function l(t,e){var n,r=[];for(n=0;n<t.length;++n)r.push(e(t[n],n));return r}function h(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function f(t,e){for(var n in e)h(e,n)&&(t[n]=e[n]);return h(e,"toString")&&(t.toString=e.toString),h(e,"valueOf")&&(t.valueOf=e.valueOf),t}function d(t,e,n,r){return be(t,e,n,r,!0).utc()}function p(t){return null==t._pf&&(t._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),t._pf}function y(t){if(null==t._isValid){var e=p(t),n=r.call(e.parsedDateParts,(function(t){return null!=t})),i=!isNaN(t._d.getTime())&&e.overflow<0&&!e.empty&&!e.invalidMonth&&!e.invalidWeekday&&!e.weekdayMismatch&&!e.nullInput&&!e.invalidFormat&&!e.userInvalidated&&(!e.meridiem||e.meridiem&&n);if(t._strict&&(i=i&&0===e.charsLeftOver&&0===e.unusedTokens.length&&void 0===e.bigHour),null!=Object.isFrozen&&Object.isFrozen(t))return i;t._isValid=i}return t._isValid}function g(t){var e=d(NaN);return null!=t?f(p(e),t):p(e).userInvalidated=!0,e}r=Array.prototype.some?Array.prototype.some:function(t){for(var e=Object(this),n=e.length>>>0,r=0;r<n;r++)if(r in e&&t.call(this,e[r],r,e))return!0;return!1};var m=i.momentProperties=[];function v(t,e){var n,r,i;if(s(e._isAMomentObject)||(t._isAMomentObject=e._isAMomentObject),s(e._i)||(t._i=e._i),s(e._f)||(t._f=e._f),s(e._l)||(t._l=e._l),s(e._strict)||(t._strict=e._strict),s(e._tzm)||(t._tzm=e._tzm),s(e._isUTC)||(t._isUTC=e._isUTC),s(e._offset)||(t._offset=e._offset),s(e._pf)||(t._pf=p(e)),s(e._locale)||(t._locale=e._locale),0<m.length)for(n=0;n<m.length;n++)s(i=e[r=m[n]])||(t[r]=i);return t}var b=!1;function _(t){v(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===b&&(b=!0,i.updateOffset(this),b=!1)}function x(t){return t instanceof _||null!=t&&null!=t._isAMomentObject}function w(t){return t<0?Math.ceil(t)||0:Math.floor(t)}function k(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=w(e)),n}function T(t,e,n){var r,i=Math.min(t.length,e.length),a=Math.abs(t.length-e.length),o=0;for(r=0;r<i;r++)(n&&t[r]!==e[r]||!n&&k(t[r])!==k(e[r]))&&o++;return o+a}function E(t){!1===i.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function C(t,e){var n=!0;return f((function(){if(null!=i.deprecationHandler&&i.deprecationHandler(null,t),n){for(var r,a=[],o=0;o<arguments.length;o++){if(r="","object"==typeof arguments[o]){for(var s in r+="\n["+o+"] ",arguments[0])r+=s+": "+arguments[0][s]+", ";r=r.slice(0,-2)}else r=arguments[o];a.push(r)}E(t+"\nArguments: "+Array.prototype.slice.call(a).join("")+"\n"+(new Error).stack),n=!1}return e.apply(this,arguments)}),e)}var S,A={};function M(t,e){null!=i.deprecationHandler&&i.deprecationHandler(t,e),A[t]||(E(e),A[t]=!0)}function N(t){return t instanceof Function||"[object Function]"===Object.prototype.toString.call(t)}function D(t,e){var n,r=f({},t);for(n in e)h(e,n)&&(o(t[n])&&o(e[n])?(r[n]={},f(r[n],t[n]),f(r[n],e[n])):null!=e[n]?r[n]=e[n]:delete r[n]);for(n in t)h(t,n)&&!h(e,n)&&o(t[n])&&(r[n]=f({},r[n]));return r}function O(t){null!=t&&this.set(t)}i.suppressDeprecationWarnings=!1,i.deprecationHandler=null,S=Object.keys?Object.keys:function(t){var e,n=[];for(e in t)h(t,e)&&n.push(e);return n};var B={};function L(t,e){var n=t.toLowerCase();B[n]=B[n+"s"]=B[e]=t}function I(t){return"string"==typeof t?B[t]||B[t.toLowerCase()]:void 0}function R(t){var e,n,r={};for(n in t)h(t,n)&&(e=I(n))&&(r[e]=t[n]);return r}var F={};function P(t,e){F[t]=e}function j(t,e,n){var r=""+Math.abs(t),i=e-r.length;return(0<=t?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}var Y=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,z=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,U={},q={};function H(t,e,n,r){var i=r;"string"==typeof r&&(i=function(){return this[r]()}),t&&(q[t]=i),e&&(q[e[0]]=function(){return j(i.apply(this,arguments),e[1],e[2])}),n&&(q[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),t)})}function $(t,e){return t.isValid()?(e=W(e,t.localeData()),U[e]=U[e]||function(t){var e,n,r,i=t.match(Y);for(e=0,n=i.length;e<n;e++)q[i[e]]?i[e]=q[i[e]]:i[e]=(r=i[e]).match(/\[[\s\S]/)?r.replace(/^\[|\]$/g,""):r.replace(/\\/g,"");return function(e){var r,a="";for(r=0;r<n;r++)a+=N(i[r])?i[r].call(e,t):i[r];return a}}(e),U[e](t)):t.localeData().invalidDate()}function W(t,e){var n=5;function r(t){return e.longDateFormat(t)||t}for(z.lastIndex=0;0<=n&&z.test(t);)t=t.replace(z,r),z.lastIndex=0,n-=1;return t}var V=/\d/,G=/\d\d/,X=/\d{3}/,Z=/\d{4}/,Q=/[+-]?\d{6}/,K=/\d\d?/,J=/\d\d\d\d?/,tt=/\d\d\d\d\d\d?/,et=/\d{1,3}/,nt=/\d{1,4}/,rt=/[+-]?\d{1,6}/,it=/\d+/,at=/[+-]?\d+/,ot=/Z|[+-]\d\d:?\d\d/gi,st=/Z|[+-]\d\d(?::?\d\d)?/gi,ct=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,ut={};function lt(t,e,n){ut[t]=N(e)?e:function(t,r){return t&&n?n:e}}function ht(t,e){return h(ut,t)?ut[t](e._strict,e._locale):new RegExp(ft(t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,(function(t,e,n,r,i){return e||n||r||i}))))}function ft(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var dt={};function pt(t,e){var n,r=e;for("string"==typeof t&&(t=[t]),c(e)&&(r=function(t,n){n[e]=k(t)}),n=0;n<t.length;n++)dt[t[n]]=r}function yt(t,e){pt(t,(function(t,n,r,i){r._w=r._w||{},e(t,r._w,r,i)}))}function gt(t){return mt(t)?366:365}function mt(t){return t%4==0&&t%100!=0||t%400==0}H("Y",0,0,(function(){var t=this.year();return t<=9999?""+t:"+"+t})),H(0,["YY",2],0,(function(){return this.year()%100})),H(0,["YYYY",4],0,"year"),H(0,["YYYYY",5],0,"year"),H(0,["YYYYYY",6,!0],0,"year"),L("year","y"),P("year",1),lt("Y",at),lt("YY",K,G),lt("YYYY",nt,Z),lt("YYYYY",rt,Q),lt("YYYYYY",rt,Q),pt(["YYYYY","YYYYYY"],0),pt("YYYY",(function(t,e){e[0]=2===t.length?i.parseTwoDigitYear(t):k(t)})),pt("YY",(function(t,e){e[0]=i.parseTwoDigitYear(t)})),pt("Y",(function(t,e){e[0]=parseInt(t,10)})),i.parseTwoDigitYear=function(t){return k(t)+(68<k(t)?1900:2e3)};var vt,bt=_t("FullYear",!0);function _t(t,e){return function(n){return null!=n?(wt(this,t,n),i.updateOffset(this,e),this):xt(this,t)}}function xt(t,e){return t.isValid()?t._d["get"+(t._isUTC?"UTC":"")+e]():NaN}function wt(t,e,n){t.isValid()&&!isNaN(n)&&("FullYear"===e&&mt(t.year())&&1===t.month()&&29===t.date()?t._d["set"+(t._isUTC?"UTC":"")+e](n,t.month(),kt(n,t.month())):t._d["set"+(t._isUTC?"UTC":"")+e](n))}function kt(t,e){if(isNaN(t)||isNaN(e))return NaN;var n=(e%12+12)%12;return t+=(e-n)/12,1===n?mt(t)?29:28:31-n%7%2}vt=Array.prototype.indexOf?Array.prototype.indexOf:function(t){var e;for(e=0;e<this.length;++e)if(this[e]===t)return e;return-1},H("M",["MM",2],"Mo",(function(){return this.month()+1})),H("MMM",0,0,(function(t){return this.localeData().monthsShort(this,t)})),H("MMMM",0,0,(function(t){return this.localeData().months(this,t)})),L("month","M"),P("month",8),lt("M",K),lt("MM",K,G),lt("MMM",(function(t,e){return e.monthsShortRegex(t)})),lt("MMMM",(function(t,e){return e.monthsRegex(t)})),pt(["M","MM"],(function(t,e){e[1]=k(t)-1})),pt(["MMM","MMMM"],(function(t,e,n,r){var i=n._locale.monthsParse(t,r,n._strict);null!=i?e[1]=i:p(n).invalidMonth=t}));var Tt=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Et="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Ct="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_");function St(t,e){var n;if(!t.isValid())return t;if("string"==typeof e)if(/^\d+$/.test(e))e=k(e);else if(!c(e=t.localeData().monthsParse(e)))return t;return n=Math.min(t.date(),kt(t.year(),e)),t._d["set"+(t._isUTC?"UTC":"")+"Month"](e,n),t}function At(t){return null!=t?(St(this,t),i.updateOffset(this,!0),this):xt(this,"Month")}var Mt=ct,Nt=ct;function Dt(){function t(t,e){return e.length-t.length}var e,n,r=[],i=[],a=[];for(e=0;e<12;e++)n=d([2e3,e]),r.push(this.monthsShort(n,"")),i.push(this.months(n,"")),a.push(this.months(n,"")),a.push(this.monthsShort(n,""));for(r.sort(t),i.sort(t),a.sort(t),e=0;e<12;e++)r[e]=ft(r[e]),i[e]=ft(i[e]);for(e=0;e<24;e++)a[e]=ft(a[e]);this._monthsRegex=new RegExp("^("+a.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+r.join("|")+")","i")}function Ot(t){var e;if(t<100&&0<=t){var n=Array.prototype.slice.call(arguments);n[0]=t+400,e=new Date(Date.UTC.apply(null,n)),isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t)}else e=new Date(Date.UTC.apply(null,arguments));return e}function Bt(t,e,n){var r=7+e-n;return-(7+Ot(t,0,r).getUTCDay()-e)%7+r-1}function Lt(t,e,n,r,i){var a,o,s=1+7*(e-1)+(7+n-r)%7+Bt(t,r,i);return o=s<=0?gt(a=t-1)+s:s>gt(t)?(a=t+1,s-gt(t)):(a=t,s),{year:a,dayOfYear:o}}function It(t,e,n){var r,i,a=Bt(t.year(),e,n),o=Math.floor((t.dayOfYear()-a-1)/7)+1;return o<1?r=o+Rt(i=t.year()-1,e,n):o>Rt(t.year(),e,n)?(r=o-Rt(t.year(),e,n),i=t.year()+1):(i=t.year(),r=o),{week:r,year:i}}function Rt(t,e,n){var r=Bt(t,e,n),i=Bt(t+1,e,n);return(gt(t)-r+i)/7}function Ft(t,e){return t.slice(e,7).concat(t.slice(0,e))}H("w",["ww",2],"wo","week"),H("W",["WW",2],"Wo","isoWeek"),L("week","w"),L("isoWeek","W"),P("week",5),P("isoWeek",5),lt("w",K),lt("ww",K,G),lt("W",K),lt("WW",K,G),yt(["w","ww","W","WW"],(function(t,e,n,r){e[r.substr(0,1)]=k(t)})),H("d",0,"do","day"),H("dd",0,0,(function(t){return this.localeData().weekdaysMin(this,t)})),H("ddd",0,0,(function(t){return this.localeData().weekdaysShort(this,t)})),H("dddd",0,0,(function(t){return this.localeData().weekdays(this,t)})),H("e",0,0,"weekday"),H("E",0,0,"isoWeekday"),L("day","d"),L("weekday","e"),L("isoWeekday","E"),P("day",11),P("weekday",11),P("isoWeekday",11),lt("d",K),lt("e",K),lt("E",K),lt("dd",(function(t,e){return e.weekdaysMinRegex(t)})),lt("ddd",(function(t,e){return e.weekdaysShortRegex(t)})),lt("dddd",(function(t,e){return e.weekdaysRegex(t)})),yt(["dd","ddd","dddd"],(function(t,e,n,r){var i=n._locale.weekdaysParse(t,r,n._strict);null!=i?e.d=i:p(n).invalidWeekday=t})),yt(["d","e","E"],(function(t,e,n,r){e[r]=k(t)}));var Pt="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),jt="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Yt="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),zt=ct,Ut=ct,qt=ct;function Ht(){function t(t,e){return e.length-t.length}var e,n,r,i,a,o=[],s=[],c=[],u=[];for(e=0;e<7;e++)n=d([2e3,1]).day(e),r=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),a=this.weekdays(n,""),o.push(r),s.push(i),c.push(a),u.push(r),u.push(i),u.push(a);for(o.sort(t),s.sort(t),c.sort(t),u.sort(t),e=0;e<7;e++)s[e]=ft(s[e]),c[e]=ft(c[e]),u[e]=ft(u[e]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+c.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function $t(){return this.hours()%12||12}function Wt(t,e){H(t,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)}))}function Vt(t,e){return e._meridiemParse}H("H",["HH",2],0,"hour"),H("h",["hh",2],0,$t),H("k",["kk",2],0,(function(){return this.hours()||24})),H("hmm",0,0,(function(){return""+$t.apply(this)+j(this.minutes(),2)})),H("hmmss",0,0,(function(){return""+$t.apply(this)+j(this.minutes(),2)+j(this.seconds(),2)})),H("Hmm",0,0,(function(){return""+this.hours()+j(this.minutes(),2)})),H("Hmmss",0,0,(function(){return""+this.hours()+j(this.minutes(),2)+j(this.seconds(),2)})),Wt("a",!0),Wt("A",!1),L("hour","h"),P("hour",13),lt("a",Vt),lt("A",Vt),lt("H",K),lt("h",K),lt("k",K),lt("HH",K,G),lt("hh",K,G),lt("kk",K,G),lt("hmm",J),lt("hmmss",tt),lt("Hmm",J),lt("Hmmss",tt),pt(["H","HH"],3),pt(["k","kk"],(function(t,e,n){var r=k(t);e[3]=24===r?0:r})),pt(["a","A"],(function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t})),pt(["h","hh"],(function(t,e,n){e[3]=k(t),p(n).bigHour=!0})),pt("hmm",(function(t,e,n){var r=t.length-2;e[3]=k(t.substr(0,r)),e[4]=k(t.substr(r)),p(n).bigHour=!0})),pt("hmmss",(function(t,e,n){var r=t.length-4,i=t.length-2;e[3]=k(t.substr(0,r)),e[4]=k(t.substr(r,2)),e[5]=k(t.substr(i)),p(n).bigHour=!0})),pt("Hmm",(function(t,e,n){var r=t.length-2;e[3]=k(t.substr(0,r)),e[4]=k(t.substr(r))})),pt("Hmmss",(function(t,e,n){var r=t.length-4,i=t.length-2;e[3]=k(t.substr(0,r)),e[4]=k(t.substr(r,2)),e[5]=k(t.substr(i))}));var Gt,Xt=_t("Hours",!0),Zt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Et,monthsShort:Ct,week:{dow:0,doy:6},weekdays:Pt,weekdaysMin:Yt,weekdaysShort:jt,meridiemParse:/[ap]\.?m?\.?/i},Qt={},Kt={};function Jt(t){return t?t.toLowerCase().replace("_","-"):t}function te(e){var r=null;if(!Qt[e]&&t&&t.exports)try{r=Gt._abbr,n(1748)("./"+e),ee(r)}catch(e){}return Qt[e]}function ee(t,e){var n;return t&&((n=s(e)?re(t):ne(t,e))?Gt=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+t+" not found. Did you forget to load it?")),Gt._abbr}function ne(t,e){if(null===e)return delete Qt[t],null;var n,r=Zt;if(e.abbr=t,null!=Qt[t])M("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),r=Qt[t]._config;else if(null!=e.parentLocale)if(null!=Qt[e.parentLocale])r=Qt[e.parentLocale]._config;else{if(null==(n=te(e.parentLocale)))return Kt[e.parentLocale]||(Kt[e.parentLocale]=[]),Kt[e.parentLocale].push({name:t,config:e}),null;r=n._config}return Qt[t]=new O(D(r,e)),Kt[t]&&Kt[t].forEach((function(t){ne(t.name,t.config)})),ee(t),Qt[t]}function re(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return Gt;if(!a(t)){if(e=te(t))return e;t=[t]}return function(t){for(var e,n,r,i,a=0;a<t.length;){for(e=(i=Jt(t[a]).split("-")).length,n=(n=Jt(t[a+1]))?n.split("-"):null;0<e;){if(r=te(i.slice(0,e).join("-")))return r;if(n&&n.length>=e&&T(i,n,!0)>=e-1)break;e--}a++}return Gt}(t)}function ie(t){var e,n=t._a;return n&&-2===p(t).overflow&&(e=n[1]<0||11<n[1]?1:n[2]<1||n[2]>kt(n[0],n[1])?2:n[3]<0||24<n[3]||24===n[3]&&(0!==n[4]||0!==n[5]||0!==n[6])?3:n[4]<0||59<n[4]?4:n[5]<0||59<n[5]?5:n[6]<0||999<n[6]?6:-1,p(t)._overflowDayOfYear&&(e<0||2<e)&&(e=2),p(t)._overflowWeeks&&-1===e&&(e=7),p(t)._overflowWeekday&&-1===e&&(e=8),p(t).overflow=e),t}function ae(t,e,n){return null!=t?t:null!=e?e:n}function oe(t){var e,n,r,a,o,s=[];if(!t._d){var c,u;for(c=t,u=new Date(i.now()),r=c._useUTC?[u.getUTCFullYear(),u.getUTCMonth(),u.getUTCDate()]:[u.getFullYear(),u.getMonth(),u.getDate()],t._w&&null==t._a[2]&&null==t._a[1]&&function(t){var e,n,r,i,a,o,s,c;if(null!=(e=t._w).GG||null!=e.W||null!=e.E)a=1,o=4,n=ae(e.GG,t._a[0],It(_e(),1,4).year),r=ae(e.W,1),((i=ae(e.E,1))<1||7<i)&&(c=!0);else{a=t._locale._week.dow,o=t._locale._week.doy;var u=It(_e(),a,o);n=ae(e.gg,t._a[0],u.year),r=ae(e.w,u.week),null!=e.d?((i=e.d)<0||6<i)&&(c=!0):null!=e.e?(i=e.e+a,(e.e<0||6<e.e)&&(c=!0)):i=a}r<1||r>Rt(n,a,o)?p(t)._overflowWeeks=!0:null!=c?p(t)._overflowWeekday=!0:(s=Lt(n,r,i,a,o),t._a[0]=s.year,t._dayOfYear=s.dayOfYear)}(t),null!=t._dayOfYear&&(o=ae(t._a[0],r[0]),(t._dayOfYear>gt(o)||0===t._dayOfYear)&&(p(t)._overflowDayOfYear=!0),n=Ot(o,0,t._dayOfYear),t._a[1]=n.getUTCMonth(),t._a[2]=n.getUTCDate()),e=0;e<3&&null==t._a[e];++e)t._a[e]=s[e]=r[e];for(;e<7;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[3]&&0===t._a[4]&&0===t._a[5]&&0===t._a[6]&&(t._nextDay=!0,t._a[3]=0),t._d=(t._useUTC?Ot:function(t,e,n,r,i,a,o){var s;return t<100&&0<=t?(s=new Date(t+400,e,n,r,i,a,o),isFinite(s.getFullYear())&&s.setFullYear(t)):s=new Date(t,e,n,r,i,a,o),s}).apply(null,s),a=t._useUTC?t._d.getUTCDay():t._d.getDay(),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[3]=24),t._w&&void 0!==t._w.d&&t._w.d!==a&&(p(t).weekdayMismatch=!0)}}var se=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ce=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ue=/Z|[+-]\d\d(?::?\d\d)?/,le=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],he=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],fe=/^\/?Date\((\-?\d+)/i;function de(t){var e,n,r,i,a,o,s=t._i,c=se.exec(s)||ce.exec(s);if(c){for(p(t).iso=!0,e=0,n=le.length;e<n;e++)if(le[e][1].exec(c[1])){i=le[e][0],r=!1!==le[e][2];break}if(null==i)return void(t._isValid=!1);if(c[3]){for(e=0,n=he.length;e<n;e++)if(he[e][1].exec(c[3])){a=(c[2]||" ")+he[e][0];break}if(null==a)return void(t._isValid=!1)}if(!r&&null!=a)return void(t._isValid=!1);if(c[4]){if(!ue.exec(c[4]))return void(t._isValid=!1);o="Z"}t._f=i+(a||"")+(o||""),me(t)}else t._isValid=!1}var pe=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;var ye={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function ge(t){var e,n,r,i=pe.exec(t._i.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));if(i){var a=function(t,e,n,r,i,a){var o=[function(t){var e=parseInt(t,10);return e<=49?2e3+e:e<=999?1900+e:e}(t),Ct.indexOf(e),parseInt(n,10),parseInt(r,10),parseInt(i,10)];return a&&o.push(parseInt(a,10)),o}(i[4],i[3],i[2],i[5],i[6],i[7]);if(n=a,r=t,(e=i[1])&&jt.indexOf(e)!==new Date(n[0],n[1],n[2]).getDay()&&(p(r).weekdayMismatch=!0,!(r._isValid=!1)))return;t._a=a,t._tzm=function(t,e,n){if(t)return ye[t];if(e)return 0;var r=parseInt(n,10),i=r%100;return(r-i)/100*60+i}(i[8],i[9],i[10]),t._d=Ot.apply(null,t._a),t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),p(t).rfc2822=!0}else t._isValid=!1}function me(t){if(t._f!==i.ISO_8601)if(t._f!==i.RFC_2822){t._a=[],p(t).empty=!0;var e,n,r,a,o,s,c,u,l=""+t._i,f=l.length,d=0;for(r=W(t._f,t._locale).match(Y)||[],e=0;e<r.length;e++)a=r[e],(n=(l.match(ht(a,t))||[])[0])&&(0<(o=l.substr(0,l.indexOf(n))).length&&p(t).unusedInput.push(o),l=l.slice(l.indexOf(n)+n.length),d+=n.length),q[a]?(n?p(t).empty=!1:p(t).unusedTokens.push(a),s=a,u=t,null!=(c=n)&&h(dt,s)&&dt[s](c,u._a,u,s)):t._strict&&!n&&p(t).unusedTokens.push(a);p(t).charsLeftOver=f-d,0<l.length&&p(t).unusedInput.push(l),t._a[3]<=12&&!0===p(t).bigHour&&0<t._a[3]&&(p(t).bigHour=void 0),p(t).parsedDateParts=t._a.slice(0),p(t).meridiem=t._meridiem,t._a[3]=function(t,e,n){var r;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):(null!=t.isPM&&((r=t.isPM(n))&&e<12&&(e+=12),r||12!==e||(e=0)),e)}(t._locale,t._a[3],t._meridiem),oe(t),ie(t)}else ge(t);else de(t)}function ve(t){var e,n,r,h,d=t._i,m=t._f;return t._locale=t._locale||re(t._l),null===d||void 0===m&&""===d?g({nullInput:!0}):("string"==typeof d&&(t._i=d=t._locale.preparse(d)),x(d)?new _(ie(d)):(u(d)?t._d=d:a(m)?function(t){var e,n,r,i,a;if(0===t._f.length)return p(t).invalidFormat=!0,t._d=new Date(NaN);for(i=0;i<t._f.length;i++)a=0,e=v({},t),null!=t._useUTC&&(e._useUTC=t._useUTC),e._f=t._f[i],me(e),y(e)&&(a+=p(e).charsLeftOver,a+=10*p(e).unusedTokens.length,p(e).score=a,(null==r||a<r)&&(r=a,n=e));f(t,n||e)}(t):m?me(t):s(n=(e=t)._i)?e._d=new Date(i.now()):u(n)?e._d=new Date(n.valueOf()):"string"==typeof n?(r=e,null===(h=fe.exec(r._i))?(de(r),!1===r._isValid&&(delete r._isValid,ge(r),!1===r._isValid&&(delete r._isValid,i.createFromInputFallback(r)))):r._d=new Date(+h[1])):a(n)?(e._a=l(n.slice(0),(function(t){return parseInt(t,10)})),oe(e)):o(n)?function(t){if(!t._d){var e=R(t._i);t._a=l([e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],(function(t){return t&&parseInt(t,10)})),oe(t)}}(e):c(n)?e._d=new Date(n):i.createFromInputFallback(e),y(t)||(t._d=null),t))}function be(t,e,n,r,i){var s,c={};return!0!==n&&!1!==n||(r=n,n=void 0),(o(t)&&function(t){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(t).length;var e;for(e in t)if(t.hasOwnProperty(e))return!1;return!0}(t)||a(t)&&0===t.length)&&(t=void 0),c._isAMomentObject=!0,c._useUTC=c._isUTC=i,c._l=n,c._i=t,c._f=e,c._strict=r,(s=new _(ie(ve(c))))._nextDay&&(s.add(1,"d"),s._nextDay=void 0),s}function _e(t,e,n,r){return be(t,e,n,r,!1)}i.createFromInputFallback=C("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",(function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))})),i.ISO_8601=function(){},i.RFC_2822=function(){};var xe=C("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=_e.apply(null,arguments);return this.isValid()&&t.isValid()?t<this?this:t:g()})),we=C("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=_e.apply(null,arguments);return this.isValid()&&t.isValid()?this<t?this:t:g()}));function ke(t,e){var n,r;if(1===e.length&&a(e[0])&&(e=e[0]),!e.length)return _e();for(n=e[0],r=1;r<e.length;++r)e[r].isValid()&&!e[r][t](n)||(n=e[r]);return n}var Te=["year","quarter","month","week","day","hour","minute","second","millisecond"];function Ee(t){var e=R(t),n=e.year||0,r=e.quarter||0,i=e.month||0,a=e.week||e.isoWeek||0,o=e.day||0,s=e.hour||0,c=e.minute||0,u=e.second||0,l=e.millisecond||0;this._isValid=function(t){for(var e in t)if(-1===vt.call(Te,e)||null!=t[e]&&isNaN(t[e]))return!1;for(var n=!1,r=0;r<Te.length;++r)if(t[Te[r]]){if(n)return!1;parseFloat(t[Te[r]])!==k(t[Te[r]])&&(n=!0)}return!0}(e),this._milliseconds=+l+1e3*u+6e4*c+1e3*s*60*60,this._days=+o+7*a,this._months=+i+3*r+12*n,this._data={},this._locale=re(),this._bubble()}function Ce(t){return t instanceof Ee}function Se(t){return t<0?-1*Math.round(-1*t):Math.round(t)}function Ae(t,e){H(t,0,0,(function(){var t=this.utcOffset(),n="+";return t<0&&(t=-t,n="-"),n+j(~~(t/60),2)+e+j(~~t%60,2)}))}Ae("Z",":"),Ae("ZZ",""),lt("Z",st),lt("ZZ",st),pt(["Z","ZZ"],(function(t,e,n){n._useUTC=!0,n._tzm=Ne(st,t)}));var Me=/([\+\-]|\d\d)/gi;function Ne(t,e){var n=(e||"").match(t);if(null===n)return null;var r=((n[n.length-1]||[])+"").match(Me)||["-",0,0],i=60*r[1]+k(r[2]);return 0===i?0:"+"===r[0]?i:-i}function De(t,e){var n,r;return e._isUTC?(n=e.clone(),r=(x(t)||u(t)?t.valueOf():_e(t).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+r),i.updateOffset(n,!1),n):_e(t).local()}function Oe(t){return 15*-Math.round(t._d.getTimezoneOffset()/15)}function Be(){return!!this.isValid()&&this._isUTC&&0===this._offset}i.updateOffset=function(){};var Le=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Ie=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function Re(t,e){var n,r,i,a=t,o=null;return Ce(t)?a={ms:t._milliseconds,d:t._days,M:t._months}:c(t)?(a={},e?a[e]=t:a.milliseconds=t):(o=Le.exec(t))?(n="-"===o[1]?-1:1,a={y:0,d:k(o[2])*n,h:k(o[3])*n,m:k(o[4])*n,s:k(o[5])*n,ms:k(Se(1e3*o[6]))*n}):(o=Ie.exec(t))?(n="-"===o[1]?-1:1,a={y:Fe(o[2],n),M:Fe(o[3],n),w:Fe(o[4],n),d:Fe(o[5],n),h:Fe(o[6],n),m:Fe(o[7],n),s:Fe(o[8],n)}):null==a?a={}:"object"==typeof a&&("from"in a||"to"in a)&&(i=function(t,e){var n;return t.isValid()&&e.isValid()?(e=De(e,t),t.isBefore(e)?n=Pe(t,e):((n=Pe(e,t)).milliseconds=-n.milliseconds,n.months=-n.months),n):{milliseconds:0,months:0}}(_e(a.from),_e(a.to)),(a={}).ms=i.milliseconds,a.M=i.months),r=new Ee(a),Ce(t)&&h(t,"_locale")&&(r._locale=t._locale),r}function Fe(t,e){var n=t&&parseFloat(t.replace(",","."));return(isNaN(n)?0:n)*e}function Pe(t,e){var n={};return n.months=e.month()-t.month()+12*(e.year()-t.year()),t.clone().add(n.months,"M").isAfter(e)&&--n.months,n.milliseconds=+e-+t.clone().add(n.months,"M"),n}function je(t,e){return function(n,r){var i;return null===r||isNaN(+r)||(M(e,"moment()."+e+"(period, number) is deprecated. Please use moment()."+e+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),i=n,n=r,r=i),Ye(this,Re(n="string"==typeof n?+n:n,r),t),this}}function Ye(t,e,n,r){var a=e._milliseconds,o=Se(e._days),s=Se(e._months);t.isValid()&&(r=null==r||r,s&&St(t,xt(t,"Month")+s*n),o&&wt(t,"Date",xt(t,"Date")+o*n),a&&t._d.setTime(t._d.valueOf()+a*n),r&&i.updateOffset(t,o||s))}Re.fn=Ee.prototype,Re.invalid=function(){return Re(NaN)};var ze=je(1,"add"),Ue=je(-1,"subtract");function qe(t,e){var n=12*(e.year()-t.year())+(e.month()-t.month()),r=t.clone().add(n,"months");return-(n+(e-r<0?(e-r)/(r-t.clone().add(n-1,"months")):(e-r)/(t.clone().add(n+1,"months")-r)))||0}function He(t){var e;return void 0===t?this._locale._abbr:(null!=(e=re(t))&&(this._locale=e),this)}i.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",i.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var $e=C("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",(function(t){return void 0===t?this.localeData():this.locale(t)}));function We(){return this._locale}var Ve=126227808e5;function Ge(t,e){return(t%e+e)%e}function Xe(t,e,n){return t<100&&0<=t?new Date(t+400,e,n)-Ve:new Date(t,e,n).valueOf()}function Ze(t,e,n){return t<100&&0<=t?Date.UTC(t+400,e,n)-Ve:Date.UTC(t,e,n)}function Qe(t,e){H(0,[t,t.length],0,e)}function Ke(t,e,n,r,i){var a;return null==t?It(this,r,i).year:((a=Rt(t,r,i))<e&&(e=a),function(t,e,n,r,i){var a=Lt(t,e,n,r,i),o=Ot(a.year,0,a.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}.call(this,t,e,n,r,i))}H(0,["gg",2],0,(function(){return this.weekYear()%100})),H(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),Qe("gggg","weekYear"),Qe("ggggg","weekYear"),Qe("GGGG","isoWeekYear"),Qe("GGGGG","isoWeekYear"),L("weekYear","gg"),L("isoWeekYear","GG"),P("weekYear",1),P("isoWeekYear",1),lt("G",at),lt("g",at),lt("GG",K,G),lt("gg",K,G),lt("GGGG",nt,Z),lt("gggg",nt,Z),lt("GGGGG",rt,Q),lt("ggggg",rt,Q),yt(["gggg","ggggg","GGGG","GGGGG"],(function(t,e,n,r){e[r.substr(0,2)]=k(t)})),yt(["gg","GG"],(function(t,e,n,r){e[r]=i.parseTwoDigitYear(t)})),H("Q",0,"Qo","quarter"),L("quarter","Q"),P("quarter",7),lt("Q",V),pt("Q",(function(t,e){e[1]=3*(k(t)-1)})),H("D",["DD",2],"Do","date"),L("date","D"),P("date",9),lt("D",K),lt("DD",K,G),lt("Do",(function(t,e){return t?e._dayOfMonthOrdinalParse||e._ordinalParse:e._dayOfMonthOrdinalParseLenient})),pt(["D","DD"],2),pt("Do",(function(t,e){e[2]=k(t.match(K)[0])}));var Je=_t("Date",!0);H("DDD",["DDDD",3],"DDDo","dayOfYear"),L("dayOfYear","DDD"),P("dayOfYear",4),lt("DDD",et),lt("DDDD",X),pt(["DDD","DDDD"],(function(t,e,n){n._dayOfYear=k(t)})),H("m",["mm",2],0,"minute"),L("minute","m"),P("minute",14),lt("m",K),lt("mm",K,G),pt(["m","mm"],4);var tn=_t("Minutes",!1);H("s",["ss",2],0,"second"),L("second","s"),P("second",15),lt("s",K),lt("ss",K,G),pt(["s","ss"],5);var en,nn=_t("Seconds",!1);for(H("S",0,0,(function(){return~~(this.millisecond()/100)})),H(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),H(0,["SSS",3],0,"millisecond"),H(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),H(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),H(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),H(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),H(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),H(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),L("millisecond","ms"),P("millisecond",16),lt("S",et,V),lt("SS",et,G),lt("SSS",et,X),en="SSSS";en.length<=9;en+="S")lt(en,it);function rn(t,e){e[6]=k(1e3*("0."+t))}for(en="S";en.length<=9;en+="S")pt(en,rn);var an=_t("Milliseconds",!1);H("z",0,0,"zoneAbbr"),H("zz",0,0,"zoneName");var on=_.prototype;function sn(t){return t}on.add=ze,on.calendar=function(t,e){var n=t||_e(),r=De(n,this).startOf("day"),a=i.calendarFormat(this,r)||"sameElse",o=e&&(N(e[a])?e[a].call(this,n):e[a]);return this.format(o||this.localeData().calendar(a,this,_e(n)))},on.clone=function(){return new _(this)},on.diff=function(t,e,n){var r,i,a;if(!this.isValid())return NaN;if(!(r=De(t,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),e=I(e)){case"year":a=qe(this,r)/12;break;case"month":a=qe(this,r);break;case"quarter":a=qe(this,r)/3;break;case"second":a=(this-r)/1e3;break;case"minute":a=(this-r)/6e4;break;case"hour":a=(this-r)/36e5;break;case"day":a=(this-r-i)/864e5;break;case"week":a=(this-r-i)/6048e5;break;default:a=this-r}return n?a:w(a)},on.endOf=function(t){var e;if(void 0===(t=I(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?Ze:Xe;switch(t){case"year":e=n(this.year()+1,0,1)-1;break;case"quarter":e=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":e=n(this.year(),this.month()+1,1)-1;break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":e=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":e=this._d.valueOf(),e+=36e5-Ge(e+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case"minute":e=this._d.valueOf(),e+=6e4-Ge(e,6e4)-1;break;case"second":e=this._d.valueOf(),e+=1e3-Ge(e,1e3)-1}return this._d.setTime(e),i.updateOffset(this,!0),this},on.format=function(t){t||(t=this.isUtc()?i.defaultFormatUtc:i.defaultFormat);var e=$(this,t);return this.localeData().postformat(e)},on.from=function(t,e){return this.isValid()&&(x(t)&&t.isValid()||_e(t).isValid())?Re({to:this,from:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},on.fromNow=function(t){return this.from(_e(),t)},on.to=function(t,e){return this.isValid()&&(x(t)&&t.isValid()||_e(t).isValid())?Re({from:this,to:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},on.toNow=function(t){return this.to(_e(),t)},on.get=function(t){return N(this[t=I(t)])?this[t]():this},on.invalidAt=function(){return p(this).overflow},on.isAfter=function(t,e){var n=x(t)?t:_e(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=I(e)||"millisecond")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(e).valueOf())},on.isBefore=function(t,e){var n=x(t)?t:_e(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=I(e)||"millisecond")?this.valueOf()<n.valueOf():this.clone().endOf(e).valueOf()<n.valueOf())},on.isBetween=function(t,e,n,r){var i=x(t)?t:_e(t),a=x(e)?e:_e(e);return!!(this.isValid()&&i.isValid()&&a.isValid())&&("("===(r=r||"()")[0]?this.isAfter(i,n):!this.isBefore(i,n))&&(")"===r[1]?this.isBefore(a,n):!this.isAfter(a,n))},on.isSame=function(t,e){var n,r=x(t)?t:_e(t);return!(!this.isValid()||!r.isValid())&&("millisecond"===(e=I(e)||"millisecond")?this.valueOf()===r.valueOf():(n=r.valueOf(),this.clone().startOf(e).valueOf()<=n&&n<=this.clone().endOf(e).valueOf()))},on.isSameOrAfter=function(t,e){return this.isSame(t,e)||this.isAfter(t,e)},on.isSameOrBefore=function(t,e){return this.isSame(t,e)||this.isBefore(t,e)},on.isValid=function(){return y(this)},on.lang=$e,on.locale=He,on.localeData=We,on.max=we,on.min=xe,on.parsingFlags=function(){return f({},p(this))},on.set=function(t,e){if("object"==typeof t)for(var n=function(t){var e=[];for(var n in t)e.push({unit:n,priority:F[n]});return e.sort((function(t,e){return t.priority-e.priority})),e}(t=R(t)),r=0;r<n.length;r++)this[n[r].unit](t[n[r].unit]);else if(N(this[t=I(t)]))return this[t](e);return this},on.startOf=function(t){var e;if(void 0===(t=I(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?Ze:Xe;switch(t){case"year":e=n(this.year(),0,1);break;case"quarter":e=n(this.year(),this.month()-this.month()%3,1);break;case"month":e=n(this.year(),this.month(),1);break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":e=n(this.year(),this.month(),this.date());break;case"hour":e=this._d.valueOf(),e-=Ge(e+(this._isUTC?0:6e4*this.utcOffset()),36e5);break;case"minute":e=this._d.valueOf(),e-=Ge(e,6e4);break;case"second":e=this._d.valueOf(),e-=Ge(e,1e3)}return this._d.setTime(e),i.updateOffset(this,!0),this},on.subtract=Ue,on.toArray=function(){var t=this;return[t.year(),t.month(),t.date(),t.hour(),t.minute(),t.second(),t.millisecond()]},on.toObject=function(){var t=this;return{years:t.year(),months:t.month(),date:t.date(),hours:t.hours(),minutes:t.minutes(),seconds:t.seconds(),milliseconds:t.milliseconds()}},on.toDate=function(){return new Date(this.valueOf())},on.toISOString=function(t){if(!this.isValid())return null;var e=!0!==t,n=e?this.clone().utc():this;return n.year()<0||9999<n.year()?$(n,e?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):N(Date.prototype.toISOString)?e?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",$(n,"Z")):$(n,e?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},on.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var t="moment",e="";this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",e="Z");var n="["+t+'("]',r=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",i=e+'[")]';return this.format(n+r+"-MM-DD[T]HH:mm:ss.SSS"+i)},on.toJSON=function(){return this.isValid()?this.toISOString():null},on.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},on.unix=function(){return Math.floor(this.valueOf()/1e3)},on.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},on.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},on.year=bt,on.isLeapYear=function(){return mt(this.year())},on.weekYear=function(t){return Ke.call(this,t,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},on.isoWeekYear=function(t){return Ke.call(this,t,this.isoWeek(),this.isoWeekday(),1,4)},on.quarter=on.quarters=function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},on.month=At,on.daysInMonth=function(){return kt(this.year(),this.month())},on.week=on.weeks=function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},on.isoWeek=on.isoWeeks=function(t){var e=It(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},on.weeksInYear=function(){var t=this.localeData()._week;return Rt(this.year(),t.dow,t.doy)},on.isoWeeksInYear=function(){return Rt(this.year(),1,4)},on.date=Je,on.day=on.days=function(t){if(!this.isValid())return null!=t?this:NaN;var e,n,r=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(e=t,n=this.localeData(),t="string"!=typeof e?e:isNaN(e)?"number"==typeof(e=n.weekdaysParse(e))?e:null:parseInt(e,10),this.add(t-r,"d")):r},on.weekday=function(t){if(!this.isValid())return null!=t?this:NaN;var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},on.isoWeekday=function(t){if(!this.isValid())return null!=t?this:NaN;if(null==t)return this.day()||7;var e,n,r=(e=t,n=this.localeData(),"string"==typeof e?n.weekdaysParse(e)%7||7:isNaN(e)?null:e);return this.day(this.day()%7?r:r-7)},on.dayOfYear=function(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},on.hour=on.hours=Xt,on.minute=on.minutes=tn,on.second=on.seconds=nn,on.millisecond=on.milliseconds=an,on.utcOffset=function(t,e,n){var r,a=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null==t)return this._isUTC?a:Oe(this);if("string"==typeof t){if(null===(t=Ne(st,t)))return this}else Math.abs(t)<16&&!n&&(t*=60);return!this._isUTC&&e&&(r=Oe(this)),this._offset=t,this._isUTC=!0,null!=r&&this.add(r,"m"),a!==t&&(!e||this._changeInProgress?Ye(this,Re(t-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,i.updateOffset(this,!0),this._changeInProgress=null)),this},on.utc=function(t){return this.utcOffset(0,t)},on.local=function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Oe(this),"m")),this},on.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var t=Ne(ot,this._i);null!=t?this.utcOffset(t):this.utcOffset(0,!0)}return this},on.hasAlignedHourOffset=function(t){return!!this.isValid()&&(t=t?_e(t).utcOffset():0,(this.utcOffset()-t)%60==0)},on.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},on.isLocal=function(){return!!this.isValid()&&!this._isUTC},on.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},on.isUtc=Be,on.isUTC=Be,on.zoneAbbr=function(){return this._isUTC?"UTC":""},on.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},on.dates=C("dates accessor is deprecated. Use date instead.",Je),on.months=C("months accessor is deprecated. Use month instead",At),on.years=C("years accessor is deprecated. Use year instead",bt),on.zone=C("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",(function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()})),on.isDSTShifted=C("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",(function(){if(!s(this._isDSTShifted))return this._isDSTShifted;var t={};if(v(t,this),(t=ve(t))._a){var e=t._isUTC?d(t._a):_e(t._a);this._isDSTShifted=this.isValid()&&0<T(t._a,e.toArray())}else this._isDSTShifted=!1;return this._isDSTShifted}));var cn=O.prototype;function un(t,e,n,r){var i=re(),a=d().set(r,e);return i[n](a,t)}function ln(t,e,n){if(c(t)&&(e=t,t=void 0),t=t||"",null!=e)return un(t,e,n,"month");var r,i=[];for(r=0;r<12;r++)i[r]=un(t,r,n,"month");return i}function hn(t,e,n,r){"boolean"==typeof t?c(e)&&(n=e,e=void 0):(e=t,t=!1,c(n=e)&&(n=e,e=void 0)),e=e||"";var i,a=re(),o=t?a._week.dow:0;if(null!=n)return un(e,(n+o)%7,r,"day");var s=[];for(i=0;i<7;i++)s[i]=un(e,(i+o)%7,r,"day");return s}cn.calendar=function(t,e,n){var r=this._calendar[t]||this._calendar.sameElse;return N(r)?r.call(e,n):r},cn.longDateFormat=function(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,(function(t){return t.slice(1)})),this._longDateFormat[t])},cn.invalidDate=function(){return this._invalidDate},cn.ordinal=function(t){return this._ordinal.replace("%d",t)},cn.preparse=sn,cn.postformat=sn,cn.relativeTime=function(t,e,n,r){var i=this._relativeTime[n];return N(i)?i(t,e,n,r):i.replace(/%d/i,t)},cn.pastFuture=function(t,e){var n=this._relativeTime[0<t?"future":"past"];return N(n)?n(e):n.replace(/%s/i,e)},cn.set=function(t){var e,n;for(n in t)N(e=t[n])?this[n]=e:this["_"+n]=e;this._config=t,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},cn.months=function(t,e){return t?a(this._months)?this._months[t.month()]:this._months[(this._months.isFormat||Tt).test(e)?"format":"standalone"][t.month()]:a(this._months)?this._months:this._months.standalone},cn.monthsShort=function(t,e){return t?a(this._monthsShort)?this._monthsShort[t.month()]:this._monthsShort[Tt.test(e)?"format":"standalone"][t.month()]:a(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},cn.monthsParse=function(t,e,n){var r,i,a;if(this._monthsParseExact)return function(t,e,n){var r,i,a,o=t.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],r=0;r<12;++r)a=d([2e3,r]),this._shortMonthsParse[r]=this.monthsShort(a,"").toLocaleLowerCase(),this._longMonthsParse[r]=this.months(a,"").toLocaleLowerCase();return n?"MMM"===e?-1!==(i=vt.call(this._shortMonthsParse,o))?i:null:-1!==(i=vt.call(this._longMonthsParse,o))?i:null:"MMM"===e?-1!==(i=vt.call(this._shortMonthsParse,o))||-1!==(i=vt.call(this._longMonthsParse,o))?i:null:-1!==(i=vt.call(this._longMonthsParse,o))||-1!==(i=vt.call(this._shortMonthsParse,o))?i:null}.call(this,t,e,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=d([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(a="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[r]=new RegExp(a.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[r].test(t))return r;if(n&&"MMM"===e&&this._shortMonthsParse[r].test(t))return r;if(!n&&this._monthsParse[r].test(t))return r}},cn.monthsRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Dt.call(this),t?this._monthsStrictRegex:this._monthsRegex):(h(this,"_monthsRegex")||(this._monthsRegex=Nt),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)},cn.monthsShortRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Dt.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,"_monthsShortRegex")||(this._monthsShortRegex=Mt),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)},cn.week=function(t){return It(t,this._week.dow,this._week.doy).week},cn.firstDayOfYear=function(){return this._week.doy},cn.firstDayOfWeek=function(){return this._week.dow},cn.weekdays=function(t,e){var n=a(this._weekdays)?this._weekdays:this._weekdays[t&&!0!==t&&this._weekdays.isFormat.test(e)?"format":"standalone"];return!0===t?Ft(n,this._week.dow):t?n[t.day()]:n},cn.weekdaysMin=function(t){return!0===t?Ft(this._weekdaysMin,this._week.dow):t?this._weekdaysMin[t.day()]:this._weekdaysMin},cn.weekdaysShort=function(t){return!0===t?Ft(this._weekdaysShort,this._week.dow):t?this._weekdaysShort[t.day()]:this._weekdaysShort},cn.weekdaysParse=function(t,e,n){var r,i,a;if(this._weekdaysParseExact)return function(t,e,n){var r,i,a,o=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)a=d([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(a,"").toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(a,"").toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(a,"").toLocaleLowerCase();return n?"dddd"===e?-1!==(i=vt.call(this._weekdaysParse,o))?i:null:"ddd"===e?-1!==(i=vt.call(this._shortWeekdaysParse,o))?i:null:-1!==(i=vt.call(this._minWeekdaysParse,o))?i:null:"dddd"===e?-1!==(i=vt.call(this._weekdaysParse,o))||-1!==(i=vt.call(this._shortWeekdaysParse,o))||-1!==(i=vt.call(this._minWeekdaysParse,o))?i:null:"ddd"===e?-1!==(i=vt.call(this._shortWeekdaysParse,o))||-1!==(i=vt.call(this._weekdaysParse,o))||-1!==(i=vt.call(this._minWeekdaysParse,o))?i:null:-1!==(i=vt.call(this._minWeekdaysParse,o))||-1!==(i=vt.call(this._weekdaysParse,o))||-1!==(i=vt.call(this._shortWeekdaysParse,o))?i:null}.call(this,t,e,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=d([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[r]=new RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[r]=new RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[r]||(a="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[r]=new RegExp(a.replace(".",""),"i")),n&&"dddd"===e&&this._fullWeekdaysParse[r].test(t))return r;if(n&&"ddd"===e&&this._shortWeekdaysParse[r].test(t))return r;if(n&&"dd"===e&&this._minWeekdaysParse[r].test(t))return r;if(!n&&this._weekdaysParse[r].test(t))return r}},cn.weekdaysRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Ht.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,"_weekdaysRegex")||(this._weekdaysRegex=zt),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)},cn.weekdaysShortRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Ht.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=Ut),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},cn.weekdaysMinRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Ht.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=qt),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},cn.isPM=function(t){return"p"===(t+"").toLowerCase().charAt(0)},cn.meridiem=function(t,e,n){return 11<t?n?"pm":"PM":n?"am":"AM"},ee("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10;return t+(1===k(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th")}}),i.lang=C("moment.lang is deprecated. Use moment.locale instead.",ee),i.langData=C("moment.langData is deprecated. Use moment.localeData instead.",re);var fn=Math.abs;function dn(t,e,n,r){var i=Re(e,n);return t._milliseconds+=r*i._milliseconds,t._days+=r*i._days,t._months+=r*i._months,t._bubble()}function pn(t){return t<0?Math.floor(t):Math.ceil(t)}function yn(t){return 4800*t/146097}function gn(t){return 146097*t/4800}function mn(t){return function(){return this.as(t)}}var vn=mn("ms"),bn=mn("s"),_n=mn("m"),xn=mn("h"),wn=mn("d"),kn=mn("w"),Tn=mn("M"),En=mn("Q"),Cn=mn("y");function Sn(t){return function(){return this.isValid()?this._data[t]:NaN}}var An=Sn("milliseconds"),Mn=Sn("seconds"),Nn=Sn("minutes"),Dn=Sn("hours"),On=Sn("days"),Bn=Sn("months"),Ln=Sn("years"),In=Math.round,Rn={ss:44,s:45,m:45,h:22,d:26,M:11},Fn=Math.abs;function Pn(t){return(0<t)-(t<0)||+t}function jn(){if(!this.isValid())return this.localeData().invalidDate();var t,e,n=Fn(this._milliseconds)/1e3,r=Fn(this._days),i=Fn(this._months);e=w((t=w(n/60))/60),n%=60,t%=60;var a=w(i/12),o=i%=12,s=r,c=e,u=t,l=n?n.toFixed(3).replace(/\.?0+$/,""):"",h=this.asSeconds();if(!h)return"P0D";var f=h<0?"-":"",d=Pn(this._months)!==Pn(h)?"-":"",p=Pn(this._days)!==Pn(h)?"-":"",y=Pn(this._milliseconds)!==Pn(h)?"-":"";return f+"P"+(a?d+a+"Y":"")+(o?d+o+"M":"")+(s?p+s+"D":"")+(c||u||l?"T":"")+(c?y+c+"H":"")+(u?y+u+"M":"")+(l?y+l+"S":"")}var Yn=Ee.prototype;return Yn.isValid=function(){return this._isValid},Yn.abs=function(){var t=this._data;return this._milliseconds=fn(this._milliseconds),this._days=fn(this._days),this._months=fn(this._months),t.milliseconds=fn(t.milliseconds),t.seconds=fn(t.seconds),t.minutes=fn(t.minutes),t.hours=fn(t.hours),t.months=fn(t.months),t.years=fn(t.years),this},Yn.add=function(t,e){return dn(this,t,e,1)},Yn.subtract=function(t,e){return dn(this,t,e,-1)},Yn.as=function(t){if(!this.isValid())return NaN;var e,n,r=this._milliseconds;if("month"===(t=I(t))||"quarter"===t||"year"===t)switch(e=this._days+r/864e5,n=this._months+yn(e),t){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(e=this._days+Math.round(gn(this._months)),t){case"week":return e/7+r/6048e5;case"day":return e+r/864e5;case"hour":return 24*e+r/36e5;case"minute":return 1440*e+r/6e4;case"second":return 86400*e+r/1e3;case"millisecond":return Math.floor(864e5*e)+r;default:throw new Error("Unknown unit "+t)}},Yn.asMilliseconds=vn,Yn.asSeconds=bn,Yn.asMinutes=_n,Yn.asHours=xn,Yn.asDays=wn,Yn.asWeeks=kn,Yn.asMonths=Tn,Yn.asQuarters=En,Yn.asYears=Cn,Yn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*k(this._months/12):NaN},Yn._bubble=function(){var t,e,n,r,i,a=this._milliseconds,o=this._days,s=this._months,c=this._data;return 0<=a&&0<=o&&0<=s||a<=0&&o<=0&&s<=0||(a+=864e5*pn(gn(s)+o),s=o=0),c.milliseconds=a%1e3,t=w(a/1e3),c.seconds=t%60,e=w(t/60),c.minutes=e%60,n=w(e/60),c.hours=n%24,s+=i=w(yn(o+=w(n/24))),o-=pn(gn(i)),r=w(s/12),s%=12,c.days=o,c.months=s,c.years=r,this},Yn.clone=function(){return Re(this)},Yn.get=function(t){return t=I(t),this.isValid()?this[t+"s"]():NaN},Yn.milliseconds=An,Yn.seconds=Mn,Yn.minutes=Nn,Yn.hours=Dn,Yn.days=On,Yn.weeks=function(){return w(this.days()/7)},Yn.months=Bn,Yn.years=Ln,Yn.humanize=function(t){if(!this.isValid())return this.localeData().invalidDate();var e,n,r,i,a,o,s,c,u,l,h=this.localeData(),f=(e=!t,n=h,r=Re(this).abs(),i=In(r.as("s")),a=In(r.as("m")),o=In(r.as("h")),s=In(r.as("d")),c=In(r.as("M")),u=In(r.as("y")),(l=i<=Rn.ss&&["s",i]||i<Rn.s&&["ss",i]||a<=1&&["m"]||a<Rn.m&&["mm",a]||o<=1&&["h"]||o<Rn.h&&["hh",o]||s<=1&&["d"]||s<Rn.d&&["dd",s]||c<=1&&["M"]||c<Rn.M&&["MM",c]||u<=1&&["y"]||["yy",u])[2]=e,l[3]=0<+this,l[4]=n,function(t,e,n,r,i){return i.relativeTime(e||1,!!n,t,r)}.apply(null,l));return t&&(f=h.pastFuture(+this,f)),h.postformat(f)},Yn.toISOString=jn,Yn.toString=jn,Yn.toJSON=jn,Yn.locale=He,Yn.localeData=We,Yn.toIsoString=C("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",jn),Yn.lang=$e,H("X",0,0,"unix"),H("x",0,0,"valueOf"),lt("x",at),lt("X",/[+-]?\d+(\.\d{1,3})?/),pt("X",(function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))})),pt("x",(function(t,e,n){n._d=new Date(k(t))})),i.version="2.24.0",e=_e,i.fn=on,i.min=function(){return ke("isBefore",[].slice.call(arguments,0))},i.max=function(){return ke("isAfter",[].slice.call(arguments,0))},i.now=function(){return Date.now?Date.now():+new Date},i.utc=d,i.unix=function(t){return _e(1e3*t)},i.months=function(t,e){return ln(t,e,"months")},i.isDate=u,i.locale=ee,i.invalid=g,i.duration=Re,i.isMoment=x,i.weekdays=function(t,e,n){return hn(t,e,n,"weekdays")},i.parseZone=function(){return _e.apply(null,arguments).parseZone()},i.localeData=re,i.isDuration=Ce,i.monthsShort=function(t,e){return ln(t,e,"monthsShort")},i.weekdaysMin=function(t,e,n){return hn(t,e,n,"weekdaysMin")},i.defineLocale=ne,i.updateLocale=function(t,e){if(null!=e){var n,r,i=Zt;null!=(r=te(t))&&(i=r._config),(n=new O(e=D(i,e))).parentLocale=Qt[t],Qt[t]=n,ee(t)}else null!=Qt[t]&&(null!=Qt[t].parentLocale?Qt[t]=Qt[t].parentLocale:null!=Qt[t]&&delete Qt[t]);return Qt[t]},i.locales=function(){return S(Qt)},i.weekdaysShort=function(t,e,n){return hn(t,e,n,"weekdaysShort")},i.normalizeUnits=I,i.relativeTimeRounding=function(t){return void 0===t?In:"function"==typeof t&&(In=t,!0)},i.relativeTimeThreshold=function(t,e){return void 0!==Rn[t]&&(void 0===e?Rn[t]:(Rn[t]=e,"s"===t&&(Rn.ss=e-1),!0))},i.calendarFormat=function(t,e){var n=t.diff(e,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},i.prototype=on,i.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},i}()},6470:t=>{"use strict";function e(t){if("string"!=typeof t)throw new TypeError("Path must be a string. Received "+JSON.stringify(t))}function n(t,e){for(var n,r="",i=0,a=-1,o=0,s=0;s<=t.length;++s){if(s<t.length)n=t.charCodeAt(s);else{if(47===n)break;n=47}if(47===n){if(a===s-1||1===o);else if(a!==s-1&&2===o){if(r.length<2||2!==i||46!==r.charCodeAt(r.length-1)||46!==r.charCodeAt(r.length-2))if(r.length>2){var c=r.lastIndexOf("/");if(c!==r.length-1){-1===c?(r="",i=0):i=(r=r.slice(0,c)).length-1-r.lastIndexOf("/"),a=s,o=0;continue}}else if(2===r.length||1===r.length){r="",i=0,a=s,o=0;continue}e&&(r.length>0?r+="/..":r="..",i=2)}else r.length>0?r+="/"+t.slice(a+1,s):r=t.slice(a+1,s),i=s-a-1;a=s,o=0}else 46===n&&-1!==o?++o:o=-1}return r}var r={resolve:function(){for(var t,r="",i=!1,a=arguments.length-1;a>=-1&&!i;a--){var o;a>=0?o=arguments[a]:(void 0===t&&(t=process.cwd()),o=t),e(o),0!==o.length&&(r=o+"/"+r,i=47===o.charCodeAt(0))}return r=n(r,!i),i?r.length>0?"/"+r:"/":r.length>0?r:"."},normalize:function(t){if(e(t),0===t.length)return".";var r=47===t.charCodeAt(0),i=47===t.charCodeAt(t.length-1);return 0!==(t=n(t,!r)).length||r||(t="."),t.length>0&&i&&(t+="/"),r?"/"+t:t},isAbsolute:function(t){return e(t),t.length>0&&47===t.charCodeAt(0)},join:function(){if(0===arguments.length)return".";for(var t,n=0;n<arguments.length;++n){var i=arguments[n];e(i),i.length>0&&(void 0===t?t=i:t+="/"+i)}return void 0===t?".":r.normalize(t)},relative:function(t,n){if(e(t),e(n),t===n)return"";if((t=r.resolve(t))===(n=r.resolve(n)))return"";for(var i=1;i<t.length&&47===t.charCodeAt(i);++i);for(var a=t.length,o=a-i,s=1;s<n.length&&47===n.charCodeAt(s);++s);for(var c=n.length-s,u=o<c?o:c,l=-1,h=0;h<=u;++h){if(h===u){if(c>u){if(47===n.charCodeAt(s+h))return n.slice(s+h+1);if(0===h)return n.slice(s+h)}else o>u&&(47===t.charCodeAt(i+h)?l=h:0===h&&(l=0));break}var f=t.charCodeAt(i+h);if(f!==n.charCodeAt(s+h))break;47===f&&(l=h)}var d="";for(h=i+l+1;h<=a;++h)h!==a&&47!==t.charCodeAt(h)||(0===d.length?d+="..":d+="/..");return d.length>0?d+n.slice(s+l):(s+=l,47===n.charCodeAt(s)&&++s,n.slice(s))},_makeLong:function(t){return t},dirname:function(t){if(e(t),0===t.length)return".";for(var n=t.charCodeAt(0),r=47===n,i=-1,a=!0,o=t.length-1;o>=1;--o)if(47===(n=t.charCodeAt(o))){if(!a){i=o;break}}else a=!1;return-1===i?r?"/":".":r&&1===i?"//":t.slice(0,i)},basename:function(t,n){if(void 0!==n&&"string"!=typeof n)throw new TypeError('"ext" argument must be a string');e(t);var r,i=0,a=-1,o=!0;if(void 0!==n&&n.length>0&&n.length<=t.length){if(n.length===t.length&&n===t)return"";var s=n.length-1,c=-1;for(r=t.length-1;r>=0;--r){var u=t.charCodeAt(r);if(47===u){if(!o){i=r+1;break}}else-1===c&&(o=!1,c=r+1),s>=0&&(u===n.charCodeAt(s)?-1==--s&&(a=r):(s=-1,a=c))}return i===a?a=c:-1===a&&(a=t.length),t.slice(i,a)}for(r=t.length-1;r>=0;--r)if(47===t.charCodeAt(r)){if(!o){i=r+1;break}}else-1===a&&(o=!1,a=r+1);return-1===a?"":t.slice(i,a)},extname:function(t){e(t);for(var n=-1,r=0,i=-1,a=!0,o=0,s=t.length-1;s>=0;--s){var c=t.charCodeAt(s);if(47!==c)-1===i&&(a=!1,i=s+1),46===c?-1===n?n=s:1!==o&&(o=1):-1!==n&&(o=-1);else if(!a){r=s+1;break}}return-1===n||-1===i||0===o||1===o&&n===i-1&&n===r+1?"":t.slice(n,i)},format:function(t){if(null===t||"object"!=typeof t)throw new TypeError('The "pathObject" argument must be of type Object. Received type '+typeof t);return function(t,e){var n=e.dir||e.root,r=e.base||(e.name||"")+(e.ext||"");return n?n===e.root?n+r:n+"/"+r:r}(0,t)},parse:function(t){e(t);var n={root:"",dir:"",base:"",ext:"",name:""};if(0===t.length)return n;var r,i=t.charCodeAt(0),a=47===i;a?(n.root="/",r=1):r=0;for(var o=-1,s=0,c=-1,u=!0,l=t.length-1,h=0;l>=r;--l)if(47!==(i=t.charCodeAt(l)))-1===c&&(u=!1,c=l+1),46===i?-1===o?o=l:1!==h&&(h=1):-1!==o&&(h=-1);else if(!u){s=l+1;break}return-1===o||-1===c||0===h||1===h&&o===c-1&&o===s+1?-1!==c&&(n.base=n.name=0===s&&a?t.slice(1,c):t.slice(s,c)):(0===s&&a?(n.name=t.slice(1,o),n.base=t.slice(1,c)):(n.name=t.slice(s,o),n.base=t.slice(s,c)),n.ext=t.slice(o,c)),s>0?n.dir=t.slice(0,s-1):a&&(n.dir="/"),n},sep:"/",delimiter:":",win32:null,posix:null};r.posix=r,t.exports=r},8218:()=>{},8009:()=>{},5354:()=>{},6878:()=>{},8183:()=>{},1428:()=>{},4551:()=>{},8800:()=>{},1993:()=>{},3069:()=>{},9143:()=>{}},e={};function n(r){var i=e[r];if(void 0!==i)return i.exports;var a=e[r]={id:r,loaded:!1,exports:{}};return t[r].call(a.exports,a,a.exports,n),a.loaded=!0,a.exports}n.c=e,n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var r in e)n.o(e,r)&&!n.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),n.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.nmd=t=>(t.paths=[],t.children||(t.children=[]),t);var r=n(n.s=7458);return r.default})()})); +//# sourceMappingURL=mermaid.min.js.map
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/SUMMARY.md b/third_party/rust/autocxx/v0_17/crate/book/src/SUMMARY.md new file mode 100644 index 0000000..59f23620 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/SUMMARY.md
@@ -0,0 +1,22 @@ +# Summary + +- [Rust ❤️ pre-existing C++](index.md) +- [Tutorial](tutorial.md) +- [Workflow](workflow.md) +- [Allowlist and syntax](allowlist.md) +- [Building](building.md) +- [Storage - stack and heaps](storage.md) +- [Pointers, references, values](references_etc.md) +- [Built-in types](primitives.md) +- [C++ type and function names](naming.md) +- [C++ structs, enums and classes](cpp_types.md) +- [C++ functions](cpp_functions.md) +- [Callbacks into Rust](rust_calls.md) +- [Other C++ features](other_features.md) +- [Safety](safety.md) +- [Rustic bindings](rustic.md) +- [Examples](examples.md) +- [Credits](credits.md) +- [Contributing](contributing.md) +- [Code of Conduct](code-of-conduct.md) +
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/allowlist.md b/third_party/rust/autocxx/v0_17/crate/book/src/allowlist.md new file mode 100644 index 0000000..2834ee6 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/allowlist.md
@@ -0,0 +1,31 @@ +# The allowlist and `include_cpp` syntax + +To include C++ in your Rust codebase using `autocxx`, you will need +at least one [`include_cpp` macro](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html). + +The simplest is: + +```rust,ignore +use autocxx::prelude::*; + +include_cpp! { + #include "my_header.h" + generate!("MyAPIFunction") +} +``` + +You need to include [`generate!` directives](https://docs.rs/autocxx/latest/autocxx/macro.generate.html) +for every *type* or *function* you wish to access from Rust. You don't need to specify this for member functions +of types that you've added - they'll be generated automatically. (If a particular member function can't +be generated, some placeholder item with explanatory documentation [will be generated instead](workflow.md)). + +Various other directives are possible inside this macro, most notably: + +* You can ask to generate all the items in a namespace using + [`generate_ns!`](https://docs.rs/autocxx/latest/autocxx/macro.generate_ns.html) +* You might sometimes want to ask that a type is generated as 'plain old data' using + [`generate_pod!`](https://docs.rs/autocxx/latest/autocxx/macro.generate_pod.html) instead of `generate!` - + see the chapter on [C++ types](cpp_types.md). +* You'll probaly want to specify a [`safety!` policy](safety.md) + +See [the docs.rs documentation for the full list](https://docs.rs/autocxx/latest/autocxx/).
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/building.md b/third_party/rust/autocxx/v0_17/crate/book/src/building.md new file mode 100644 index 0000000..4d5d9191 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/building.md
@@ -0,0 +1,60 @@ +# Building + +## Building if you're using cargo + +The basics of building in a `cargo` environment are explained in [the tutorial](tutorial.md). + +If your build depends on later editions of the C++ standard library, you will need to ensure that both `libclang` and the compiler are sent the appropriate flag, like this: + +```rust,ignore +fn main() { + let path = std::path::PathBuf::from("src"); // include path + let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]) + .extra_clang_args(&["-std=c++17"]) + .expect_build(); + b.flag_if_supported("-std=c++17") + .compile("autocxx-demo"); // arbitrary library name, pick anything + println!("cargo:rerun-if-changed=src/main.rs"); + // Add instructions to link to any C++ libraries you need. +} +``` + +## Building - if you're not using cargo + +See the `autocxx-gen` crate. You'll need to: + +* Run the `codegen` phase. You'll need to use the [`autocxx-gen`](https://crates.io/crates/autocxx-gen) + tool to process the .rs code into C++ header and + implementation files. This will also generate `.rs` side bindings. +* Educate the procedural macro about where to find the generated `.rs` bindings. Set the + `AUTOCXX_RS` environment variable to a list of directories to search. + If you use `autocxx-build`, this happens automatically. (You can alternatively + specify `AUTOCXX_RS_FILE` to give a precise filename as opposed to a directory to search, + though this isn't recommended unless your build system specifically requires it + because it allows only a single `include_cpp!` block per `.rs` file.) See `gen --help` + for details on the naming of the generated files. + +```mermaid +flowchart TB + s(Rust source with include_cpp!) + c(Existing C++ headers) + cg(autocxx-gen or autocxx-build) + genrs(Generated .rs file) + gencpp(Generated .cpp and .h files) + rsb(Rust/Cargo build) + cppb(C++ build) + l(Linker) + s --> cg + c --> cg + cg --> genrs + cg --> gencpp + m(autocxx-macro) + s --> m + genrs-. included .->m + m --> rsb + gencpp --> cppb + cppb --> l + rsb --> l +``` + +This interop inevitably involves lots of fiddly small functions. It's likely to perform far better if you can achieve cross-language link-time-optimization (LTO). [This issue](https://github.com/dtolnay/cxx/issues/371) may give some useful hints - see also all the build-related help in [the cxx manual](https://cxx.rs/) which all applies here too.
diff --git a/third_party/rust/autocxx/v0_16/crate/docs/code-of-conduct.md b/third_party/rust/autocxx/v0_17/crate/book/src/code-of-conduct.md similarity index 100% rename from third_party/rust/autocxx/v0_16/crate/docs/code-of-conduct.md rename to third_party/rust/autocxx/v0_17/crate/book/src/code-of-conduct.md
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/contributing.md b/third_party/rust/autocxx/v0_17/crate/book/src/contributing.md new file mode 100644 index 0000000..9942445 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/contributing.md
@@ -0,0 +1,106 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution; +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to <https://cla.developers.google.com/> to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. + +## Community Guidelines + +This project follows [Google's Open Source Community +Guidelines](https://opensource.google/conduct/). + +## Directory structure + +* `book` - you're reading it! +* `demo` - a very simple demo example +* `examples` - will gradually fill with more complex examples +* `parser` - code which parses a single `include_cpp!` macro. Used by both the macro + (which doesn't do much) and the code generator (which does much more, by means of + `engine` below) +* `engine` - all the core code for actual code generation. +* `macro` - the procedural macro which expands the Rust code. +* `gen/build` - a library to be used from `build.rs` scripts to generate .cc and .h + files from an `include_cxx` section. +* `gen/cmd` - a command-line tool which does the same. +* `src` (outermost project) - a wrapper crate which imports the procedural macro and + a few other things. + +## Where to start reading + +The main algorithm is in `engine/src/lib.rs`, in the function `generate()`. This asks +`bindgen` to generate a heap of Rust code and then passes it into +`engine/src/conversion` to convert it to be a format suitable for input +to `cxx`. + +However, most of the actual code is in `engine/src/conversion/mod.rs`. + +At the moment we're using a slightly branched version of `bindgen` called `autocxx-bindgen`. +It's hoped this is temporary; some of our changes are sufficiently weird that it would be +presumptious to try to get them accepted upstream until we're sure `autocxx` has roughly the right approach. + +## How to develop + +If you're making a change, here's what you need to do to get useful diagnostics etc. +First of all, `cargo run` in the `demo` directory. If it breaks, you don't get much +in the way of useful diagnostics, because `stdout` is swallowed by cargo build scripts. +So, practically speaking, you would almost always move onto running one of the tests +in the test suite. With suitable options, you can get plenty of output. For instance: + +```ignore +RUST_BACKTRACE=1 RUST_LOG=autocxx_engine=info cargo test --all test_cycle_string_full_pipeline -- --nocapture +``` + +This is especially valuable to see the `bindgen` output Rust code, and then the converted Rust code which we pass into cxx. Usually, most problems are due to some mis-conversion somewhere +in `engine/src/conversion`. See [here](https://docs.rs/autocxx-engine/latest/autocxx_engine/struct.IncludeCppEngine.html) for documentation and diagrams on how the engine works. + +## Reporting bugs + +If you've found a problem, and you're reading this, *thank you*! Your diligence +in reporting the bug is much appreciated and will make `autocxx` better. In +order of preference here's how we would like to hear about your problem: + +* Raise a pull request adding a new failing integration test to + `engine/src/integration_tests.rs`. +* Minimize the test using `tools/reduce`, something like this: + `target/debug/autocxx-reduce file -d "safety!(unsafe_ffi)" -d + 'generate_pod!("A")' -I ~/my-include-dir -h my-header.h -p + problem-error-message -- --remove-pass pass_line_markers` + This is a wrapper for the amazing `creduce` which will take thousands of lines + of C++, preprocess it, and then identify the minimum required lines to + reproduce the same problem. +* Use the C++ preprocessor to give a single complete C++ file which demonstrates + the problem, along with the `include_cpp!` directive you use. + Alternatively, run your build using `AUTOCXX_REPRO_CASE=repro.json` which should + put everything we need into `output.h`. If necessary, you can use the `CLANG_PATH` + or `CXX` environment variables to specify the path to the Clang compiler to use. +* Failing all else, build using + `cargo clean -p <your package name> && RUST_LOG=autocxx_engine=info cargo build -vvv` + and send the _entire_ log to us. This will include two key bits of logging: + the C++ bindings as distilled by `bindgen`, and then the version which + we've converted and moulded to be suitable for use by `cxx`. + +## How to contribute to this manual + +More examples in this manual are _very_ welcome! + +Because `autocxx` examples require both Rust and C++ code to be linked together, +a custom preprocessor is used for this manual. See one of the existing examples +such as in `index.md` to see how to do this. \ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/cpp_functions.md b/third_party/rust/autocxx/v0_17/crate/book/src/cpp_functions.md new file mode 100644 index 0000000..c34a4e8 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/cpp_functions.md
@@ -0,0 +1,228 @@ +# C++ functions + +Calling C++ functions is largly as you might expect. + +## Value and rvalue parameters + +Functions taking [non-POD](cpp_types.md) value parameters can take a `cxx::UniquePtr<T>` +or a `&T`. This gives you the choice of Rust semantics - where a parameter +is absorbed and destroyed - or C++ semantics where the parameter is copied. + + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +" +#include <strstream> +Goat::Goat() {} +void feed_goat(Goat g) {} +", +"#include <cstdint> + +struct Goat { + Goat(); + uint32_t horn_count; +}; + +void feed_goat(Goat g); // takes goat by value +", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("Goat") + generate!("feed_goat") +} + +fn main() { + let goat = ffi::Goat::make_unique(); // returns a cxx::UniquePtr, i.e. a std::unique_ptr + // C++-like semantics... + ffi::feed_goat(&goat); + // ... you've still got the goat! + ffi::feed_goat(&goat); + // Or, Rust-like semantics, where the goat is consumed. + ffi::feed_goat(goat); + // No goat any more... + // ffi::feed_goat(&goat); // doesn't compile +} +} +) +``` + +Specifically, you can pass anything which implements [`ValueParam<T>`](https://docs.rs/autocxx/latest/autocxx/trait.ValueParam.html). + +If you're keeping non-POD values on the Rust stack, you need to explicitly use [`as_mov`](https://docs.rs/autocxx/latest/autocxx/prelude/fn.as_mov.html) to indicate that you want to +consume the object using move semantics: + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +" +#include <strstream> +Blimp::Blimp() {} +void burst(Blimp g) {} +", +"#include <cstdint> + +struct Blimp { + Blimp(); + uint32_t tons_of_helium; +}; + +void burst(Blimp b); // consumes blimp, + // but because C++ is amazing, may copy the blimp first. +", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("Blimp") + generate!("burst") +} + +fn main() { + moveit! { + let mut blimp = ffi::Blimp::new(); + } + ffi::burst(&*blimp); // pass by copy + ffi::burst(as_copy(blimp.as_ref())); // explicitly say you want to pass by copy + ffi::burst(as_mov(blimp)); // consume, using move constructor +} +} +) +``` + +Rvalue parameters are not yet supported. + +## Default parameters + +Are not yet supported[^default]. + +[^default]: the work is [planned here](https://github.com/google/autocxx/issues/563). + +## Return values + +At present, return values for [non-POD](cpp_types.md) types are always +a `cxx::UniquePtr<T>`. This is likely to change in future, at least to a type +which is guaranteed not to be null[^not-null.] + +[^not-null]: [plans here](https://github.com/google/autocxx/issues/845) + +## Overloads - and identifiers ending in digits + +C++ allows function overloads; Rust doesn't. `autocxx` follows the lead +of `bindgen` here and generating overloads as `func`, `func1`, `func2` etc. +This is essentially awful without `rust-analyzer` IDE support - see the +[workflows chapter](workflow.md) for why you should be using an IDE. + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +" +void saw(const View&) {} +void saw(const Tree&) {}", +" +#include <string> +struct View { + std::string of_what; // go and watch In Bruges, it's great +}; + +struct Tree { + int dendrochronologically_determined_age; +}; + +void saw(const View&); +void saw(const Tree&); +", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("Tree") + generate!("View") + generate!("saw") + generate!("saw1") +} + +fn main() { + let view = ffi::View::make_unique(); + ffi::saw(&view); + let tree = ffi::Tree::make_unique(); + ffi::saw1(&tree); // yuck, overload +} +} +) +``` + +`autocxx` doesn't yet support default parameters. + +It's fairly likely we'll change the model here in the future, such that +we can pass tuples of different parameter types into a single function +implementation. + +## Methods + +Calling a *const* method is simple: + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +"", +" +class Sloth { +public: + void sleep() const {} // sloths unchanged by sleep +}; +", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("Sloth") +} + +fn main() { + let sloth = ffi::Sloth::make_unique(); + sloth.sleep(); + sloth.sleep(); +} +} +) +``` + +Calling a non-const method is a bit more of a pain. Per `cxx` norms, all mutable +references to C++ objects must be [pinned](https://doc.rust-lang.org/std/pin/). +In practice, this means you must call [`.pin_mut()`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html#method.pin_mut) +every time you call a method: + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +"", +" +class Sloth { +public: + void unpeel_from_tree() {} // sloths get agitated when removed from + // trees, probably shouldn't be const +}; +", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("Sloth") +} + +fn main() { + let mut sloth = ffi::Sloth::make_unique(); + sloth.pin_mut().unpeel_from_tree(); + sloth.pin_mut().unpeel_from_tree(); +} +} +) +``` \ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/cpp_types.md b/third_party/rust/autocxx/v0_17/crate/book/src/cpp_types.md new file mode 100644 index 0000000..3a6f3ea --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/cpp_types.md
@@ -0,0 +1,177 @@ +# C++ structs, enums and classes + +If you add a C++ struct, class or enum to the [allowlist](allowlist.md), Rust bindings will be generated to that type and to any methods it has. +Even if you don't add it to the allowlist, the type may be generated if it's required by some other function - but in this case +all its methods won't be generated. + +Rust and C++ differ in an important way: + +* In Rust, the compiler is free to pick up some data and move it to somewhere else (in a `memcpy` sense). The object is none the wiser. +* In C++, once created, an object stays where it is, until or unless it has its "move constructor" invoked. + +This makes a big difference: C++ objects can have self-referential pointers, and any such pointer would be invalidated by Rust doing +a memcpy. Such self-referential pointers are common - even some implementations of `std::string` do it. + +## POD and non-POD + +When asking `autocxx` to generate bindings for a type, then, you have to make a choice. + +* *This C++ type is trivial*. It has no destructor or move constructor (or they're trivial), and thus Rust is free to move it around the stack as it wishes. `autocxx` calls these types POD ("plain old data"). Alternatively, +* *This C++ type has a non-trivial destructor or move constructor, so we can't allow Rust to move this around*. `autocxx` calls these types non-POD. + +POD types are nicer: + +* You can just use them as regular Rust types. +* You get direct field access. +* No funny business. + +Non-POD types are awkward: + +* You can't just _have_ one as a Rust variable. Normally you hold them in a [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html), though there are other options. +* There is no access to fields (yet). +* You can't even have a `&mut` reference to one, because then you might be able to use [`std::mem::swap`](https://doc.rust-lang.org/stable/std/mem/fn.swap.html) or similar. You can have a `Pin<&mut>` reference, which is more fiddly. + +By default, `autocxx` generates non-POD types. You can request a POD type using [`generate_pod!`](https://docs.rs/autocxx/latest/autocxx/macro.generate_pod.html). Don't worry: you can't mess this up. If the C++ type doesn't in fact comply with the requirements for a POD type, your build will fail thanks to some static assertions generated in the C++. (If you're _really_ sure your type is freely relocatable, because you implemented the move constructor and destructor and you promise they're trivial, you can override these assertions using the C++ trait `IsRelocatable` per the instructions in [cxx.h](https://github.com/dtolnay/cxx/blob/master/include/cxx.h)). + +See [the chapter on storage](storage.md) for lots more detail on how you can hold onto non-POD types. + +## Construction + +Each constructor results in _two_ Rust functions. + +* A `new` function exists, which + offers a constructor per the standards of the `moveit` crate, and thus can be used + to place the object on the Rust stack (as well as in various containers such as `Box` + and `UniquePtr`) +* A `make_unique` function is also created, which constructs the item directly into + a `cxx::UniquePtr`. This is more commonly what you want. + +Multiple constructors (aka constructor overloading) follows the same [rules as other functions](cpp_functions.html#overloads---and-identifiers-ending-in-digits). + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +"void A::set(uint32_t val) { a = val; } +uint32_t A::get() const { return a; }", +"#include <stdint.h> +#include <string> +struct A { + A() {} + void set(uint32_t val); + uint32_t get() const; + uint32_t a; +}; +", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("A") +} + +fn main() { + moveit! { + let mut stack_obj = ffi::A::new(); + } + stack_obj.as_mut().set(42); + assert_eq!(stack_obj.get(), 42); + + let mut heap_obj = ffi::A::make_unique(); + heap_obj.pin_mut().set(42); + assert_eq!(heap_obj.get(), 42); +} +} +) +``` + +## Forward declarations + +A type which is incomplete in the C++ headers (i.e. represented only by a forward +declaration) can't be held in a `UniquePtr` within Rust (because Rust can't know +if it has a destructor that will need to be called if the object is dropped.) +Naturally, such an object can't be passed by value either; it can still be +referenced in Rust references. + +## Generic (templated) types + +If you're using one of the generic types which is supported natively by cxx, +e.g. `std::unique_ptr`, it should work as you expect. For other generic types, +we synthesize a concrete Rust type, corresponding to a C++ typedef, for each +concrete instantiation of the type. Such generated types are always opaque, +and never have methods attached. That's therefore enough to pass them +between return types and parameters of other functions within [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html)s +but not really enough to do anything else with these types yet[^templated]. + +[^templated]: Future improvements tracked [here](https://github.com/google/autocxx/issues/349) + +To make them more useful, you might have to add extra C++ functions to extract +data or otherwise deal with them. + +## Implicit member functions + +Most of the API of a C++ type is contained within the type, so `autocxx` can +understand what is available for Rust to call when that type is analyzed. +However, there is an important exception for the so-called special +member functions, which will be implicitly generated by the C++ compiler for +some types. `autocxx` makes use of these types of special members: +* Default constructor +* Destructor +* Copy constructor +* Move constructor + +Explicitly declared versions of these special members are easy: `autocxx` knows +they exist and uses them. + +`autocxx` currently uses its own analysis to determine when implicit versions of +these exist. This analysis tries to be conservative (avoid generating wrappers +that require the existence of C++ functions that don't exist), but sometimes +this goes wrong and understanding the details is necessary to get the correct +Rust wrappers generated. + +In particular, determing whether an implicit version of any of these exists +requires analyzing the types of all bases and members. `autocxx` only analyzes +types when requested, because some may be un-analyzable. If the types of any +bases or members are not analyzed, `autocxx` will assume a public destructor +exists (in the absence of any other destructors), and avoid using any other +implicit special member functions. Notably this includes the default +constructor, so types with un-analyzed bases or members and no explicit +constructors will not get a `make_unique` or `new` generated. If `autocxx` isn't +generating a `make_unique` or `CopyNew` or `MoveNew` for a type which permits +the corresponding operations in C++, make sure the types of all bases and +members are analyzed or implement it explicitly. + +`autocxx` currently does not take member initializers (`const int x = 5`) into +account when determining whether a default constructor +exists[^member-initializers]. Explicitly declared default destructors still +work though. + +Currently, `autocxx` assumes that an explicitly defaulted (`= default`) member +function exists, although it is valid C++ for that to be +deleted[^explicitly-defaulted]. Clang's +[-Wdefaulted-function-deleted](https://clang.llvm.org/docs/DiagnosticsReference.html#wdefaulted-function-deleted) +flag (enabled by default) will warn about types like this. + +A C++ type which can be instantiated but has an inaccessible constructor will +be leaked by Rust[^inaccessible-destructor]. The object's memory itself will be +freed without calling any C++ destructor, which will leak any resources tracked +by the C++ implementation. + +Many of the special members may be overloaded in C++. This generally means +adding `const` or `volatile` qualifiers or extra arguments with defaults. +`autocxx` avoids using any overloaded special members because choosing which +one to call from Rust gets tricky. + +[^member-initializers]: Handling of member initializers is tracked +[here](https://github.com/google/autocxx/issues/816). +[^explicitly-defaulted]: Fix for explicitly defaulted special member functions +that are deleted is tracked [here](https://github.com/google/autocxx/issues/815). +[^inaccessible-destructor]: Discussion around what to do about inaccessible or +deleted destructors [here](https://github.com/google/autocxx/issues/829). + +## Abstract types + +`autocxx` does not allow instantiation of abstract types[^abstract] (aka types with pure virtual methods). + +[^abstract]: `autocxx`'s determination of abstract types is a bit approximate and +[could be improved](https://github.com/google/autocxx/issues/774). \ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/credits.md b/third_party/rust/autocxx/v0_17/crate/book/src/credits.md new file mode 100644 index 0000000..04b2501e --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/credits.md
@@ -0,0 +1,7 @@ +# Credits + +David Tolnay did much of the hard work here, by inventing the underlying cxx crate, and in fact nearly all of the parsing infrastructure on which this crate depends. `bindgen` is also awesome. This crate stands on the shoulders of giants! + +Thanks to all the other contributors to cxx, bindgen and autocxx. + +And thanks to [all in the Chromium community for inspiring this tool](https://www.chromium.org/Home/chromium-security/memory-safety/rust-and-c-interoperability/). \ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/examples.md b/third_party/rust/autocxx/v0_17/crate/book/src/examples.md new file mode 100644 index 0000000..d0246f5 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/examples.md
@@ -0,0 +1,10 @@ +# Examples + +* [Demo](https://github.com/google/autocxx/tree/main/demo) - simplest possible demo +* [S2 example](https://github.com/google/autocxx/tree/main/examples/s2) - example using S2 geometry library +* [Steam example](https://github.com/google/autocxx/tree/main/examples/steam-mini) - example using (something like) the Steam client library +* [Subclass example](https://github.com/google/autocxx/tree/main/examples/subclass) - example using subclasses +* [Integration tests](https://github.com/google/autocxx/blob/main/integration-tests/src/tests.rs) + - hundreds of small snippets + +Contributions of more examples to the `examples` directory are much appreciated!
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/index.md b/third_party/rust/autocxx/v0_17/crate/book/src/index.md new file mode 100644 index 0000000..59832bb --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/index.md
@@ -0,0 +1,110 @@ +[](https://github.com/google/autocxx) +[](https://crates.io/crates/autocxx) +[](https://docs.rs/autocxx) + +# autocxx — automatic safe interop between Rust and C++ + +Welcome to `autocxx` and thank you for reading! + +Use `autocxx` if you have a large existing C++ codebase and you want to use its types and functions from Rust with maximal safety and minimal fuss. + +`autocxx` is like `bindgen`, in that it enables you to use C++ functions and types from within Rust. But it automates a lot of the fiddly things you need to do with `bindgen` bindings: calling destructors, converting strings, unsafely handling raw pointers. C++ functions and types within `autocxx` bindings should behave naturally and ergonomically, _almost_ as if they were safe Rust functions and types themselves. These ergonomics and safety improvements come from the [`cxx`](https://cxx.rs) project - hence the name of this tool, `autocxx`. + +`autocxx` combines the safety and ergonomics of `cxx` with the automatic bindings generation of `bindgen`. It stands on the shoulders of those giants! + +## When is `autocxx` the right tool? + +Not always: + +* If you are making bindings to C code, as opposed to C++, use [`bindgen`](https://rust-lang.github.io/rust-bindgen/) instead. +* If you can make unrestricted changes to the C++ code, use [`cxx`](https://cxx.rs) instead. +* If your C++ to Rust interface is just a few functions or types, use [`cxx`](https://cxx.rs) instead. + +But sometimes: + +* If you need to call arbitrary functions and use arbitrary types within an existing C++ codebase, use `autocxx`. You're in the right place! +* Like `cxx`, but unlike `bindgen`, `autocxx` helps with calls from C++ to Rust, too. + +## Examples to give you a feel for `autocxx` + +Here's a code example: + +```rust,ignore,autocxx +autocxx_integration_tests::doctest( +"", +"#include <stdint.h> +inline uint32_t do_math(uint32_t a, uint32_t b) { return a+b; }", +{ +// Use all the autocxx types which might be handy. +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("do_math") // allowlist a function +} + +fn main() { + assert_eq!(ffi::do_math(12, 13), 25); +} +} +) +``` + +A more complex example: + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +" +#include <strstream> +void Goat::add_a_horn() { horns++; } +Goat::Goat() : horns(0) {} +Goat::~Goat() {} +std::string Goat::describe() const { + std::ostrstream oss; + std::string plural = horns == 1 ? \"\" : \"s\"; + oss << \"This goat has \" << horns << \" horn\" << plural << \".\"; + return oss.str(); +} +", +"#include <cstdint> +#include <string> + +class Goat { +public: + Goat(); + ~Goat(); + void add_a_horn(); + std::string describe() const; +private: + uint32_t horns; +}; +", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("Goat") // allowlist a type and all its methods +} + +fn main() { + let mut goat = ffi::Goat::make_unique(); // returns a cxx::UniquePtr, i.e. a std::unique_ptr + goat.pin_mut().add_a_horn(); + goat.pin_mut().add_a_horn(); + assert_eq!(goat.describe().as_ref().unwrap().to_string_lossy(), "This goat has 2 horns."); +} +} +) +``` + +This is typical `autocxx` code: the C++ objects behave much like Rust objects, but +sometimes extra steps are required to handle cases like null pointers or converting strings that may not be UTF-8. + +Still, fundamentally, you can interact with C++ objects without using `unsafe` in the majority of cases. +`cxx` takes care of the fundamentals of lifetimes and destructors. + +## How to read this book + +We'd recommend starting with [tutorial](tutorial.md) and then [workflow](workflow.md). \ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/naming.md b/third_party/rust/autocxx/v0_17/crate/book/src/naming.md new file mode 100644 index 0000000..dee6aaa --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/naming.md
@@ -0,0 +1,77 @@ +# Naming + +## Namespaces + +The C++ namespace structure is reflected in mods within the generated +ffi mod. However, at present there is an internal limitation that +autocxx can't handle multiple types with the same identifier, even +if they're in different namespaces. This will be fixed in future. + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +" +void generations::hey_boomer() {} +void submarines::hey_boomer() {}", +" +namespace generations { + void hey_boomer(); +} +namespace submarines { + void hey_boomer(); +} +", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("submarines::hey_boomer") + generate!("generations::hey_boomer") +} + +fn main() { + ffi::generations::hey_boomer(); // insults your elders and betters + ffi::submarines::hey_boomer(); // launches missiles +} +} +) +``` + +## Nested types + +There is support for generating bindings of nested types, with some +restrictions. Currently the C++ type `A::B` will be given the Rust name +`A_B` in the same module as its enclosing namespace. + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +"", +" +struct Turkey { + struct Duck { + struct Hen { + int wings; + }; + }; +}; +", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate_pod!("Turkey_Duck_Hen") +} + +fn main() { + let turducken = ffi::Turkey_Duck_Hen::make_unique(); +} +} +) +``` + +## Overloads + +See [the chapter on C++ functions](cpp_functions.md).
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/other_features.md b/third_party/rust/autocxx/v0_17/crate/book/src/other_features.md new file mode 100644 index 0000000..2cc95cd --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/other_features.md
@@ -0,0 +1,34 @@ +# Other C++ features + +You can make Rust subclasses of C++ classes - as these are mostly used to +implement the Observer pattern, they're documented under [calls from C++ to Rust](rust_calls.md). + +## Exceptions + +Exceptions are not supported. If your C++ code is compiled with exceptions, +you can expect serious runtime explosions. The underlying [`cxx`](https://cxx.rs) crate has +exception support, so it would be possible to add them. + +## Preprocessor symbols + +`#define` and other preprocessor symbols will appear as constants. +At present there is no way to do compile-time disablement of code +(equivalent of `#ifdef`)[^ifdef]. + +[^ifdef]: [This feature](https://github.com/google/autocxx/issues/57) should add ifdef support. + +## String constants + +Whether from a preprocessor symbol or from a C++ `char*` constant, +strings appear as `[u8]` with a null terminator. To get a Rust string, +do this: + +```cpp +#define BOB "Hello" +``` + +``` +# mod ffi { pub static BOB: [u8; 6] = [72u8, 101u8, 108u8, 108u8, 111u8, 0u8]; } +assert_eq!(std::str::from_utf8(&ffi::BOB).unwrap().trim_end_matches(char::from(0)), "Hello"); +``` +
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/primitives.md b/third_party/rust/autocxx/v0_17/crate/book/src/primitives.md new file mode 100644 index 0000000..80e8e90 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/primitives.md
@@ -0,0 +1,78 @@ +# Built-in types + +`autocxx` relies primarily on the [standard cxx types](https://cxx.rs/bindings.html). +In particular you should become familiar with [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html) and [`cxx::CxxString`](https://docs.rs/cxx/latest/cxx/struct.CxxString.html). + +There are a few additional integer types, such as [`c_int`](https://docs.rs/autocxx/latest/autocxx/struct.c_int.html), +which are not yet upstreamed to `cxx`. These are to support those pesky C/C++ integer types +which do not have a predictable number of bits on different machines. + +```rust,ignore,autocxx +autocxx_integration_tests::doctest( +"", +"inline int do_math(int a, int b) { return a+b; }", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("do_math") +} + +fn main() { + assert_eq!(ffi::do_math(c_int(12), c_int(13)), c_int(25)); +} +} +) +``` + +## Strings + +`autocxx` uses [`cxx::CxxString`](https://docs.rs/cxx/latest/cxx/struct.CxxString.html). However, as noted above, we can't +just pass a C++ string by value, so we'll box and unbox it automatically +such that you're really dealing with `UniquePtr<CxxString>` on the Rust +side, even if the API just took or returned a plain old `std::string`. + +However, to ease ergonomics, functions that accept a `std::string` will +actually accept anything that +implements a trait called `ffi::ToCppString`. That may either be a +`UniquePtr<CxxString>` or just a plain old Rust string - which will be +converted transparently to a C++ string. + +This trait, and its implementations, are not present in the `autocxx` +documentation because they're dynamically generated in _your_ code +so that they can call through to a `make_string` implementation in +the C++ that we're injecting into your C++ build system. + +(None of that happens if you use [`exclude_utilities`](https://docs.rs/autocxx/latest/autocxx/macro.exclude_utilities.html), so don't do that.) + +```rust,ignore,autocxx +autocxx_integration_tests::doctest( +"", +"#include <string> +#include <cstdint> +inline uint32_t take_string(std::string a) { return a.size(); }", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("take_string") +} + +fn main() { + assert_eq!(ffi::take_string("hello"), 5) +} +} +) +``` + +If you need to create a blank `UniquePtr<CxxString>` in Rust, such that +(for example) you can pass its mutable reference or pointer into some +pre-existing C++ API, call `ffi::make_string("")` which will return +a blank `UniquePtr<CxxString>`. + +If all you need is a _reference_ to a `CxxString`, you can alternatively use +[`cxx::let_cpp_string`](https://docs.rs/cxx/latest/cxx/macro.let_cxx_string.html).
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/references_etc.md b/third_party/rust/autocxx/v0_17/crate/book/src/references_etc.md new file mode 100644 index 0000000..d0f618d6 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/references_etc.md
@@ -0,0 +1,70 @@ +# Pointers, references, values + +`autocxx` knows how to deal with C++ APIs which take C++ types: +* By value +* By reference (const or not) +* By raw pointer +* By `std::unique_ptr` +* By `std::shared_ptr` +* By `std::weak_ptr` +* (Soon to come) By rvalue reference (that is, as a move parameter) + +(all of this is because the underlying [`cxx`](https://cxx.rs) crate has such versatility). +Some of these have some quirks in the way they're exposed in Rust, described below. + +## Passing between C++ and Rust by value + +See the section on [C++ types](cpp_types.md) for the distinction between POD and non-POD types. +POD types can be passed around however you like. Non-POD types can be passed into functions +in various ways - see [calling C++ functions](cpp_functions.md) for more details. + +## References and pointers + +We follow [`cxx`](https://cxx.rs) norms here. Specifically: + +* A C++ reference becomes a Rust reference +* A C++ pointer becomes a Rust pointer. +* If a reference is returned with an ambiguous lifetime, we don't generate + code for the function +* Pointers require use of `unsafe`, references don't necessarily. + +That last point is key. If your C++ API takes pointers, you're going +to have to use `unsafe`. Similarly, if your C++ API returns a pointer, +you'll have to use `unsafe` to do anything useful with the pointer in Rust. +This is intentional: a pointer from C++ might be subject to concurrent +mutation, or it might have a lifetime that could disappear at any moment. +As a human, you must promise that you understand the constraints around +use of that pointer and that's what the `unsafe` keyword is for. + +Exactly the same issues apply to C++ references _in theory_, but in practice, +they usually don't. Therefore [`cxx`](https://cxx.rs) has taken the view that we can "trust" +a C++ reference to a higher degree than a pointer, and autocxx follows that +lead. In practice, of course, references are rarely return values from C++ +APIs so we rarely have to navel-gaze about the trustworthiness of a +reference. + +(See also the discussion of [`safety`](safety.md) - if you haven't specified +an unsafety policy, _all_ C++ APIs require `unsafe` so the discussion is moot.) + +If you're given a C++ object by pointer, and you want to interact with it, +you'll need to figure out the guarantees attached to the C++ object - most +notably its lifetime. To see some of the decision making process involved +see the [Steam example](https://github.com/google/autocxx/tree/main/examples/steam-mini/src/main.rs). + +## [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html)s tips + +We use [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html) in completely the normal way, but there are a few +quirks which you're more likely to run into with `autocxx`. + +* You'll need to use [`.pin_mut()`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html#method.pin_mut) a lot - + see [the example at the bottom of C++ functions](cpp_functions.md). +* If you need to pass a raw pointer to a function, lots of unsafety is required - something like this: + ```rust,ignore + let mut a = ffi::A::make_unique(); + unsafe { ffi::TakePointerToA(std::pin::Pin::<&mut ffi::A>::into_inner_unchecked(a.pin_mut())) }; + ``` + This may be simplified in future. + +## Rvalue references + +Currently rvalue references (that is, move parameters) are not supported. \ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/rust_calls.md b/third_party/rust/autocxx/v0_17/crate/book/src/rust_calls.md new file mode 100644 index 0000000..ebd37c5 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/rust_calls.md
@@ -0,0 +1,177 @@ +# Callbacks into Rust + +`autocxx` is primarily to allow calls from Rust to C++, but like `cxx` it also allows you to expose Rust APIs to C++. + +You can: +* Declare that Rust types should be available to C++ using [`extern_rust_type`](https://docs.rs/autocxx/latest/autocxx/extern_rust/attr.extern_rust_type.html) +* Make Rust functions available to C++ using [`extern_rust_function`](https://docs.rs/autocxx/latest/autocxx/extern_rust/attr.extern_rust_function.html). +* Allow Rust subclasses of C++ classes. + +This latter option is most commonly used for implementing "listeners" or ["observers"](https://en.wikipedia.org/wiki/Observer_pattern), so is often in practice how C++ will call into Rust. More details below. + +## Subclasses + +There is limited and experimental support for creating Rust subclasses of +C++ classes. (Yes, even more experimental than all the rest of this!) +See [`subclass::CppSubclass`](https://docs.rs/autocxx/latest/autocxx/subclass/trait.CppSubclass.html) for information about how you do this. +This is useful primarily if you want to listen out for messages broadcast +using the C++ observer/listener pattern. + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +" +GoatObserver* obs = NULL; +int goat_feed = 0; + +void register_observer(const GoatObserver& observer) { + obs = const_cast<GoatObserver*>(&observer); +} +void deregister_observer() { + obs = NULL; +}; +void feed_goat() { + goat_feed++; + if (goat_feed > 2 && obs) { + obs->goat_full(); + } +} +", +"#include <memory> +class GoatObserver { +public: + virtual void goat_full() const = 0; + virtual ~GoatObserver() {} +}; + +void register_observer(const GoatObserver& observer); +void deregister_observer(); +void feed_goat(); +", +{ +use autocxx::prelude::*; +use autocxx::subclass::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + generate!("register_observer") + generate!("deregister_observer") + generate!("feed_goat") + subclass!("GoatObserver", MyGoatObserver) +} + +use ffi::*; + +#[is_subclass(superclass("GoatObserver"))] +#[derive(Default)] +pub struct MyGoatObserver; + +impl GoatObserver_methods for MyGoatObserver { + fn goat_full(&self) { + println!("BURP!"); + } +} + +impl Drop for MyGoatObserver { + fn drop(&mut self) { + deregister_observer(); + } +} + +fn main() { + let goat_obs = MyGoatObserver::default_rust_owned(); + // Register a reference to the superclass &ffi::GoatObserver + register_observer(goat_obs.as_ref().borrow().as_ref()); + feed_goat(); + feed_goat(); + feed_goat(); // prints BURP! +} +} +) +``` + +## Subclass ownership + +See [`subclass::CppSubclass`](https://docs.rs/autocxx/latest/autocxx/subclass/trait.CppSubclass.html) +for full details, but you must decide who owns your subclass: + +* C++ owns it +* Rust owns it +* It's self-owned, and only ever frees itself (using [`delete_self`](https://docs.rs/autocxx/latest/autocxx/subclass/trait.CppSubclassSelfOwned.html#method.delete_self)). + +Please be careful: the observer pattern is a minefield for use-after-free bugs. +It's recommended that you wrap any such subclass in some sort of Rust newtype +wrapper which [enforces any ownership invariants](rustic.md) so that users +of your types literally can't make any mistakes. + +## Calling superclass methods + +Each subclass also implements a trait called `<superclass name>_supers` which +includes all superclass methods. You can call methods on that, and if you +don't implement a particular method, that will be used as the default. + +```rust,ignore,autocxx,hidecpp +autocxx_integration_tests::doctest( +"", +" +#include <iostream> +class Dinosaur { +public: + Dinosaur() {} + virtual void eat() const { + std::cout << \"Roarrr!! I ate you!\n\"; + } + virtual ~Dinosaur() {} +}; + +// Currently, autocxx requires at least one 'generate!' call. +inline void do_a_thing() {}; +", +{ +use autocxx::prelude::*; +use autocxx::subclass::*; + +include_cpp! { + #include "input.h" + safety!(unsafe_ffi) + subclass!("Dinosaur", TRex) + subclass!("Dinosaur", Diplodocus) + generate!("do_a_thing") +} + +use ffi::*; + +#[is_subclass(superclass("Dinosaur"))] +#[derive(Default)] +pub struct TRex; + +#[is_subclass(superclass("Dinosaur"))] +#[derive(Default)] +pub struct Diplodocus; + +impl Dinosaur_methods for TRex { + // TRex does NOT implement the 'eat' method + // so C++ behavior will be used +} + +impl Dinosaur_methods for Diplodocus { + fn eat(&self) { + println!("Ahh, some nice juicy leaves."); + // Could call self.eat_super() if we + // developed unexpected carnivorous cravings. + } +} + +fn main() { + let trex = TRex::default_rust_owned(); + trex.borrow().as_ref().eat(); // eats human + let diplo = Diplodocus::default_rust_owned(); + diplo.borrow().as_ref().eat(); // eats shoots and leaves +} +} +) +``` + +## Subclass casting + +Subclasses implement `AsRef` to enable casting to superclasses.
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/rustic.md b/third_party/rust/autocxx/v0_17/crate/book/src/rustic.md new file mode 100644 index 0000000..09511b6 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/rustic.md
@@ -0,0 +1,13 @@ +# Using the generated bindings + +Congratulations, you've built some bindings using `autocxx`! + +But are they Rustic? How can you ensure that users of the bindings get Rust-like safety? + +The C++ API may have documented usage invariants. Your ideal is to encode as many as possible of those into compile-time checks in Rust. + +Some options to consider: + +* Wrap the bindings in a newtype wrapper which enforces compile-time variants in its APIs; for example, taking a mutable reference to enforce exclusive access. +* Add extra `impl` blocks to add methods with a more Rustic API. +* Read [the C++ to Rust design FAQ](https://cppfaq.rs).
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/safety.md b/third_party/rust/autocxx/v0_17/crate/book/src/safety.md new file mode 100644 index 0000000..34724d2 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/safety.md
@@ -0,0 +1,91 @@ +# Safety + +## Unsafety policies + +By default, every `autocxx` function is `unsafe`. That means you can only call C++ functions from `unsafe` blocks, and it's up to you to be sure that the C++ code upholds the invariants the Rust compiler expects. + +You can optionally specify: + +`safety!(unsafe)` + +within your `include_cpp!` macro invocation. If you do this, you are promising the Rust compiler that _all_ your C++ function calls are upholding the invariants which `rustc` expects, and thus each individual function is no longer `unsafe`. + +See [`safety!`](https://docs.rs/autocxx/latest/autocxx/macro.safety.html) in the documentation for more details. + +## Examples with and without `safety!(unsafe)` + +Without a `safety!` directive: + +```rust,ignore,autocxx +autocxx_integration_tests::doctest( +"", +"#include <cstdint> +inline uint32_t do_math(uint32_t a, uint32_t b) { return a+b; }", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + generate!("do_math") +} + +fn main() { + assert_eq!(unsafe { ffi::do_math(12, 13) }, 25); +} +} +) +``` + +With a `safety!` directive: + +```rust,ignore,autocxx +autocxx_integration_tests::doctest( +"", +"#include <cstdint> +inline uint32_t do_math(uint32_t a, uint32_t b) { return a+b; }", +{ +use autocxx::prelude::*; + +include_cpp! { + #include "input.h" + safety!(unsafe) + generate!("do_math") +} + +fn main() { + assert_eq!(ffi::do_math(12, 13), 25); +} +} +) +``` + + +## Pragmatism in a complex C++ codebase + +This crate mostly intends to follow the lead of the `cxx` crate in where and when `unsafe` is required. But, this crate is opinionated. It believes some unsafety requires more careful review than other bits, along the following spectrum: + +* Rust unsafe code (requires most review) +* Rust code calling C++ with raw pointers +* Rust code calling C++ with shared pointers, or anything else where there can be concurrent mutation +* Rust code calling C++ with unique pointers, where the Rust single-owner model nearly always applies (but we can't _prove_ that the C++ developer isn't doing something weird) +* Rust safe code (requires least review) + +If your project is 90% Rust code, with small bits of C++, _don't use this crate_. You need something where all C++ interaction is marked with big red "this is terrifying" flags. This crate is aimed at cases where there's 90% C++ and small bits of Rust, and so we want the Rust code to be pragmatically reviewable without the signal:noise ratio of `unsafe` in the Rust code becoming so bad that `unsafe` loses all value. + +## Worked example + +Imagine you have this C++: + +```cpp +struct Thing; +void print_thing(const Thing& thing); +``` + +By using `autocxx` (or `cxx`), you're promising the Rust compiler that the `print_thing` function does sensible things with that +reference: + +* It doesn't store a pointer to the thing anywhere and pass it back to Rust later. +* It doesn't mutate it. +* It doesn't delete it. +* or any of the other things that you're not permitted to do in unsafe Rust. +
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/storage.md b/third_party/rust/autocxx/v0_17/crate/book/src/storage.md new file mode 100644 index 0000000..5f79ca96 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/storage.md
@@ -0,0 +1,24 @@ +# Storage - stack and heaps + +Ensure you understand the distinction between [POD and non-POD types described in the C++ types section before proceeding](cpp_types.md). + +## POD types + +POD types are just regular Rust types! Store them on the stack, heap, in a `Vec`, a `HashMap`, whatever you want. + +## Non-POD types + +Non-POD types can be stored: + +* In a [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html). This is cxx's Rust wrapper for `std::unique_ptr` - so the object is stored in the C++ heap. Most of the time you handle a C++ object from `autocxx`, it will be stored in one of these. +* In a [`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) - so the object is stored on the Rust heap. This is kind of pointless. Don't do this. +* On the Rust stack, using the [`autocxx::moveit`](https://docs.rs/moveit/latest/moveit/macro.moveit.html) macro. + +If in doubt, use [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html). It's simple and ergonomic. + +See [C++ types](cpp_types.md) for a code example showing a type existing on both the stack and the heap. + +## Whose heap is it anyway? + +Specifically [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html) is a binding to `std::unique_ptr<T,std::default_delete<T>>` which means the object will be deleted using the C++ `delete` operator. This will respect any overridden `operator delete` on the type, and similarly, the functions which `autocxx` provides to _construct_ types should respect overridden `operator new`. This means: if your C++ type has code to create itself in some special or unusual heap partition, that should work fine. +
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/tutorial.md b/third_party/rust/autocxx/v0_17/crate/book/src/tutorial.md new file mode 100644 index 0000000..96b3786b --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/tutorial.md
@@ -0,0 +1,67 @@ +# Tutorial + +If you're here, you want to call some C++ from Rust, right? + +Let's assume you're calling into some _existing_ C++ code. + +You will need: + +* Some C++ header files (`.h` files) +* The C++ "include path". That is, the set of directories containing those headers. (That's not necessarily the directory in which each header _file_ lives; C++ might contain `#include "foo/bar.h"` and so your include path would need to include the directory _containing_ the `foo` directory). +* A list of the APIs (types and functions) from those header files which you wish to make available in Rust. +* To know how to link the C++ libraries into your Cargo project. This is beyond the scope of what `autocxx` helps with, but one solution is to emit a print from your [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib). +* [LLVM to be installed](https://rust-lang.github.io/rust-bindgen/requirements.html). +* Some patience. This is not a magic solution. C++/Rust interop is hard. Avoid it if you can! + +The rest of this 'getting started' section assumes Cargo - if you're using something else, see the [building](building.md) section. + +First, add `autocxx` *and `cxx`* to your `dependencies` and `autocxx-build` to your `build-dependencies` in your `Cargo.toml`. + +```toml +[dependencies] +autocxx = "0.17.2" +cxx = "1.0" + +[build-dependencies] +autocxx-build = "0.17.2" +``` + +Now, add a `build.rs` next to your `Cargo.toml` (this is a standard `cargo` [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html)). This is where you need your include path: + +```rust,ignore +fn main() { + let path = std::path::PathBuf::from("src"); // include path + let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).expect_build(); + // This assumes all your C++ bindings are in main.rs + b.flag_if_supported("-std=c++14") + .compile("autocxx-demo"); // arbitrary library name, pick anything + println!("cargo:rerun-if-changed=src/main.rs"); + // Add instructions to link to any C++ libraries you need. +} +``` + +See [the standard cargo build script output mechanisms for how you can direct Rust to link against pre-existing libraries](https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script), and see the [building section of this book for more details about build options - for example, enabling C++17](building.md). + +Finally, in your `main.rs` you can use the [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html) macro which is the heart of `autocxx`: + +```rust,ignore +use autocxx::prelude::*; // use all the main autocxx functions + +include_cpp! { + #include "my_header.h" // your header file name + safety!(unsafe) // see details of unsafety policies described in the 'safety' section of the book + generate!("DeepThought") // add this line for each function or type you wish to generate +} +``` + +You should then find you can call the function by referring to an `ffi` namespace: + +```rust,ignore +fn main() { + println!("The answer to Life, The Universe and Everything is {}", ffi::DeepThought()); +} +``` + +C++ types such as `std::string` and `std::unique_ptr` are represented using the types provided by the marvellous [cxx](https://cxx.rs) library. This provides good ergonomics and safety norms, so unlike with normal `bindgen` bindings, you won't _normally_ need to write `unsafe` code for every function call. + +Next, read the section about [workflows](workflow.md).
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/vscode1.png b/third_party/rust/autocxx/v0_17/crate/book/src/vscode1.png new file mode 100644 index 0000000..c57b4d0 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/vscode1.png Binary files differ
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/vscode2.png b/third_party/rust/autocxx/v0_17/crate/book/src/vscode2.png new file mode 100644 index 0000000..c350617 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/vscode2.png Binary files differ
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/workflow.md b/third_party/rust/autocxx/v0_17/crate/book/src/workflow.md new file mode 100644 index 0000000..65cf3c5 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/book/src/workflow.md
@@ -0,0 +1,89 @@ +# Workflow + +C++ is complex, and `autocxx` can't ingest everything. + +First tip - use an IDE. Type annotation and autocompletion is _incredibly_ helpful in an `autocxx` +context, where you may be dealing with `UniquePtr<T>` and `Option<&T>` and `Pin<&mut T>` very often. + + +As you'll see, it's also _essential_ when `autocxx` can't produce bindings for some reason. + +## What if `autocxx` can't generate bindings? + +This bit is important. + +When you use `autocxx`, you'll ask it to generate Rust bindings for [C++ types or functions](allowlist.md) using +`generate!` directives. + +If you ask to generate bindings for a specific function, and it can't: the build will fail. + +If you ask to generate bindings for an entire type, `autocxx` will generate bindings for as +many methods as possible. For those methods where it can't generate bindings, it will instead +generate some placeholder function or struct with documentation explaining what went wrong: + + + +_This_ is why it's crucial to use an IDE with `autocxx`. (Alternatively, you can use +`cargo expand`, but it's unpleasant.) + +## How to work around cases where `autocxx` can't generate bindings + +Your options are: + +* Write extra C++ functions with simpler parameters or return types, and generate + bindings to them, instead. +* Write some manual `#[cxx::bridge]` bindings - see below. + +## Mixing manual and automated bindings + +`autocxx` uses [`cxx`](https://cxx.rs) underneath, and its build process will happily spot and +process manually-crafted [`cxx::bridge` mods](https://cxx.rs/concepts.html) which you include in your +Rust source code. A common pattern could be to use `autocxx` to generate +all the bindings possible, then hand-craft a `cxx::bridge` mod for the +remainder where `autocxx` falls short. + +To do this, you'll need to use the [ability of one cxx::bridge mod to refer to types from another](https://cxx.rs/extern-c++.html#reusing-existing-binding-types), +for example: + +```rust,ignore +autocxx::include_cpp! { + #include "foo.h" + safety!(unsafe_ffi) + generate!("take_A") + generate!("A") +} +#[cxx::bridge] +mod ffi2 { + unsafe extern "C++" { + include!("foo.h"); + type A = crate::ffi::A; + fn give_A() -> UniquePtr<A>; // in practice, autocxx could happily do this + } +} +fn main() { + let a = ffi2::give_A(); + assert_eq!(ffi::take_A(&a), autocxx::c_int(5)); +} +``` + +## My build entirely failed + +`autocxx` should nearly always successfully parse the C++ codebase and +generate _some_ APIs. It's reliant on `bindgen`, but `bindgen` is excellent +and rarely bails out entirely. + +If it does, you may be able to use the [`block!` macro](https://docs.rs/autocxx/latest/autocxx/macro.block.html). + +We'd appreciate a minimized bug report of the troublesome code - see [contributing](contributing.md). + + +## Enabling autocompletion in a rust-analyzer IDE + +You'll need to enable _both_: +* Rust-analyzer: Proc Macro: Enable +* Rust-analyzer: Experimental: Proc Attr Macros + +## Next steps + +Now you've read what can go wrong with `autocxx`, and how to diagnose problems - the next step is to give it a try! +Treat the rest of this manual as a reference.
diff --git a/third_party/rust/autocxx/v0_17/crate/src/lib.rs b/third_party/rust/autocxx/v0_17/crate/src/lib.rs new file mode 100644 index 0000000..fad1b16f --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/src/lib.rs
@@ -0,0 +1,461 @@ +#![doc = include_str!("../README.md")] + +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// The crazy macro_rules magic in this file is thanks to dtolnay@ +// and is a way of attaching rustdoc to each of the possible directives +// within the include_cpp outer macro. None of the directives actually +// do anything - all the magic is handled entirely by +// autocxx_macro::include_cpp_impl. + +pub mod subclass; +mod value_param; + +#[cfg_attr(doc, aquamarine::aquamarine)] +/// Include some C++ headers in your Rust project. +/// +/// This macro allows you to include one or more C++ headers within +/// your Rust code, and call their functions fairly naturally. +/// +/// # Examples +/// +/// C++ header (`input.h`): +/// ```cpp +/// #include <cstdint> +/// +/// uint32_t do_math(uint32_t a); +/// ``` +/// +/// Rust code: +/// ``` +/// # use autocxx_macro::include_cpp_impl as include_cpp; +/// include_cpp!( +/// # parse_only!() +/// #include "input.h" +/// generate!("do_math") +/// safety!(unsafe) +/// ); +/// +/// # mod ffi { pub fn do_math(a: u32) -> u32 { a+3 } } +/// # fn main() { +/// ffi::do_math(3); +/// # } +/// ``` +/// +/// The resulting bindings will use idiomatic Rust wrappers for types from the [cxx] +/// crate, for example [`cxx::UniquePtr`] or [`cxx::CxxString`]. Due to the care and thought +/// that's gone into the [cxx] crate, such bindings are pleasant and idiomatic to use +/// from Rust, and usually don't require the `unsafe` keyword. +/// +/// For full documentation, see [the manual](https://google.github.io/autocxx/). +/// +/// # The [`include_cpp`] macro +/// +/// Within the braces of the `include_cpp!{...}` macro, you should provide +/// a list of at least the following: +/// +/// * `#include "cpp_header.h"`: a header filename to parse and include +/// * `generate!("type_or_function_name")`: a type or function name whose declaration +/// should be made available to C++. (See the section on Allowlisting, below). +/// * Optionally, `safety!(unsafe)` - see discussion of [`safety`]. +/// +/// Other directives are possible as documented in this crate. +/// +/// Now, try to build your Rust project. `autocxx` may fail to generate bindings +/// for some of the items you specified with [generate] directives: remove +/// those directives for now, then see the next section for advice. +/// +/// # Allowlisting +/// +/// How do you inform autocxx which bindings to generate? There are three +/// strategies: +/// +/// * *Recommended*: provide various [`generate`] directives in the +/// [`include_cpp`] macro. This can specify functions or types. +/// * *Not recommended*: in your `build.rs`, call [`Builder::auto_allowlist`]. +/// This will attempt to spot _uses_ of FFI bindings anywhere in your Rust code +/// and build the allowlist that way. This is experimental and has known limitations. +/// * *Strongly not recommended*: use [`generate_all`]. This will attempt to +/// generate Rust bindings for _any_ C++ type or function discovered in the +/// header files. This is generally a disaster if you're including any +/// remotely complex header file: we'll try to generate bindings for all sorts +/// of STL types. This will be slow, and some may well cause problems. +/// Effectively this is just a debug option to discover such problems. Don't +/// use it! +/// +/// # Internals +/// +/// For documentation on how this all actually _works_, see +/// `IncludeCppEngine` within the `autocxx_engine` crate. +#[macro_export] +macro_rules! include_cpp { + ( + $(#$include:ident $lit:literal)* + $($mac:ident!($($arg:tt)*))* + ) => { + $($crate::$include!{__docs})* + $($crate::$mac!{__docs})* + $crate::include_cpp_impl! { + $(#include $lit)* + $($mac!($($arg)*))* + } + }; +} + +/// Include a C++ header. A directive to be included inside +/// [include_cpp] - see [include_cpp] for details +#[macro_export] +macro_rules! include { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Generate Rust bindings for the given C++ type or function. +/// A directive to be included inside +/// [include_cpp] - see [include_cpp] for general information. +/// See also [generate_pod]. +#[macro_export] +macro_rules! generate { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Generate as "plain old data" and add to allowlist. +/// Generate Rust bindings for the given C++ type such that +/// it can be passed and owned by value in Rust. This only works +/// for C++ types which have trivial move constructors and no +/// destructor - you'll encounter a compile error otherwise. +/// If your type doesn't match that description, use [generate] +/// instead, and own the type using [UniquePtr][cxx::UniquePtr]. +/// A directive to be included inside +/// [include_cpp] - see [include_cpp] for general information. +#[macro_export] +macro_rules! generate_pod { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Generate Rust bindings for all C++ types and functions +/// in a given namespace. +/// A directive to be included inside +/// [include_cpp] - see [include_cpp] for general information. +/// See also [generate]. +#[macro_export] +macro_rules! generate_ns { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Generate Rust bindings for all C++ types and functions +/// found. Highly experimental and not recommended. +/// A directive to be included inside +/// [include_cpp] - see [include_cpp] for general information. +/// See also [generate]. +#[macro_export] +macro_rules! generate_all { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Generate as "plain old data". For use with [generate_all] +/// and similarly experimental. +#[macro_export] +macro_rules! pod { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Skip the normal generation of a `make_string` function +/// and other utilities which we might generate normally. +/// A directive to be included inside +/// [include_cpp] - see [include_cpp] for general information. +#[macro_export] +macro_rules! exclude_utilities { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Entirely block some type from appearing in the generated +/// code. This can be useful if there is a type which is not +/// understood by bindgen or autocxx, and incorrect code is +/// otherwise generated. +/// This is 'greedy' in the sense that any functions/methods +/// which take or return such a type will _also_ be blocked. +/// +/// A directive to be included inside +/// [include_cpp] - see [include_cpp] for general information. +#[macro_export] +macro_rules! block { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Avoid generating implicit constructors for this type. +/// The rules for when to generate C++ implicit constructors +/// are complex, and if autocxx gets it wrong, you can block +/// such constructors using this. +/// +/// A directive to be included inside +/// [include_cpp] - see [include_cpp] for general information. +#[macro_export] +macro_rules! block_constructors { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// The name of the mod to be generated with the FFI code. +/// The default is `ffi`. +/// +/// A directive to be included inside +/// [include_cpp] - see [include_cpp] for general information. +#[macro_export] +macro_rules! name { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Specifies a global safety policy for functions generated +/// from these headers. By default (without such a `safety!` +/// directive) all such functions are marked as `unsafe` and +/// therefore can only be called within an `unsafe {}` block +/// or some `unsafe` function which you create. +/// +/// Alternatively, by specifying a `safety!` block you can +/// declare that most generated functions are in fact safe. +/// Specifically, you'd specify: +/// `safety!(unsafe)` +/// or +/// `safety!(unsafe_ffi)` +/// These two options are functionally identical. If you're +/// unsure, simply use `unsafe`. The reason for the +/// latter option is if you have code review policies which +/// might want to give a different level of scrutiny to +/// C++ interop as opposed to other types of unsafe Rust code. +/// Maybe in your organization, C++ interop is less scary than +/// a low-level Rust data structure using pointer manipulation. +/// Or maybe it's more scary. Either way, using `unsafe` for +/// the data structure and using `unsafe_ffi` for the C++ +/// interop allows you to apply different linting tools and +/// policies to the different options. +/// +/// Irrespective, C++ code is of course unsafe. It's worth +/// noting that use of C++ can cause unexpected unsafety at +/// a distance in faraway Rust code. As with any use of the +/// `unsafe` keyword in Rust, *you the human* are declaring +/// that you've analyzed all possible ways that the code +/// can be used and you are guaranteeing to the compiler that +/// no badness can occur. Good luck. +/// +/// Generated C++ APIs which use raw pointers remain `unsafe` +/// no matter what policy you choose. +#[macro_export] +macro_rules! safety { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Whether to avoid generating [`cxx::UniquePtr`] and [`cxx::Vector`] +/// implementations. This is primarily useful for reducing test cases and +/// shouldn't be used in normal operation. +/// +/// A directive to be included inside +/// [include_cpp] - see [include_cpp] for general information. +#[macro_export] +macro_rules! exclude_impls { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// Deprecated - use [`extern_rust_type`] instead. +#[macro_export] +#[deprecated] +macro_rules! rust_type { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// See [`extern_rust::extern_rust_type`]. +#[macro_export] +macro_rules! extern_rust_type { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +/// See [`subclass::subclass`]. +#[macro_export] +macro_rules! subclass { + ($($tt:tt)*) => { $crate::usage!{$($tt)*} }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! usage { + (__docs) => {}; + ($($tt:tt)*) => { + compile_error! {r#"usage: include_cpp! { + #include "path/to/header.h" + generate!(...) + generate_pod!(...) + } +"#} + }; +} + +#[doc(hidden)] +pub use autocxx_macro::include_cpp_impl; + +#[doc(hidden)] +pub use autocxx_macro::cpp_semantics; + +macro_rules! ctype_wrapper { + ($r:ident, $c:expr, $d:expr) => { + #[doc=$d] + #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash)] + #[allow(non_camel_case_types)] + #[repr(transparent)] + pub struct $r(pub ::std::os::raw::$r); + + /// # Safety + /// + /// We assert that the namespace and type ID refer to a C++ + /// type which is equivalent to this Rust type. + unsafe impl cxx::ExternType for $r { + type Id = cxx::type_id!($c); + type Kind = cxx::kind::Trivial; + } + + impl From<::std::os::raw::$r> for $r { + fn from(val: ::std::os::raw::$r) -> Self { + Self(val) + } + } + + impl From<$r> for ::std::os::raw::$r { + fn from(val: $r) -> Self { + val.0 + } + } + }; +} + +ctype_wrapper!( + c_ulonglong, + "c_ulonglong", + "Newtype wrapper for an unsigned long long" +); +ctype_wrapper!(c_longlong, "c_longlong", "Newtype wrapper for a long long"); +ctype_wrapper!(c_ulong, "c_ulong", "Newtype wrapper for an unsigned long"); +ctype_wrapper!(c_long, "c_long", "Newtype wrapper for a long"); +ctype_wrapper!( + c_ushort, + "c_ushort", + "Newtype wrapper for an unsigned short" +); +ctype_wrapper!(c_short, "c_short", "Newtype wrapper for an short"); +ctype_wrapper!(c_uint, "c_uint", "Newtype wrapper for an unsigned int"); +ctype_wrapper!(c_int, "c_int", "Newtype wrapper for an int"); +ctype_wrapper!(c_uchar, "c_uchar", "Newtype wrapper for an unsigned char"); + +/// Newtype wrapper for a C void. Only useful as a `*c_void` +#[allow(non_camel_case_types)] +#[repr(transparent)] +pub struct c_void(pub ::std::os::raw::c_void); + +/// # Safety +/// +/// We assert that the namespace and type ID refer to a C++ +/// type which is equivalent to this Rust type. +unsafe impl cxx::ExternType for c_void { + type Id = cxx::type_id!(c_void); + type Kind = cxx::kind::Trivial; +} + +/// autocxx couldn't generate these bindings. +/// If you come across a method, type or function which refers to this type, +/// it indicates that autocxx couldn't generate that binding. A documentation +/// comment should be attached indicating the reason. +pub struct BindingGenerationFailure { + _unallocatable: [*const u8; 0], + _pinned: core::marker::PhantomData<core::marker::PhantomPinned>, +} + +/// Tools to export Rust code to C++. +// These are in a mod to avoid shadowing the definitions of the +// directives above, which, being macro_rules, are unavoidably +// in the crate root but must be function-style macros to keep +// the include_cpp impl happy. +pub mod extern_rust { + + /// Declare that this is a Rust type which is to be exported to C++. + /// You can use this in two ways: + /// * as an attribute macro on a Rust type, for instance: + /// ``` + /// # use autocxx_macro::extern_rust_type as extern_rust_type; + /// #[extern_rust_type] + /// struct Bar; + /// ``` + /// * as a directive within the [include_cpp] macro, in which case + /// provide the type path in brackets: + /// ``` + /// # use autocxx_macro::include_cpp_impl as include_cpp; + /// include_cpp!( + /// # parse_only!() + /// #include "input.h" + /// extern_rust_type!(Bar) + /// safety!(unsafe) + /// ); + /// struct Bar; + /// ``` + /// These may be used within references in the signatures of C++ functions, + /// for instance. This will contribute to an `extern "Rust"` section of the + /// generated `cxx` bindings, and this type will appear in the C++ header + /// generated for use in C++. + pub use autocxx_macro::extern_rust_type; + + /// Declare that a given function is a Rust function which is to be exported + /// to C++. This is used as an attribute macro on a Rust function, for instance: + /// ``` + /// # use autocxx_macro::extern_rust_function as extern_rust_function; + /// #[extern_rust_function] + /// pub fn call_me_from_cpp() { } + /// ``` + pub use autocxx_macro::extern_rust_function; +} + +/// Equivalent to [`std::convert::AsMut`], but returns a pinned mutable reference +/// such that cxx methods can be called on it. +pub trait PinMut<T>: AsRef<T> { + /// Return a pinned mutable reference to a type. + fn pin_mut(&mut self) -> std::pin::Pin<&mut T>; +} + +pub use value_param::as_copy; +pub use value_param::as_mov; +pub use value_param::as_new; +pub use value_param::ValueParam; +pub use value_param::ValueParamHandler; + +/// Imports which you're likely to want to use. +pub mod prelude { + pub use crate::as_copy; + pub use crate::as_mov; + pub use crate::as_new; + pub use crate::c_int; + pub use crate::c_long; + pub use crate::c_longlong; + pub use crate::c_short; + pub use crate::c_uchar; + pub use crate::c_uint; + pub use crate::c_ulong; + pub use crate::c_ulonglong; + pub use crate::c_ushort; + pub use crate::c_void; + pub use crate::cpp_semantics; + pub use crate::include_cpp; + pub use crate::PinMut; + pub use crate::ValueParam; + pub use moveit::moveit; + pub use moveit::new::New; +} + +/// Re-export moveit for ease of consumers. +pub use moveit; + +/// Re-export cxx such that clients can use the same version as +/// us. This doesn't enable clients to avoid depending on the cxx +/// crate too, unfortunately, since generated cxx::bridge code +/// refers explicitly to ::cxx. See +/// <https://github.com/google/autocxx/issues/36> +pub use cxx;
diff --git a/third_party/rust/autocxx/v0_16/crate/src/subclass.rs b/third_party/rust/autocxx/v0_17/crate/src/subclass.rs similarity index 93% rename from third_party/rust/autocxx/v0_16/crate/src/subclass.rs rename to third_party/rust/autocxx/v0_17/crate/src/subclass.rs index 564528af..d2827ca 100644 --- a/third_party/rust/autocxx/v0_16/crate/src/subclass.rs +++ b/third_party/rust/autocxx/v0_17/crate/src/subclass.rs
@@ -3,17 +3,11 @@ // Copyright 2021 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://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, @@ -74,11 +68,14 @@ }; } +/// A trait representing the C++ side of a Rust/C++ subclass pair. #[doc(hidden)] pub trait CppSubclassCppPeer: UniquePtrTarget { fn relinquish_ownership(&self); } +/// A type used for how the C++ side of a Rust/C++ subclass pair refers to +/// the Rust side. #[doc(hidden)] pub enum CppSubclassRustPeerHolder<T> { Owned(Rc<RefCell<T>>), @@ -102,6 +99,8 @@ } } +/// A type showing how the Rust side of a Rust/C++ subclass pair refers to +/// the C++ side. #[doc(hidden)] pub enum CppSubclassCppPeerHolder<CppPeer: CppSubclassCppPeer> { Empty, @@ -121,6 +120,8 @@ CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"), CppSubclassCppPeerHolder::Owned(peer) => peer.pin_mut(), CppSubclassCppPeerHolder::Unowned(peer) => unsafe { + // Safety: guaranteed safe because this is a pointer to a C++ object, + // and C++ never moves things in memory. Pin::new_unchecked(peer.as_mut().unwrap()) }, } @@ -129,6 +130,8 @@ match self { CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"), CppSubclassCppPeerHolder::Owned(peer) => peer.as_ref(), + // Safety: guaranteed safe because this is a pointer to a C++ object, + // and C++ never moves things in memory. CppSubclassCppPeerHolder::Unowned(peer) => unsafe { peer.as_ref().unwrap() }, } } @@ -136,6 +139,8 @@ *self = Self::Owned(Box::new(peer)); } fn set_unowned(&mut self, peer: &mut UniquePtr<CppPeer>) { + // Safety: guaranteed safe because this is a pointer to a C++ object, + // and C++ never moves things in memory. *self = Self::Unowned(unsafe { std::pin::Pin::<&mut CppPeer>::into_inner_unchecked(peer.pin_mut()) });
diff --git a/third_party/rust/autocxx/v0_17/crate/src/value_param.rs b/third_party/rust/autocxx/v0_17/crate/src/value_param.rs new file mode 100644 index 0000000..ee21a506 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/src/value_param.rs
@@ -0,0 +1,264 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cxx::{memory::UniquePtrTarget, UniquePtr}; +use moveit::{CopyNew, DerefMove, MoveNew, New}; +use std::{marker::PhantomPinned, mem::MaybeUninit, ops::Deref, pin::Pin}; + +/// A trait representing a parameter to a C++ function which is received +/// by value. +/// +/// Rust has the concept of receiving parameters by _move_ or by _reference_. +/// C++ has the concept of receiving a parameter by 'value', which means +/// the parameter gets copied. +/// +/// To make it easy to pass such parameters from Rust, this trait exists. +/// It is implemented both for references `&T` and for `UniquePtr<T>`, +/// subject to the presence or absence of suitable copy and move constructors. +/// This allows you to pass in parameters by copy (as is ergonomic and normal +/// in C++) retaining the original parameter; or by move semantics thus +/// destroying the object you're passing in. Simply use a reference if you want +/// copy semantics, or the item itself if you want move semantics. +/// +/// It is not recommended that you implement this trait, nor that you directly +/// use its methods, which are for use by `autocxx` generated code only. +/// +/// # Use of `moveit` traits +/// +/// Most of the implementations of this trait require the type to implement +/// [`CopyNew`], which is simply the `autocxx`/`moveit` way of saying that +/// the type has a copy constructor in C++. +/// +/// # Being explicit +/// +/// If you wish to explicitly force either a move or a copy of some type, +/// use [`as_mov`] or [`as_copy`]. +/// +/// # Performance +/// +/// At present, some additional copying occurs for all implementations of +/// this trait other than that for [`cxx::UniquePtr`]. In the future it's +/// hoped that the implementation for `&T where T: CopyNew` can also avoid +/// this extra copying. +/// +/// # Panics +/// +/// The implementations of this trait which take a [`cxx::UniquePtr`] will +/// panic if the pointer is NULL. +/// +/// # Safety +/// +/// Implementers must guarantee that the pointer returned by `get_ptr` +/// is of the correct size and alignment of `T`. +pub unsafe trait ValueParam<T> { + /// Any stack storage required. If, as part of passing to C++, + /// we need to store a temporary copy of the value, this will be `T`, + /// otherwise `()`. + #[doc(hidden)] + type StackStorage; + /// Populate the stack storage given as a parameter. Only called if you + /// return `true` from `needs_stack_space`. + /// + /// # Safety + /// + /// Callers must guarantee that this object will not move in memory + /// between this call and any subsequent `get_ptr` call or drop. + #[doc(hidden)] + unsafe fn populate_stack_space(self, this: Pin<&mut Option<Self::StackStorage>>); + /// Retrieve the pointer to the underlying item, to be passed to C++. + /// Note that on the C++ side this is currently passed to `std::move` + /// and therefore may be mutated. + #[doc(hidden)] + fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T; + #[doc(hidden)] + /// Any special drop steps required for the stack storage. This is not + /// necessary if the `StackStorage` type is something self-dropping + /// such as `UniquePtr`; it's only necessary if it's something where + /// manual management is required such as `MaybeUninit`. + fn do_drop(_stack: Pin<&mut Self::StackStorage>) {} +} + +unsafe impl<T> ValueParam<T> for &T +where + T: CopyNew, +{ + type StackStorage = MaybeUninit<T>; + + unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) { + // Safety: we won't move/swap things within the pin. + let slot = Pin::into_inner_unchecked(stack.as_mut()); + *slot = Some(MaybeUninit::uninit()); + crate::moveit::new::copy(self).new(Pin::new_unchecked(slot.as_mut().unwrap())) + } + fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T { + // Safety: it's OK to (briefly) create a reference to the T because we + // populated it within `populate_stack_space`. It's OK to unpack the pin + // because we're not going to move the contents. + unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut T } + } + + fn do_drop(stack: Pin<&mut Self::StackStorage>) { + // Switch to MaybeUninit::assume_init_drop when stabilized + // Safety: per caller guarantees of populate_stack_space, we know this hasn't moved. + unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) }; + } +} + +unsafe impl<T> ValueParam<T> for UniquePtr<T> +where + T: UniquePtrTarget, +{ + type StackStorage = UniquePtr<T>; + + unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) { + // Safety: we will not move the contents of the pin. + *Pin::into_inner_unchecked(stack.as_mut()) = Some(self) + } + + fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T { + // Safety: we won't move/swap the contents of the outer pin, nor of the + // type stored within the UniquePtr. + unsafe { + (Pin::into_inner_unchecked( + (*Pin::into_inner_unchecked(stack)) + .as_mut() + .expect("Passed a NULL UniquePtr as a C++ value parameter"), + )) as *mut T + } + } +} + +unsafe impl<'a, T: 'a> ValueParam<T> for &'a UniquePtr<T> +where + T: UniquePtrTarget + CopyNew, +{ + type StackStorage = <&'a T as ValueParam<T>>::StackStorage; + + unsafe fn populate_stack_space(self, stack: Pin<&mut Option<Self::StackStorage>>) { + self.as_ref() + .expect("Passed a NULL &UniquePtr as a C++ value parameter") + .populate_stack_space(stack) + } + + fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T { + <&'a T as ValueParam<T>>::get_ptr(stack) + } + + fn do_drop(stack: Pin<&mut Self::StackStorage>) { + <&'a T as ValueParam<T>>::do_drop(stack) + } +} + +/// Explicitly force a value parameter to be taken using any type of [`crate::moveit::new::New`], +/// i.e. a constructor. +pub fn as_new<N: New<Output = T>, T>(constructor: N) -> impl ValueParam<T> { + ByNew(constructor) +} + +/// Explicitly force a value parameter to be taken by copy. +pub fn as_copy<P: Deref<Target = T>, T>(ptr: P) -> impl ValueParam<T> +where + T: CopyNew, +{ + ByNew(crate::moveit::new::copy(ptr)) +} + +/// Explicitly force a value parameter to be taken usign C++ move semantics. +pub fn as_mov<P: DerefMove + Deref<Target = T>, T>(ptr: impl Into<Pin<P>>) -> impl ValueParam<T> +where + P: DerefMove, + P::Target: MoveNew, +{ + ByNew(crate::moveit::new::mov(ptr)) +} + +#[doc(hidden)] +pub struct ByNew<N: New>(N); + +unsafe impl<N, T> ValueParam<T> for ByNew<N> +where + N: New<Output = T>, +{ + type StackStorage = MaybeUninit<T>; + + unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) { + // Safety: we won't move/swap things within the pin. + let slot = Pin::into_inner_unchecked(stack.as_mut()); + *slot = Some(MaybeUninit::uninit()); + self.0.new(Pin::new_unchecked(slot.as_mut().unwrap())) + } + fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T { + // Safety: it's OK to (briefly) create a reference to the T because we + // populated it within `populate_stack_space`. It's OK to unpack the pin + // because we're not going to move the contents. + unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut T } + } + + fn do_drop(stack: Pin<&mut Self::StackStorage>) { + // Switch to MaybeUninit::assume_init_drop when stabilized + // Safety: per caller guarantees of populate_stack_space, we know this hasn't moved. + unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) }; + } +} + +/// Implementation detail for how we pass value parameters into C++. +/// This type is instantiated by auto-generated autocxx code each time we +/// need to pass a value parameter into C++, and will take responsibility +/// for extracting that value parameter from the [`ValueParam`] and doing +/// any later cleanup. +#[doc(hidden)] +pub struct ValueParamHandler<T, VP: ValueParam<T>> { + // We can't populate this on 'new' because the object may move. + // Hence this is an Option - it's None until populate is called. + space: Option<VP::StackStorage>, + _pinned: PhantomPinned, +} + +impl<T, VP: ValueParam<T>> ValueParamHandler<T, VP> { + /// Populate this stack space if needs be. Note safety guarantees + /// on [`get_ptr`]. + /// + /// # Safety + /// + /// Callers must call [`populate`] exactly once prior to calling [`get_ptr`]. + pub unsafe fn populate(self: Pin<&mut Self>, param: VP) { + // Structural pinning, as documented in [`std::pin`]. + param.populate_stack_space(self.map_unchecked_mut(|s| &mut s.space)) + } + + /// Return a pointer to the underlying value which can be passed to C++. + /// + /// Per the unsafety contract of [`populate`], [`populate`] has been called exactly once + /// prior to this call. + pub fn get_ptr(self: Pin<&mut Self>) -> *mut T { + // Structural pinning, as documented in [`std::pin`]. `map_unchecked_mut` doesn't play + // nicely with `unwrap`, so we have to do it manually. + unsafe { + VP::get_ptr(Pin::new_unchecked( + self.get_unchecked_mut().space.as_mut().unwrap(), + )) + } + } +} + +impl<T, VP: ValueParam<T>> Default for ValueParamHandler<T, VP> { + fn default() -> Self { + Self { + space: None, + _pinned: PhantomPinned, + } + } +} + +impl<T, VP: ValueParam<T>> Drop for ValueParamHandler<T, VP> { + fn drop(&mut self) { + if let Some(space) = self.space.as_mut() { + unsafe { VP::do_drop(Pin::new_unchecked(space)) } + } + } +}
diff --git a/third_party/rust/autocxx/v0_16/crate/tools/publish-all.sh b/third_party/rust/autocxx/v0_17/crate/tools/publish-all.sh similarity index 100% rename from third_party/rust/autocxx/v0_16/crate/tools/publish-all.sh rename to third_party/rust/autocxx/v0_17/crate/tools/publish-all.sh
diff --git a/third_party/rust/autocxx/v0_16/crate/tools/upgrade-cxx.sh b/third_party/rust/autocxx/v0_17/crate/tools/upgrade-cxx.sh similarity index 100% rename from third_party/rust/autocxx/v0_16/crate/tools/upgrade-cxx.sh rename to third_party/rust/autocxx/v0_17/crate/tools/upgrade-cxx.sh
diff --git a/third_party/rust/autocxx/v0_17/crate/tools/upgrade-version.sh b/third_party/rust/autocxx/v0_17/crate/tools/upgrade-version.sh new file mode 100755 index 0000000..58ed04a3 --- /dev/null +++ b/third_party/rust/autocxx/v0_17/crate/tools/upgrade-version.sh
@@ -0,0 +1,6 @@ +#!/bin/bash + +OLD=$1 +NEW=$2 +find . -type f -name "Cargo.toml" -print0 | xargs -0 sed -i '' -e "s/$OLD/$NEW/g" +find . -type f -name "*.md" -print0 | xargs -0 sed -i '' -e "s/$OLD/$NEW/g"
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_engine/v0_16/crate/.cargo_vcs_info.json deleted file mode 100644 index e27bcad..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/.cargo_vcs_info.json +++ /dev/null
@@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "1c57dc961febd206a8046db5a9badac9f5f23b74" - }, - "path_in_vcs": "engine" -} \ No newline at end of file
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/abstract_types.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/abstract_types.rs deleted file mode 100644 index ce90f86..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/abstract_types.rs +++ /dev/null
@@ -1,158 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use autocxx_parser::IncludeCppConfig; - -use super::{ - fun::{FnAnalysis, FnKind, FnPhase, MethodKind, TraitMethodKind}, - pod::PodAnalysis, -}; -use crate::conversion::{ - api::TypeKind, - error_reporter::{convert_apis, convert_item_apis}, - ConvertError, -}; -use crate::{conversion::api::Api, types::QualifiedName}; -use std::collections::HashSet; - -/// Spot types with pure virtual functions and mark them abstract. -pub(crate) fn mark_types_abstract( - config: &IncludeCppConfig, - mut apis: Vec<Api<FnPhase>>, -) -> Vec<Api<FnPhase>> { - let mut abstract_types: HashSet<_> = apis - .iter() - .filter_map(|api| match &api { - Api::Function { - analysis: - FnAnalysis { - kind: FnKind::Method(self_ty_name, MethodKind::PureVirtual(_)), - .. - }, - .. - } => Some(self_ty_name.clone()), - _ => None, - }) - .collect(); - - for mut api in apis.iter_mut() { - match &mut api { - Api::Struct { analysis, name, .. } if abstract_types.contains(&name.name) => { - analysis.kind = TypeKind::Abstract; - } - _ => {} - } - } - - // Spot any derived classes (recursively). Also, any types which have a base - // class that's not on the allowlist are presumed to be abstract, because we - // have no way of knowing (as they're not on the allowlist, there will be - // no methods associated so we won't be able to spot pure virtual methods). - let mut iterate = true; - while iterate { - iterate = false; - for mut api in apis.iter_mut() { - match &mut api { - Api::Struct { - analysis: PodAnalysis { bases, kind, .. }, - .. - } if *kind != TypeKind::Abstract - && (!abstract_types.is_disjoint(bases) - || any_missing_from_allowlist(config, bases)) => - { - *kind = TypeKind::Abstract; - abstract_types.insert(api.name().clone()); - // Recurse in case there are further dependent types - iterate = true; - } - _ => {} - } - } - } - - // We also need to remove any constructors belonging to these - // abstract types. - apis.retain(|api| { - !matches!(&api, - Api::Function { - analysis: - FnAnalysis { - kind: FnKind::Method(self_ty, MethodKind::MakeUnique | MethodKind::Constructor) - | FnKind::TraitMethod{ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, impl_for: self_ty, ..}, - .. - }, - .. - } if abstract_types.contains(self_ty)) - }); - - // Finally, if there are any types which are nested inside other types, - // they can't be abstract. This is due to two small limitations in cxx. - // Imagine we have class Foo { class Bar } - // 1) using "type Foo = super::bindgen::root::Foo_Bar" results - // in the creation of std::unique_ptr code which isn't acceptable - // for an abtract class - // 2) using "type Foo;" isn't possible unless Foo is a top-level item - // within its namespace. Any outer names will be interpreted as namespace - // names and result in cxx generating "namespace Foo { class Bar }"". - let mut results = Vec::new(); - convert_item_apis(apis, &mut results, |api| match api { - Api::Struct { - analysis: - PodAnalysis { - kind: TypeKind::Abstract, - .. - }, - .. - } if api - .cpp_name() - .as_ref() - .map(|n| n.contains("::")) - .unwrap_or_default() => - { - Err(ConvertError::AbstractNestedType) - } - _ => Ok(Box::new(std::iter::once(api))), - }); - results -} - -fn any_missing_from_allowlist(config: &IncludeCppConfig, bases: &HashSet<QualifiedName>) -> bool { - bases - .iter() - .any(|qn| !config.is_on_allowlist(&qn.to_cpp_name())) -} - -pub(crate) fn discard_ignored_functions(apis: Vec<Api<FnPhase>>) -> Vec<Api<FnPhase>> { - // Some APIs can't be generated, e.g. because they're protected. - // Now we've finished analyzing abstract types and constructors, we'll - // convert them to IgnoredI - let mut apis_new = Vec::new(); - convert_apis( - apis, - &mut apis_new, - |name, fun, analysis, name_for_gc| { - analysis.ignore_reason.clone()?; - Ok(Box::new(std::iter::once(Api::Function { - name, - fun, - analysis, - name_for_gc, - }))) - }, - Api::struct_unchanged, - Api::enum_unchanged, - Api::typedef_unchanged, - ); - apis_new -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/ctypes.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/ctypes.rs deleted file mode 100644 index fef56d1..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/ctypes.rs +++ /dev/null
@@ -1,40 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use std::collections::HashMap; - -use syn::Ident; - -use crate::conversion::api::ApiName; -use crate::types::Namespace; -use crate::{conversion::api::Api, known_types::known_types, types::QualifiedName}; - -use super::fun::FnPhase; - -/// Spot any variable-length C types (e.g. unsigned long) -/// used in the [Api]s and append those as extra APIs. -pub(crate) fn append_ctype_information(apis: &mut Vec<Api<FnPhase>>) { - let ctypes: HashMap<Ident, QualifiedName> = apis - .iter() - .flat_map(|api| api.deps()) - .filter(|ty| known_types().is_ctype(ty)) - .map(|ty| (ty.get_final_ident(), ty)) - .collect(); - for (id, typename) in ctypes { - apis.push(Api::CType { - name: ApiName::new(&Namespace::new(), id), - typename, - }); - } -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructor_rules.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructor_rules.rs deleted file mode 100644 index d3df4c85..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructor_rules.rs +++ /dev/null
@@ -1,163 +0,0 @@ -// Copyright 2022 Google LLC -// -// 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 -// -// https://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. - -//! Module which understands C++ constructor synthesis rules. - -#[cfg_attr(test, derive(Eq, PartialEq))] -pub(super) struct ImplicitConstructorsNeeded { - pub(super) default_constructor: bool, - pub(super) copy_constructor_taking_t: bool, - pub(super) copy_constructor_taking_const_t: bool, - pub(super) move_constructor: bool, -} - -#[derive(Default)] -pub(super) struct ExplicitItemsFound { - pub(super) move_constructor: bool, - pub(super) copy_constructor: bool, - pub(super) any_other_constructor: bool, - pub(super) any_bases_or_fields_lack_const_copy_constructors: bool, - pub(super) any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors: bool, - pub(super) destructor: bool, - pub(super) any_bases_have_deleted_or_inaccessible_destructors: bool, - pub(super) copy_assignment_operator: bool, - pub(super) move_assignment_operator: bool, - pub(super) has_rvalue_reference_fields: bool, -} - -pub(super) fn determine_implicit_constructors( - explicits: ExplicitItemsFound, -) -> ImplicitConstructorsNeeded { - let any_constructor = - explicits.copy_constructor || explicits.move_constructor || explicits.any_other_constructor; - // If no user-declared constructors of any kind are provided for a class type (struct, class, or union), - // the compiler will always declare a default constructor as an inline public member of its class. - let default_constructor = !any_constructor; - - // If no user-defined copy constructors are provided for a class type (struct, class, or union), - // the compiler will always declare a copy constructor as a non-explicit inline public member of its class. - // This implicitly-declared copy constructor has the form T::T(const T&) if all of the following are true: - // each direct and virtual base B of T has a copy constructor whose parameters are const B& or const volatile B&; - // each non-static data member M of T of class type or array of class type has a copy constructor whose parameters are const M& or const volatile M&. - - // The implicitly-declared or defaulted copy constructor for class T is defined as deleted if any of the following conditions are true: - // T is a union-like class and has a variant member with non-trivial copy constructor; // we don't support unions anyway - // T has a user-defined move constructor or move assignment operator (this condition only causes the implicitly-declared, not the defaulted, copy constructor to be deleted). - // T has non-static data members that cannot be copied (have deleted, inaccessible, or ambiguous copy constructors); - // T has direct or virtual base class that cannot be copied (has deleted, inaccessible, or ambiguous copy constructors); - // T has direct or virtual base class with a deleted or inaccessible destructor; - // T has a data member of rvalue reference type; - let copy_constructor_is_deleted = explicits.move_constructor - || explicits.move_assignment_operator - || explicits.any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors - || explicits.any_bases_have_deleted_or_inaccessible_destructors - || explicits.has_rvalue_reference_fields; - - let (copy_constructor_taking_const_t, copy_constructor_taking_t) = - if explicits.copy_constructor || copy_constructor_is_deleted { - (false, false) - } else if explicits.any_bases_or_fields_lack_const_copy_constructors { - (false, true) - } else { - (true, false) - }; - - // If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true: - // there are no user-declared copy constructors; - // there are no user-declared copy assignment operators; - // there are no user-declared move assignment operators; - // there is no user-declared destructor. - // then the compiler will declare a move constructor - let move_constructor = !(explicits.move_constructor - || explicits.copy_constructor - || explicits.destructor - || explicits.copy_assignment_operator - || explicits.move_assignment_operator); - - ImplicitConstructorsNeeded { - default_constructor, - copy_constructor_taking_t, - copy_constructor_taking_const_t, - move_constructor, - } -} - -#[cfg(test)] -mod tests { - use super::determine_implicit_constructors; - - use super::ExplicitItemsFound; - - #[test] - fn test_simple() { - let inputs = ExplicitItemsFound::default(); - let outputs = determine_implicit_constructors(inputs); - assert_eq!(true, outputs.default_constructor); - assert_eq!(true, outputs.copy_constructor_taking_const_t); - assert_eq!(false, outputs.copy_constructor_taking_t); - assert_eq!(true, outputs.move_constructor); - } - - #[test] - fn test_with_destructor() { - let inputs = ExplicitItemsFound { - destructor: true, - ..Default::default() - }; - let outputs = determine_implicit_constructors(inputs); - assert_eq!(true, outputs.default_constructor); - assert_eq!(true, outputs.copy_constructor_taking_const_t); - assert_eq!(false, outputs.copy_constructor_taking_t); - assert_eq!(false, outputs.move_constructor); - } - - #[test] - fn test_with_pesky_base() { - let inputs = ExplicitItemsFound { - any_bases_or_fields_lack_const_copy_constructors: true, - ..Default::default() - }; - let outputs = determine_implicit_constructors(inputs); - assert_eq!(true, outputs.default_constructor); - assert_eq!(false, outputs.copy_constructor_taking_const_t); - assert_eq!(true, outputs.copy_constructor_taking_t); - assert_eq!(true, outputs.move_constructor); - } - - #[test] - fn test_with_user_defined_move_constructor() { - let inputs = ExplicitItemsFound { - move_constructor: true, - ..Default::default() - }; - let outputs = determine_implicit_constructors(inputs); - assert_eq!(false, outputs.default_constructor); - assert_eq!(false, outputs.copy_constructor_taking_const_t); - assert_eq!(false, outputs.copy_constructor_taking_t); - assert_eq!(false, outputs.move_constructor); - } - - #[test] - fn test_with_user_defined_misc_constructor() { - let inputs = ExplicitItemsFound { - any_other_constructor: true, - ..Default::default() - }; - let outputs = determine_implicit_constructors(inputs); - assert_eq!(false, outputs.default_constructor); - assert_eq!(true, outputs.copy_constructor_taking_const_t); - assert_eq!(false, outputs.copy_constructor_taking_t); - assert_eq!(true, outputs.move_constructor); - } -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructors.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructors.rs deleted file mode 100644 index 27931a2..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructors.rs +++ /dev/null
@@ -1,269 +0,0 @@ -// Copyright 2022 Google LLC -// -// 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 -// -// https://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. - -use std::collections::{HashMap, HashSet}; - -use crate::{ - conversion::{ - analysis::{depth_first::depth_first, pod::PodAnalysis}, - api::{Api, CppVisibility, FuncToConvert, SpecialMemberKind}, - }, - types::QualifiedName, -}; - -use super::{ - implicit_constructor_rules::{ - determine_implicit_constructors, ExplicitItemsFound, ImplicitConstructorsNeeded, - }, - FnAnalysis, FnKind, FnPhase, MethodKind, ReceiverMutability, TraitMethodKind, -}; - -#[derive(Hash, Eq, PartialEq)] -enum ExplicitKind { - MoveConstructor, - ConstCopyConstructor, - NonConstCopyConstructor, - OtherConstructor, - Destructor, - CopyAssignmentOperator, - MoveAssignmentOperator, - DeletedOrInaccessibleCopyConstructor, - DeletedOrInaccessibleDestructor, -} - -#[derive(Hash, Eq, PartialEq)] -struct ExplicitFound { - ty: QualifiedName, - kind: ExplicitKind, -} - -/// If a type has explicit constructors, bindgen will generate corresponding -/// constructor functions, which we'll have already converted to make_unique methods. -/// For types with implicit constructors, we synthesize them here. -/// It is tempting to make this a separate analysis phase, to be run later than -/// the function analysis; but that would make the code much more complex as it -/// would need to output a `FnAnalysisBody`. By running it as part of this phase -/// we can simply generate the sort of thing bindgen generates, then ask -/// the existing code in this phase to figure out what to do with it. -pub(super) fn find_missing_constructors( - apis: &[Api<FnPhase>], -) -> HashMap<QualifiedName, ImplicitConstructorsNeeded> { - let explicits = find_explicit_items(apis); - let mut implicit_constructors_needed = HashMap::new(); - for api in depth_first(apis) { - if let Api::Struct { - name, - analysis: PodAnalysis { - bases, field_types, .. - }, - details, - .. - } = api - { - let name = &name.name; - let find = |kind: ExplicitKind| -> bool { - explicits.contains(&ExplicitFound { - ty: name.clone(), - kind, - }) - }; - let any_bases_or_fields_lack_const_copy_constructors = - bases.iter().chain(field_types.iter()).any(|qn| { - let has_explicit = explicits.contains(&ExplicitFound { - ty: qn.clone(), - kind: ExplicitKind::ConstCopyConstructor, - }); - let has_implicit = implicit_constructors_needed - .get(qn) - .map(|imp: &ImplicitConstructorsNeeded| imp.copy_constructor_taking_const_t) - .unwrap_or_default(); - !has_explicit && !has_implicit - }); - let any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors = - bases.iter().chain(field_types.iter()).any(|qn| { - explicits.contains(&ExplicitFound { - ty: qn.clone(), - kind: ExplicitKind::DeletedOrInaccessibleCopyConstructor, - }) - }); - let any_bases_have_deleted_or_inaccessible_destructors = bases.iter().any(|qn| { - explicits.contains(&ExplicitFound { - ty: qn.clone(), - kind: ExplicitKind::DeletedOrInaccessibleDestructor, - }) - }); - let explicit_items_found = ExplicitItemsFound { - move_constructor: find(ExplicitKind::MoveConstructor), - copy_constructor: find(ExplicitKind::ConstCopyConstructor) - || find(ExplicitKind::NonConstCopyConstructor) - || find(ExplicitKind::DeletedOrInaccessibleCopyConstructor), - any_other_constructor: find(ExplicitKind::OtherConstructor), - any_bases_or_fields_lack_const_copy_constructors, - any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors, - any_bases_have_deleted_or_inaccessible_destructors, - destructor: find(ExplicitKind::Destructor) - || find(ExplicitKind::DeletedOrInaccessibleDestructor), - copy_assignment_operator: find(ExplicitKind::CopyAssignmentOperator), - move_assignment_operator: find(ExplicitKind::MoveAssignmentOperator), - has_rvalue_reference_fields: details.has_rvalue_reference_fields, - }; - let implicits = determine_implicit_constructors(explicit_items_found); - implicit_constructors_needed.insert(name.clone(), implicits); - } - } - implicit_constructors_needed -} - -fn find_explicit_items(apis: &[Api<FnPhase>]) -> HashSet<ExplicitFound> { - apis.iter() - .filter_map(|api| match api { - Api::Function { - analysis: - FnAnalysis { - kind: FnKind::Method(self_ty, MethodKind::Constructor), - .. - }, - .. - } => Some(ExplicitFound { - ty: self_ty.clone(), - kind: ExplicitKind::OtherConstructor, - }), - Api::Function { - analysis: - FnAnalysis { - kind: - FnKind::TraitMethod { - kind: TraitMethodKind::MoveConstructor, - impl_for, - .. - }, - .. - }, - .. - } => Some(ExplicitFound { - ty: impl_for.clone(), - kind: ExplicitKind::MoveConstructor, - }), - Api::Function { - analysis: - FnAnalysis { - kind: - FnKind::TraitMethod { - kind: TraitMethodKind::Destructor, - impl_for, - .. - }, - .. - }, - fun, - .. - } if is_deleted_or_inaccessible(fun) => Some(ExplicitFound { - ty: impl_for.clone(), - kind: ExplicitKind::DeletedOrInaccessibleDestructor, - }), - Api::Function { - analysis: - FnAnalysis { - kind: - FnKind::TraitMethod { - kind: TraitMethodKind::Destructor, - impl_for, - .. - }, - .. - }, - .. - } => Some(ExplicitFound { - ty: impl_for.clone(), - kind: ExplicitKind::Destructor, - }), - Api::Function { - analysis: - FnAnalysis { - kind: - FnKind::TraitMethod { - kind: TraitMethodKind::CopyConstructor, - impl_for, - .. - }, - .. - }, - fun, - .. - } if is_deleted_or_inaccessible(fun) => Some(ExplicitFound { - ty: impl_for.clone(), - kind: ExplicitKind::DeletedOrInaccessibleCopyConstructor, - }), - Api::Function { - analysis: - FnAnalysis { - kind: - FnKind::TraitMethod { - kind: TraitMethodKind::CopyConstructor, - impl_for, - .. - }, - param_details, - .. - }, - .. - } => { - let receiver_mutability = ¶m_details - .iter() - .next() - .unwrap() - .self_type - .as_ref() - .unwrap() - .1; - let kind = match receiver_mutability { - ReceiverMutability::Const => ExplicitKind::ConstCopyConstructor, - ReceiverMutability::Mutable => ExplicitKind::NonConstCopyConstructor, - }; - Some(ExplicitFound { - ty: impl_for.clone(), - kind, - }) - } - Api::Function { - fun, - analysis: - FnAnalysis { - kind: FnKind::Method(self_ty, ..), - .. - }, - .. - } if matches!( - fun.special_member, - Some(SpecialMemberKind::AssignmentOperator) - ) => - { - let is_move_assignment_operator = !fun.references.rvalue_ref_params.is_empty(); - Some(ExplicitFound { - ty: self_ty.clone(), - kind: if is_move_assignment_operator { - ExplicitKind::MoveAssignmentOperator - } else { - ExplicitKind::CopyAssignmentOperator - }, - }) - } - _ => None, - }) - .collect() -} - -fn is_deleted_or_inaccessible(fun: &FuncToConvert) -> bool { - fun.cpp_vis == CppVisibility::Private || fun.is_deleted -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/rust_name_tracker.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/rust_name_tracker.rs deleted file mode 100644 index bd98a5ef..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/rust_name_tracker.rs +++ /dev/null
@@ -1,50 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use std::collections::HashSet; - -/// Type which tracks the uniqueness of the _output_ names from -/// cxx (as dictated by the #[rust_name] attribute). -/// See also `bridge_name_tracker` which tracks -/// the names of the items in the cxx::bridge: there's a big comment -/// there explaining the relationship of all the names. -#[derive(Default)] -pub(crate) struct RustNameTracker { - rust_names_used: HashSet<String>, -} - -impl RustNameTracker { - pub(crate) fn new() -> Self { - Self::default() - } - - /// Is it OK to create a global Rust function with this name - /// in the output of the cxx module? - pub(crate) fn ok_to_use_rust_name(&mut self, rust_name: &str) -> bool { - self.rust_names_used.insert(rust_name.to_string()) - } -} - -#[cfg(test)] -mod tests { - use super::RustNameTracker; - - #[test] - fn test() { - let mut rnt = RustNameTracker::new(); - assert!(rnt.ok_to_use_rust_name("a")); - assert!(!rnt.ok_to_use_rust_name("a")); - assert!(rnt.ok_to_use_rust_name("b")); - } -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/mod.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/mod.rs deleted file mode 100644 index 8453adb4..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/mod.rs +++ /dev/null
@@ -1,27 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -pub(crate) mod abstract_types; -pub(crate) mod casts; -pub(crate) mod ctypes; -mod depth_first; -pub(crate) mod fun; -pub(crate) mod gc; -mod name_check; -pub(crate) mod pod; // hey, that rhymes -pub(crate) mod remove_ignored; -pub(crate) mod tdef; -mod type_converter; - -pub(crate) use name_check::check_names;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs deleted file mode 100644 index fb79c9e..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs +++ /dev/null
@@ -1,76 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use crate::conversion::{ - analysis::fun::function_wrapper::{CppConversionType, TypeConversionPolicy}, - ConvertError, -}; -use crate::known_types::type_lacks_copy_constructor; - -use super::type_to_cpp::{type_to_cpp, CppNameMap}; - -impl TypeConversionPolicy { - pub(super) fn unconverted_type( - &self, - cpp_name_map: &CppNameMap, - ) -> Result<String, ConvertError> { - match self.cpp_conversion { - CppConversionType::FromUniquePtrToValue => self.wrapped_type(cpp_name_map), - _ => self.unwrapped_type_as_string(cpp_name_map), - } - } - - pub(super) fn converted_type(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> { - match self.cpp_conversion { - CppConversionType::FromValueToUniquePtr => self.wrapped_type(cpp_name_map), - _ => self.unwrapped_type_as_string(cpp_name_map), - } - } - - fn unwrapped_type_as_string(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> { - type_to_cpp(&self.unwrapped_type, cpp_name_map) - } - - fn wrapped_type(&self, original_name_map: &CppNameMap) -> Result<String, ConvertError> { - Ok(format!( - "std::unique_ptr<{}>", - self.unwrapped_type_as_string(original_name_map)? - )) - } - - pub(super) fn cpp_conversion( - &self, - var_name: &str, - cpp_name_map: &CppNameMap, - use_rvo: bool, - ) -> Result<String, ConvertError> { - Ok(match self.cpp_conversion { - CppConversionType::None => { - if type_lacks_copy_constructor(&self.unwrapped_type) && !use_rvo { - format!("std::move({})", var_name) - } else { - var_name.to_string() - } - } - CppConversionType::FromUniquePtrToValue | CppConversionType::FromPtrToMove => { - format!("std::move(*{})", var_name) - } - CppConversionType::FromValueToUniquePtr => format!( - "std::make_unique<{}>({})", - self.unconverted_type(cpp_name_map)?, - var_name - ), - }) - } -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/fun_codegen.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/fun_codegen.rs deleted file mode 100644 index 0078688..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/fun_codegen.rs +++ /dev/null
@@ -1,358 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use proc_macro2::TokenStream; -use quote::quote; -use syn::{ - parse::Parser, - parse_quote, - punctuated::Punctuated, - token::{Comma, Unsafe}, - Attribute, FnArg, ForeignItem, Ident, ImplItem, Item, ReturnType, -}; - -use super::{ - unqualify::{unqualify_params, unqualify_ret_type}, - RsCodegenResult, Use, -}; -use crate::{ - conversion::{ - analysis::fun::{ - ArgumentAnalysis, FnAnalysis, FnKind, MethodKind, RustRenameStrategy, - TraitMethodDetails, UnsafetyNeeded, - }, - api::ImplBlockDetails, - codegen_rs::lifetime::add_lifetime_to_all_params, - }, - types::{Namespace, QualifiedName}, -}; -use crate::{ - conversion::{api::FuncToConvert, codegen_rs::lifetime::add_explicit_lifetime_if_necessary}, - types::make_ident, -}; - -impl UnsafetyNeeded { - fn bridge_token(&self) -> Option<Unsafe> { - match self { - UnsafetyNeeded::None => None, - _ => Some(parse_quote! { unsafe }), - } - } - - fn wrapper_token(&self) -> Option<Unsafe> { - match self { - UnsafetyNeeded::Always => Some(parse_quote! { unsafe }), - _ => None, - } - } -} - -pub(super) fn gen_function( - ns: &Namespace, - fun: FuncToConvert, - analysis: FnAnalysis, - cpp_call_name: String, -) -> RsCodegenResult { - if analysis.ignore_reason.is_err() || !analysis.externally_callable { - return RsCodegenResult::default(); - } - let cxxbridge_name = analysis.cxxbridge_name; - let rust_name = analysis.rust_name; - let ret_type = analysis.ret_type; - let param_details = analysis.param_details; - let wrapper_function_needed = analysis.cpp_wrapper.is_some(); - let params = analysis.params; - let vis = analysis.vis; - let kind = analysis.kind; - let doc_attr = fun.doc_attr; - - let mut cpp_name_attr = Vec::new(); - let mut impl_entry = None; - let mut trait_impl_entry = None; - let rust_name_attr: Vec<_> = match &analysis.rust_rename_strategy { - RustRenameStrategy::RenameUsingRustAttr => Attribute::parse_outer - .parse2(quote!( - #[rust_name = #rust_name] - )) - .unwrap(), - _ => Vec::new(), - }; - let wrapper_unsafety = analysis.requires_unsafe.wrapper_token(); - let fn_generator = FnGenerator { - param_details: ¶m_details, - cxxbridge_name: &cxxbridge_name, - rust_name: &rust_name, - unsafety: &wrapper_unsafety, - doc_attr: &doc_attr, - }; - let mut materialization = match kind { - FnKind::Method(..) | FnKind::TraitMethod { .. } => None, - FnKind::Function => match analysis.rust_rename_strategy { - RustRenameStrategy::RenameInOutputMod(alias) => { - Some(Use::UsedFromCxxBridgeWithAlias(alias)) - } - _ => Some(Use::UsedFromCxxBridge), - }, - }; - let any_param_needs_rust_conversion = param_details - .iter() - .any(|pd| pd.conversion.rust_work_needed()); - let rust_wrapper_needed = match kind { - FnKind::TraitMethod { .. } => true, - FnKind::Method(..) => any_param_needs_rust_conversion || cxxbridge_name != rust_name, - _ => any_param_needs_rust_conversion, - }; - if rust_wrapper_needed { - match kind { - FnKind::Method(ref type_name, MethodKind::Constructor) => { - // Constructor. - impl_entry = Some(fn_generator.generate_constructor_impl(type_name)); - } - FnKind::Method(ref type_name, ref method_kind) => { - // Method, or static method. - impl_entry = Some(fn_generator.generate_method_impl( - matches!( - method_kind, - MethodKind::MakeUnique | MethodKind::Constructor - ), - type_name, - &ret_type, - )); - } - FnKind::TraitMethod { ref details, .. } => { - trait_impl_entry = Some(fn_generator.generate_trait_impl(details, &ret_type)); - } - _ => { - // Generate plain old function - materialization = Some(Use::Custom(fn_generator.generate_function_impl(&ret_type))); - } - } - } - if cxxbridge_name != cpp_call_name && !wrapper_function_needed { - cpp_name_attr = Attribute::parse_outer - .parse2(quote!( - #[cxx_name = #cpp_call_name] - )) - .unwrap(); - } - // In very rare occasions, we might need to give an explicit lifetime. - let (lifetime_tokens, params, ret_type) = - add_explicit_lifetime_if_necessary(¶m_details, params, &ret_type); - - // Finally - namespace support. All the Types in everything - // above this point are fully qualified. We need to unqualify them. - // We need to do that _after_ the above wrapper_function_needed - // work, because it relies upon spotting fully qualified names like - // std::unique_ptr. However, after it's done its job, all such - // well-known types should be unqualified already (e.g. just UniquePtr) - // and the following code will act to unqualify only those types - // which the user has declared. - let params = unqualify_params(params); - let ret_type = unqualify_ret_type(ret_type.into_owned()); - // And we need to make an attribute for the namespace that the function - // itself is in. - let namespace_attr = if ns.is_empty() || wrapper_function_needed { - Vec::new() - } else { - let namespace_string = ns.to_string(); - Attribute::parse_outer - .parse2(quote!( - #[namespace = #namespace_string] - )) - .unwrap() - }; - // At last, actually generate the cxx::bridge entry. - let bridge_unsafety = analysis.requires_unsafe.bridge_token(); - let extern_c_mod_item = ForeignItem::Fn(parse_quote!( - #(#namespace_attr)* - #(#rust_name_attr)* - #(#cpp_name_attr)* - #doc_attr - #vis #bridge_unsafety fn #cxxbridge_name #lifetime_tokens ( #params ) #ret_type; - )); - RsCodegenResult { - extern_c_mod_items: vec![extern_c_mod_item], - bridge_items: Vec::new(), - global_items: trait_impl_entry.into_iter().collect(), - bindgen_mod_items: Vec::new(), - impl_entry, - materializations: materialization.into_iter().collect(), - extern_rust_mod_items: Vec::new(), - } -} - -/// Knows how to generate a given function. -#[derive(Clone)] -struct FnGenerator<'a> { - param_details: &'a [ArgumentAnalysis], - cxxbridge_name: &'a Ident, - rust_name: &'a str, - unsafety: &'a Option<Unsafe>, - doc_attr: &'a Option<Attribute>, -} - -impl<'a> FnGenerator<'a> { - fn generate_arg_lists(&self, avoid_self: bool) -> (Punctuated<FnArg, Comma>, Vec<TokenStream>) { - let mut wrapper_params: Punctuated<FnArg, Comma> = Punctuated::new(); - let mut arg_list = Vec::new(); - - for pd in self.param_details { - let type_name = pd.conversion.rust_wrapper_unconverted_type(); - let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self { - parse_quote!(self) - } else { - pd.name.clone() - }; - let param_mutability = pd.conversion.rust_conversion.requires_mutability(); - wrapper_params.push(parse_quote!( - #param_mutability #wrapper_arg_name: #type_name - )); - arg_list.push(pd.conversion.rust_conversion(wrapper_arg_name)); - } - (wrapper_params, arg_list) - } - - /// Generate an 'impl Type { methods-go-here }' item - fn generate_method_impl( - &self, - avoid_self: bool, - impl_block_type_name: &QualifiedName, - ret_type: &ReturnType, - ) -> Box<ImplBlockDetails> { - let (wrapper_params, arg_list) = self.generate_arg_lists(avoid_self); - let (lifetime_tokens, wrapper_params, ret_type) = - add_explicit_lifetime_if_necessary(self.param_details, wrapper_params, ret_type); - let rust_name = make_ident(self.rust_name); - let unsafety = self.unsafety; - let doc_attr = self.doc_attr; - let cxxbridge_name = self.cxxbridge_name; - Box::new(ImplBlockDetails { - item: ImplItem::Method(parse_quote! { - #doc_attr - pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type { - cxxbridge::#cxxbridge_name ( #(#arg_list),* ) - } - }), - ty: impl_block_type_name.get_final_ident(), - }) - } - - /// Generate an 'impl Trait for Type { methods-go-here }' in its entrety. - fn generate_trait_impl(&self, details: &TraitMethodDetails, ret_type: &ReturnType) -> Item { - let (mut wrapper_params, arg_list) = self.generate_arg_lists(details.avoid_self); - if let Some(parameter_reordering) = &details.parameter_reordering { - wrapper_params = Self::reorder_parameters(wrapper_params, parameter_reordering); - } - let (lifetime_tokens, wrapper_params, ret_type) = - add_explicit_lifetime_if_necessary(self.param_details, wrapper_params, ret_type); - let doc_attr = self.doc_attr; - let unsafety = self.unsafety; - let cxxbridge_name = self.cxxbridge_name; - let trait_signature = &details.trait_signature; - let trait_unsafety = &details.trait_unsafety; - let impl_for_specifics = &details.impl_for_specifics; - let method_name = &details.method_name; - let call_body = quote! { - cxxbridge::#cxxbridge_name ( #(#arg_list),* ) - }; - let call_body = if details.trait_call_is_unsafe { - quote! { - unsafe { - #call_body - } - } - } else { - call_body - }; - parse_quote! { - #trait_unsafety impl #lifetime_tokens #trait_signature for #impl_for_specifics { - #doc_attr - #unsafety fn #method_name ( #wrapper_params ) #ret_type { - #call_body - } - } - } - } - - /// Generate a 'impl Type { methods-go-here }' item which is a constructor - /// for use with moveit traits. - fn generate_constructor_impl( - &self, - impl_block_type_name: &QualifiedName, - ) -> Box<ImplBlockDetails> { - let (wrapper_params, arg_list) = self.generate_arg_lists(true); - let mut wrapper_params: Punctuated<FnArg, Comma> = - wrapper_params.into_iter().skip(1).collect(); - let ptr_arg_name = &arg_list[0]; - let rust_name = make_ident(&self.rust_name); - let any_references = self.param_details.iter().any(|pd| pd.was_reference); - let (lifetime_param, lifetime_addition) = if any_references { - add_lifetime_to_all_params(&mut wrapper_params); - (quote! { <'a> }, quote! { + 'a }) - } else { - (quote! {}, quote! {}) - }; - let cxxbridge_name = self.cxxbridge_name; - let body = quote! { - autocxx::moveit::new::by_raw(move |#ptr_arg_name| { - let #ptr_arg_name = #ptr_arg_name.get_unchecked_mut().as_mut_ptr(); - cxxbridge::#cxxbridge_name(#(#arg_list),* ) - }) - }; - let body = if self.unsafety.is_some() { - // No need for `unsafe` inside the function - body - } else { - quote! { - unsafe { #body } - } - }; - let doc_attr = self.doc_attr; - let unsafety = self.unsafety; - Box::new(ImplBlockDetails { - item: ImplItem::Method(parse_quote! { - #doc_attr - pub #unsafety fn #rust_name #lifetime_param ( #wrapper_params ) -> impl autocxx::moveit::new::New<Output=Self> #lifetime_addition { - #body - } - }), - ty: impl_block_type_name.get_final_ident(), - }) - } - - /// Generate a function call wrapper - fn generate_function_impl(&self, ret_type: &ReturnType) -> Box<Item> { - let (wrapper_params, arg_list) = self.generate_arg_lists(false); - let rust_name = make_ident(self.rust_name); - let doc_attr = self.doc_attr; - let unsafety = self.unsafety; - Box::new(Item::Fn(parse_quote! { - #doc_attr - pub #unsafety fn #rust_name ( #wrapper_params ) #ret_type { - cxxbridge::#rust_name ( #(#arg_list),* ) - } - })) - } - - fn reorder_parameters( - params: Punctuated<FnArg, Comma>, - parameter_ordering: &[usize], - ) -> Punctuated<FnArg, Comma> { - let old_params = params.into_iter().collect::<Vec<_>>(); - parameter_ordering - .iter() - .map(|n| old_params.get(*n).unwrap().clone()) - .collect() - } -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/function_wrapper_rs.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/function_wrapper_rs.rs deleted file mode 100644 index c484b58..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/function_wrapper_rs.rs +++ /dev/null
@@ -1,86 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use proc_macro2::TokenStream; -use syn::{Pat, Type, TypePtr}; - -use crate::conversion::analysis::fun::function_wrapper::{ - RustConversionType, TypeConversionPolicy, -}; -use quote::quote; -use syn::parse_quote; - -impl TypeConversionPolicy { - pub(super) fn rust_wrapper_unconverted_type(&self) -> Type { - match self.rust_conversion { - RustConversionType::None => self.converted_rust_type(), - RustConversionType::ToBoxedUpHolder(ref sub) => { - let id = sub.id(); - parse_quote! { autocxx::subclass::CppSubclassRustPeerHolder< - super::super::super:: #id> - } - } - RustConversionType::FromStr => parse_quote! { impl ToCppString }, - RustConversionType::FromPinMaybeUninitToPtr => { - let ty = match &self.unwrapped_type { - Type::Ptr(TypePtr { elem, .. }) => &*elem, - _ => panic!("Not a ptr"), - }; - parse_quote! { - ::std::pin::Pin<&mut ::std::mem::MaybeUninit< #ty >> - } - } - RustConversionType::FromPinMoveRefToPtr => { - let ty = match &self.unwrapped_type { - Type::Ptr(TypePtr { elem, .. }) => &*elem, - _ => panic!("Not a ptr"), - }; - parse_quote! { - ::std::pin::Pin<autocxx::moveit::MoveRef< '_, #ty >> - } - } - RustConversionType::FromTypeToPtr => { - let ty = match &self.unwrapped_type { - Type::Ptr(TypePtr { elem, .. }) => &*elem, - _ => panic!("Not a ptr"), - }; - parse_quote! { &mut #ty } - } - } - } - - pub(super) fn rust_conversion(&self, var: Pat) -> TokenStream { - match self.rust_conversion { - RustConversionType::None => quote! { #var }, - RustConversionType::FromStr => quote! ( #var .into_cpp() ), - RustConversionType::ToBoxedUpHolder(ref sub) => { - let holder_type = sub.holder(); - quote! { - Box::new(#holder_type(#var)) - } - } - RustConversionType::FromPinMaybeUninitToPtr => quote! { - #var.get_unchecked_mut().as_mut_ptr() - }, - RustConversionType::FromPinMoveRefToPtr => quote! { - { let r: &mut _ = ::std::pin::Pin::into_inner_unchecked(#var.as_mut()); - r - } - }, - RustConversionType::FromTypeToPtr => quote! { - #var - }, - } - } -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/impl_item_creator.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/impl_item_creator.rs deleted file mode 100644 index db6e1d0a..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/impl_item_creator.rs +++ /dev/null
@@ -1,39 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use autocxx_parser::IncludeCppConfig; -use syn::{parse_quote, Ident, Item}; - -pub(crate) fn create_impl_items(id: &Ident, movable: bool, config: &IncludeCppConfig) -> Vec<Item> { - if config.exclude_impls { - return vec![]; - } - let mut results = vec![ - Item::Impl(parse_quote! { - impl UniquePtr<#id> {} - }), - Item::Impl(parse_quote! { - impl SharedPtr<#id> {} - }), - Item::Impl(parse_quote! { - impl WeakPtr<#id> {} - }), - ]; - if movable { - results.push(Item::Impl(parse_quote! { - impl CxxVector<#id> {} - })) - } - results -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/doc_attr.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/doc_attr.rs deleted file mode 100644 index 99e4d2d..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/doc_attr.rs +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use syn::Attribute; - -/// Returns the attribute (if any) which contains a doc comment. -pub(super) fn get_doc_attr(attrs: &[Attribute]) -> Option<Attribute> { - attrs - .iter() - .find(|a| a.path.get_ident().iter().any(|p| *p == "doc")) - .cloned() -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/mod.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/mod.rs deleted file mode 100644 index 1c85fe4..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/mod.rs +++ /dev/null
@@ -1,20 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -mod bindgen_semantic_attributes; -mod parse_bindgen; -mod parse_foreign_mod; - -pub(crate) use bindgen_semantic_attributes::BindgenSemanticAttributes; -pub(crate) use parse_bindgen::ParseBindgen;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/utilities.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/utilities.rs deleted file mode 100644 index cfef9e04..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/utilities.rs +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use autocxx_parser::IncludeCppConfig; - -use super::api::{ApiName, UnanalyzedApi}; -use crate::types::{make_ident, Namespace}; - -/// Adds items which we always add, cos they're useful. -/// Any APIs or techniques which do not involve actual C++ interop -/// shouldn't go here, but instead should go into the main autocxx -/// src/lib.rs. -pub(crate) fn generate_utilities(apis: &mut Vec<UnanalyzedApi>, config: &IncludeCppConfig) { - // Unless we've been specifically asked not to do so, we always - // generate a 'make_string' function. That pretty much *always* means - // we run two passes through bindgen. i.e. the next 'if' is always true, - // and we always generate an additional C++ file for our bindings additions, - // unless the include_cpp macro has specified ExcludeUtilities. - apis.push(UnanalyzedApi::StringConstructor { - name: ApiName::new(&Namespace::new(), make_ident(config.get_makestring_name())), - }); -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/parse_callbacks.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/parse_callbacks.rs deleted file mode 100644 index b766b25..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/parse_callbacks.rs +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use std::panic::UnwindSafe; - -use crate::RebuildDependencyRecorder; -use autocxx_bindgen::callbacks::ParseCallbacks; - -#[derive(Debug)] -pub(crate) struct AutocxxParseCallbacks(pub(crate) Box<dyn RebuildDependencyRecorder>); - -impl UnwindSafe for AutocxxParseCallbacks {} - -impl ParseCallbacks for AutocxxParseCallbacks { - fn include_file(&self, filename: &str) { - self.0.record_header_file_dependency(filename); - } -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/parse_file.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/parse_file.rs deleted file mode 100644 index 9064c02c..0000000 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/parse_file.rs +++ /dev/null
@@ -1,324 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 -// -// https://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. - -use crate::ast_discoverer::Discoveries; -use crate::CppCodegenOptions; -use crate::{ - cxxbridge::CxxBridge, Error as EngineError, GeneratedCpp, IncludeCppEngine, - RebuildDependencyRecorder, -}; -use autocxx_parser::directives::SUBCLASS; -use autocxx_parser::{Subclass, SubclassAttrs}; -use proc_macro2::{Span, TokenStream}; -use quote::ToTokens; -use std::{collections::HashSet, fmt::Display, io::Read, path::PathBuf}; -use std::{panic::UnwindSafe, path::Path, rc::Rc}; -use syn::{Item, LitStr}; - -/// Errors which may occur when parsing a Rust source file to discover -/// and interpret include_cxx macros. -#[derive(Debug)] -pub enum ParseError { - /// Unable to open the source file - FileOpen(std::io::Error), - /// The .rs file couldn't be read. - FileRead(std::io::Error), - /// The .rs file couldn't be parsed. - Syntax(syn::Error), - /// The include CPP macro could not be expanded into - /// Rust bindings to C++, because of some problem during the conversion - /// process. This could be anything from a C++ parsing error to some - /// C++ feature that autocxx can't yet handle and isn't able to skip - /// over. It could also cover errors in your syntax of the `include_cpp` - /// macro or the directives inside. - AutocxxCodegenError(EngineError), - /// There are two or more `include_cpp` macros with the same - /// mod name. - ConflictingModNames, - ZeroModsForDynamicDiscovery, - MultipleModsForDynamicDiscovery, - DiscoveredRustItemsWhenNotInAutoDiscover, -} - -impl Display for ParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ParseError::FileOpen(err) => write!(f, "Unable to open file: {}", err)?, - ParseError::FileRead(err) => write!(f, "Unable to read file: {}", err)?, - ParseError::Syntax(err) => write!(f, "Syntax error parsing Rust file: {}", err)?, - ParseError::AutocxxCodegenError(err) => - write!(f, "Unable to parse include_cpp! macro: {}", err)?, - ParseError::ConflictingModNames => - write!(f, "There are two or more include_cpp! macros with the same output mod name. Use name!")?, - ParseError::ZeroModsForDynamicDiscovery => - write!(f, "This file contains extra information to append to an include_cpp! but no such include_cpp! was found in this file.")?, - ParseError::MultipleModsForDynamicDiscovery => - write!(f, "This file contains extra information to append to an include_cpp! but multiple such include_cpp! declarations were found in this file.")?, - ParseError::DiscoveredRustItemsWhenNotInAutoDiscover => - write!(f, "This file contains extra information to append to an \"extern Rust\" but auto-discover was switched off.")?, - } - Ok(()) - } -} - -/// Parse a Rust file, and spot any include_cpp macros within it. -pub fn parse_file<P1: AsRef<Path>>( - rs_file: P1, - auto_allowlist: bool, -) -> Result<ParsedFile, ParseError> { - let mut source = String::new(); - let mut file = std::fs::File::open(rs_file).map_err(ParseError::FileOpen)?; - file.read_to_string(&mut source) - .map_err(ParseError::FileRead)?; - proc_macro2::fallback::force(); - let source = syn::parse_file(&source).map_err(ParseError::Syntax)?; - parse_file_contents(source, auto_allowlist) -} - -fn parse_file_contents(source: syn::File, auto_allowlist: bool) -> Result<ParsedFile, ParseError> { - let mut results = Vec::new(); - let mut extra_superclasses = Vec::new(); - let mut discoveries = Discoveries::default(); - for item in source.items { - results.push(match item { - Item::Macro(mac) - if mac - .mac - .path - .segments - .last() - .map(|s| s.ident == "include_cpp") - .unwrap_or(false) => - { - Segment::Autocxx( - crate::IncludeCppEngine::new_from_syn(mac.mac.clone()) - .map_err(ParseError::AutocxxCodegenError)?, - ) - } - Item::Mod(itm) - if itm - .attrs - .iter() - .any(|attr| attr.path.to_token_stream().to_string() == "cxx :: bridge") => - { - Segment::Cxx(CxxBridge::from(itm)) - } - Item::Struct(ref its) if auto_allowlist => { - let attrs = &its.attrs; - let is_superclass_attr = attrs.iter().find(|attr| { - attr.path - .segments - .last() - .map(|seg| seg.ident == "is_subclass" || seg.ident == SUBCLASS) - .unwrap_or(false) - }); - if let Some(is_superclass_attr) = is_superclass_attr { - if !is_superclass_attr.tokens.is_empty() { - let subclass = its.ident.clone(); - let args: SubclassAttrs = is_superclass_attr - .parse_args() - .map_err(ParseError::Syntax)?; - if let Some(superclass) = args.superclass { - extra_superclasses.push(Subclass { - superclass, - subclass, - }) - } - } - } - discoveries.search_item(&item); - Segment::Other(item) - } - _ => { - discoveries.search_item(&item); - Segment::Other(item) - } - }); - } - if !auto_allowlist - && (!discoveries.extern_rust_types.is_empty() || !discoveries.extern_rust_funs.is_empty()) - { - return Err(ParseError::DiscoveredRustItemsWhenNotInAutoDiscover); - } - if !extra_superclasses.is_empty() || (auto_allowlist && !discoveries.is_empty()) { - let mut autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg { - Segment::Autocxx(engine) => Some(engine), - _ => None, - }); - let our_seg = autocxx_seg_iterator.next(); - match our_seg { - None => return Err(ParseError::ZeroModsForDynamicDiscovery), - Some(engine) => { - engine - .config_mut() - .subclasses - .append(&mut extra_superclasses); - if auto_allowlist { - for cpp in discoveries.cpp_list { - engine - .config_mut() - .allowlist - .push(LitStr::new(&cpp, Span::call_site())) - .map_err(ParseError::Syntax)?; - } - } - engine - .config_mut() - .extern_rust_funs - .append(&mut discoveries.extern_rust_funs); - engine - .config_mut() - .rust_types - .append(&mut discoveries.extern_rust_types); - } - } - if autocxx_seg_iterator.next().is_some() { - return Err(ParseError::MultipleModsForDynamicDiscovery); - } - } - let autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg { - Segment::Autocxx(engine) => Some(engine), - _ => None, - }); - for seg in autocxx_seg_iterator { - seg.config - .confirm_complete(auto_allowlist) - .map_err(ParseError::Syntax)?; - } - Ok(ParsedFile(results)) -} - -/// A Rust file parsed by autocxx. May contain zero or more autocxx 'engines', -/// i.e. the `IncludeCpp` class, corresponding to zero or more include_cpp -/// macros within this file. Also contains `syn::Item` structures for all -/// the rest of the Rust code, such that it can be reconstituted if necessary. -pub struct ParsedFile(Vec<Segment>); - -#[allow(clippy::large_enum_variant)] -enum Segment { - Autocxx(IncludeCppEngine), - Cxx(CxxBridge), - Other(Item), -} - -pub trait CppBuildable { - fn generate_h_and_cxx( - &self, - cpp_codegen_options: &CppCodegenOptions, - ) -> Result<GeneratedCpp, cxx_gen::Error>; -} - -impl ParsedFile { - /// Get all the autocxxes in this parsed file. - pub fn get_rs_buildables(&self) -> impl Iterator<Item = &IncludeCppEngine> { - self.0.iter().filter_map(|s| match s { - Segment::Autocxx(includecpp) => Some(includecpp), - _ => None, - }) - } - - /// Get all items which can result in C++ code - pub fn get_cpp_buildables(&self) -> impl Iterator<Item = &dyn CppBuildable> { - self.0.iter().filter_map(|s| match s { - Segment::Autocxx(includecpp) => Some(includecpp as &dyn CppBuildable), - Segment::Cxx(cxxbridge) => Some(cxxbridge as &dyn CppBuildable), - _ => None, - }) - } - - fn get_autocxxes_mut(&mut self) -> impl Iterator<Item = &mut IncludeCppEngine> { - self.0.iter_mut().filter_map(|s| match s { - Segment::Autocxx(includecpp) => Some(includecpp), - _ => None, - }) - } - - pub fn include_dirs(&self) -> impl Iterator<Item = &PathBuf> { - self.0 - .iter() - .filter_map(|s| match s { - Segment::Autocxx(includecpp) => Some(includecpp.include_dirs()), - _ => None, - }) - .flatten() - } - - pub fn resolve_all( - &mut self, - autocxx_inc: Vec<PathBuf>, - extra_clang_args: &[&str], - dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>, - cpp_codegen_options: &CppCodegenOptions, - ) -> Result<(), ParseError> { - let mut mods_found = HashSet::new(); - let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> = - dep_recorder.map(Rc::from); - for include_cpp in self.get_autocxxes_mut() { - #[allow(clippy::manual_map)] // because of dyn shenanigans - let dep_recorder: Option<Box<dyn RebuildDependencyRecorder>> = match &inner_dep_recorder - { - None => None, - Some(inner_dep_recorder) => Some(Box::new(CompositeDepRecorder::new( - inner_dep_recorder.clone(), - ))), - }; - if !mods_found.insert(include_cpp.get_mod_name()) { - return Err(ParseError::ConflictingModNames); - } - include_cpp - .generate( - autocxx_inc.clone(), - extra_clang_args, - dep_recorder, - cpp_codegen_options, - ) - .map_err(ParseError::AutocxxCodegenError)? - } - Ok(()) - } -} - -impl ToTokens for ParsedFile { - fn to_tokens(&self, tokens: &mut TokenStream) { - for seg in &self.0 { - match seg { - Segment::Other(item) => item.to_tokens(tokens), - Segment::Autocxx(autocxx) => { - let these_tokens = autocxx.generate_rs(); - tokens.extend(these_tokens); - } - Segment::Cxx(itemmod) => itemmod.to_tokens(tokens), - } - } - } -} - -/// Shenanigans required to share the same RebuildDependencyRecorder -/// with all of the include_cpp instances in this one file. -#[derive(Debug, Clone)] -struct CompositeDepRecorder(Rc<dyn RebuildDependencyRecorder>); - -impl CompositeDepRecorder { - fn new(inner: Rc<dyn RebuildDependencyRecorder>) -> Self { - CompositeDepRecorder(inner) - } -} - -impl UnwindSafe for CompositeDepRecorder {} - -impl RebuildDependencyRecorder for CompositeDepRecorder { - fn record_header_file_dependency(&self, filename: &str) { - self.0.record_header_file_dependency(filename); - } -}
diff --git a/third_party/rust/autocxx_engine/v0_16/BUILD.gn b/third_party/rust/autocxx_engine/v0_17/BUILD.gn similarity index 86% rename from third_party/rust/autocxx_engine/v0_16/BUILD.gn rename to third_party/rust/autocxx_engine/v0_17/BUILD.gn index cf4e0231..50fe1441 100644 --- a/third_party/rust/autocxx_engine/v0_16/BUILD.gn +++ b/third_party/rust/autocxx_engine/v0_17/BUILD.gn
@@ -6,7 +6,7 @@ cargo_crate("lib") { crate_name = "autocxx_engine" - epoch = "0.16" + epoch = "0.17" crate_type = "rlib" # Only for usage from third-party crates. Add the crate to @@ -21,8 +21,7 @@ deps = [ "//third_party/rust/aquamarine/v0_1:lib", "//third_party/rust/autocxx_bindgen/v0_59:lib", - "//third_party/rust/autocxx_parser/v0_16:lib", - "//third_party/rust/cxx/v1:lib", + "//third_party/rust/autocxx_parser/v0_17:lib", "//third_party/rust/cxx_gen/v0_7:lib", "//third_party/rust/indoc/v1:lib", "//third_party/rust/itertools/v0_10:lib", @@ -31,14 +30,14 @@ "//third_party/rust/proc_macro2/v1:lib", "//third_party/rust/quote/v1:lib", "//third_party/rust/serde_json/v1:lib", - "//third_party/rust/strum_macros/v0_23:lib", + "//third_party/rust/strum_macros/v0_24:lib", "//third_party/rust/syn/v1:lib", "//third_party/rust/tempfile/v3:lib", - "//third_party/rust/unzip_n/v0_1:lib", "//third_party/rust/version_check/v0_9:lib", ] features = [ "reproduction_case", "serde_json", + "static", ] }
diff --git a/third_party/rust/autocxx_engine/v0_16/README.chromium b/third_party/rust/autocxx_engine/v0_17/README.chromium similarity index 78% rename from third_party/rust/autocxx_engine/v0_16/README.chromium rename to third_party/rust/autocxx_engine/v0_17/README.chromium index c784f05b..cad75921 100644 --- a/third_party/rust/autocxx_engine/v0_16/README.chromium +++ b/third_party/rust/autocxx_engine/v0_17/README.chromium
@@ -1,6 +1,6 @@ Name: autocxx-engine URL: https://crates.io/crates/autocxx-engine Description: Safe autogenerated interop between Rust and C++ -Version: 0.16.0 -Security Critical: no +Version: 0.17.2 +Security Critical: yes License: Apache 2.0
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_engine/v0_17/crate/.cargo_vcs_info.json new file mode 100644 index 0000000..a34c1ec6 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "5bb748878f97f854bffa61e5a7cd8ebf970f3dcd" + }, + "path_in_vcs": "engine" +} \ No newline at end of file
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml b/third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml similarity index 92% rename from third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml rename to third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml index d4654bb..1d0bc25 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml +++ b/third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml
@@ -12,7 +12,7 @@ [package] edition = "2021" name = "autocxx-engine" -version = "0.16.0" +version = "0.17.2" authors = ["Adrian Taylor <adetaylor@chromium.org>"] description = "Safe autogenerated interop between Rust and C++" keywords = ["ffi"] @@ -37,15 +37,12 @@ version = "=0.59.13" [dependencies.autocxx-parser] -version = "=0.16.0" +version = "=0.17.2" [dependencies.cc] version = "1.0" optional = true -[dependencies.cxx] -version = "1.0.54" - [dependencies.cxx-gen] version = "0.7.54" @@ -53,7 +50,7 @@ version = "1.0" [dependencies.itertools] -version = "0.10" +version = "0.10.3" [dependencies.log] version = "0.4" @@ -72,7 +69,7 @@ optional = true [dependencies.strum_macros] -version = "0.23" +version = "0.24" [dependencies.syn] version = "1.0.39" @@ -84,9 +81,6 @@ [dependencies.tempfile] version = "3.1" -[dependencies.unzip-n] -version = "0.1.2" - [dependencies.version_check] version = "0.9"
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml.orig b/third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml.orig similarity index 67% rename from third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml.orig rename to third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml.orig index 726cf58..311214a 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml.orig +++ b/third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml.orig
@@ -1,20 +1,14 @@ # Copyright 2020 Google LLC # -# 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 -# -# https://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. +# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. [package] name = "autocxx-engine" -version = "0.16.0" +version = "0.17.2" authors = ["Adrian Taylor <adetaylor@chromium.org>"] license = "MIT OR Apache-2.0" description = "Safe autogenerated interop between Rust and C++" @@ -38,20 +32,18 @@ indoc = "1.0" autocxx-bindgen = "=0.59.13" #autocxx-bindgen = { git = "https://github.com/adetaylor/rust-bindgen", branch = "denote-deleted-move-constructors" } -itertools = "0.10" +itertools = "0.10.3" cc = { version = "1.0", optional = true } -unzip-n = "0.1.2" # Note: Keep the patch-level version of cxx-gen and cxx in sync. # There can be interdependencies between the code generated by cxx-gen and # what cxx expects to be there. cxx-gen = "0.7.54" -cxx = "1.0.54" -autocxx-parser = { version = "=0.16.0", path="../parser" } +autocxx-parser = { version = "=0.17.2", path="../parser" } version_check = "0.9" aquamarine = "0.1" # docs tempfile = "3.1" once_cell = "1.7" -strum_macros = "0.23" +strum_macros = "0.24" serde_json = { version = "1.0", optional = true } [dependencies.syn]
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/README.md b/third_party/rust/autocxx_engine/v0_17/crate/README.md new file mode 100644 index 0000000..9b91d4a --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/README.md
@@ -0,0 +1 @@ +This crate is a [component of autocxx](https://google.github.io/autocxx/).
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/ast_discoverer.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/ast_discoverer.rs similarity index 92% rename from third_party/rust/autocxx_engine/v0_16/crate/src/ast_discoverer.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/ast_discoverer.rs index 2336e43..7374a51 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/ast_discoverer.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/ast_discoverer.rs
@@ -1,16 +1,10 @@ // Copyright 2021 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::collections::HashSet; @@ -36,10 +30,10 @@ } impl Discoveries { - pub(super) fn search_item(&mut self, item: &Item) { + pub(super) fn search_item(&mut self, item: &Item, mod_path: Option<RustPath>) { let mut this_mod = PerModDiscoveries { discoveries: self, - mod_path: None, + mod_path, }; this_mod.search_item(item); } @@ -49,6 +43,12 @@ && self.extern_rust_funs.is_empty() && self.extern_rust_types.is_empty() } + + pub(crate) fn extend(&mut self, other: Self) { + self.cpp_list.extend(other.cpp_list); + self.extern_rust_funs.extend(other.extern_rust_funs); + self.extern_rust_types.extend(other.extern_rust_types); + } } struct PerModDiscoveries<'a> { @@ -425,7 +425,7 @@ } } }; - discoveries.search_item(&itm); + discoveries.search_item(&itm, None); assert_cpp_found(&discoveries); } @@ -437,7 +437,7 @@ ffi::xxx() } }; - discoveries.search_item(&itm); + discoveries.search_item(&itm, None); assert_cpp_found(&discoveries); } @@ -449,7 +449,7 @@ ffi::xxx(); } }; - discoveries.search_item(&itm); + discoveries.search_item(&itm, None); assert_cpp_found(&discoveries); } @@ -461,7 +461,7 @@ ffi::a::b::xxx(); } }; - discoveries.search_item(&itm); + discoveries.search_item(&itm, None); assert!(!discoveries.cpp_list.is_empty()); assert!(discoveries.cpp_list.iter().next().unwrap() == "a::b::xxx"); } @@ -474,7 +474,7 @@ a + 3 * foo(ffi::xxx()); } }; - discoveries.search_item(&itm); + discoveries.search_item(&itm, None); assert_cpp_found(&discoveries); } @@ -486,7 +486,7 @@ let foo: ffi::xxx = bar(); } }; - discoveries.search_item(&itm); + discoveries.search_item(&itm, None); assert_cpp_found(&discoveries); } @@ -497,7 +497,7 @@ fn bar(a: &mut ffi::xxx) { } }; - discoveries.search_item(&itm); + discoveries.search_item(&itm, None); assert_cpp_found(&discoveries); } @@ -508,7 +508,7 @@ fn bar(a: cxx::UniquePtr<ffi::xxx>) { } }; - discoveries.search_item(&itm); + discoveries.search_item(&itm, None); assert_cpp_found(&discoveries); } @@ -520,17 +520,8 @@ fn bar(a: cxx::UniquePtr<ffi::xxx>) { } }; - discoveries.search_item(&itm); - assert!( - discoveries - .extern_rust_funs - .iter() - .next() - .unwrap() - .sig - .ident - == "bar" - ); + discoveries.search_item(&itm, None); + assert!(discoveries.extern_rust_funs.get(0).unwrap().sig.ident == "bar"); } #[test] @@ -542,12 +533,11 @@ } }; - discoveries.search_item(&itm); + discoveries.search_item(&itm, None); assert!( discoveries .extern_rust_types - .iter() - .next() + .get(0) .unwrap() .get_final_ident() == "Bar"
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/builder.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/builder.rs similarity index 94% rename from third_party/rust/autocxx_engine/v0_16/crate/src/builder.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/builder.rs index 58906ea..50af5b2 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/builder.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/builder.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use autocxx_parser::file_locations::FileLocationStrategy; use proc_macro2::TokenStream; @@ -83,14 +77,14 @@ /// It would be unusual to use this directly - see the `autocxx_build` or /// `autocxx_gen` crates. #[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))] -pub struct Builder<BuilderContext> { +pub struct Builder<'a, BuilderContext> { rs_file: PathBuf, autocxx_incs: Vec<OsString>, extra_clang_args: Vec<String>, dependency_recorder: Option<Box<dyn RebuildDependencyRecorder>>, custom_gendir: Option<PathBuf>, auto_allowlist: bool, - cpp_codegen_options: CppCodegenOptions, + cpp_codegen_options: CppCodegenOptions<'a>, // This member is to ensure that this type is parameterized // by a BuilderContext. The goal is to balance three needs: // (1) have most of the functionality over in autocxx_engine, @@ -101,7 +95,7 @@ ctx: PhantomData<BuilderContext>, } -impl<CTX: BuilderContext> Builder<CTX> { +impl<CTX: BuilderContext> Builder<'_, CTX> { #[doc(hidden)] pub fn new( rs_file: impl AsRef<Path>, @@ -179,6 +173,13 @@ self } + /// Whether to skip using [`cxx_gen`] to generate the C++ code, + /// so that some other process can handle that. + pub fn skip_cxx_gen(mut self, skip_cxx_gen: bool) -> Self { + self.cpp_codegen_options.skip_cxx_gen = skip_cxx_gen; + self + } + /// Build autocxx C++ files and return a cc::Build you can use to build /// more from a build.rs file. pub fn build(self) -> Result<BuilderBuild, BuilderError> {
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/abstract_types.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/abstract_types.rs new file mode 100644 index 0000000..43a0eb1 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/abstract_types.rs
@@ -0,0 +1,172 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::{ + fun::{ + FnAnalysis, FnKind, FnPhase, FnPrePhase2, MethodKind, PodAndConstructorAnalysis, + TraitMethodKind, + }, + pod::PodAnalysis, +}; +use crate::conversion::{api::Api, apivec::ApiVec}; +use crate::conversion::{ + api::TypeKind, + error_reporter::{convert_apis, convert_item_apis}, + ConvertError, +}; +use std::collections::HashSet; + +/// Spot types with pure virtual functions and mark them abstract. +pub(crate) fn mark_types_abstract(mut apis: ApiVec<FnPrePhase2>) -> ApiVec<FnPrePhase2> { + let mut abstract_types: HashSet<_> = apis + .iter() + .filter_map(|api| match &api { + Api::Function { + analysis: + FnAnalysis { + kind: + FnKind::Method { + impl_for: self_ty_name, + method_kind: MethodKind::PureVirtual(_), + .. + }, + .. + }, + .. + } => Some(self_ty_name.clone()), + _ => None, + }) + .collect(); + + // Spot any derived classes (recursively). Also, any types which have a base + // class that's not on the allowlist are presumed to be abstract, because we + // have no way of knowing (as they're not on the allowlist, there will be + // no methods associated so we won't be able to spot pure virtual methods). + let mut iterate = true; + while iterate { + iterate = false; + apis = apis + .into_iter() + .map(|api| { + match api { + Api::Struct { + analysis: + PodAndConstructorAnalysis { + pod: + PodAnalysis { + bases, + kind: TypeKind::Pod | TypeKind::NonPod, + castable_bases, + field_deps, + field_info, + is_generic, + }, + constructors, + }, + name, + details, + } if abstract_types.contains(&name.name) + || !abstract_types.is_disjoint(&bases) => + { + abstract_types.insert(name.name.clone()); + // Recurse in case there are further dependent types + iterate = true; + Api::Struct { + analysis: PodAndConstructorAnalysis { + pod: PodAnalysis { + bases, + kind: TypeKind::Abstract, + castable_bases, + field_deps, + field_info, + is_generic, + }, + constructors, + }, + name, + details, + } + } + _ => api, + } + }) + .collect() + } + + // We also need to remove any constructors belonging to these + // abstract types. + apis.retain(|api| { + !matches!(&api, + Api::Function { + analysis: + FnAnalysis { + kind: FnKind::Method{impl_for: self_ty, method_kind: MethodKind::MakeUnique | MethodKind::Constructor{..}, ..} + | FnKind::TraitMethod{ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, impl_for: self_ty, ..}, + .. + }, + .. + } if abstract_types.contains(self_ty)) + }); + + // Finally, if there are any types which are nested inside other types, + // they can't be abstract. This is due to two small limitations in cxx. + // Imagine we have class Foo { class Bar } + // 1) using "type Foo = super::bindgen::root::Foo_Bar" results + // in the creation of std::unique_ptr code which isn't acceptable + // for an abtract class + // 2) using "type Foo;" isn't possible unless Foo is a top-level item + // within its namespace. Any outer names will be interpreted as namespace + // names and result in cxx generating "namespace Foo { class Bar }"". + let mut results = ApiVec::new(); + convert_item_apis(apis, &mut results, |api| match api { + Api::Struct { + analysis: + PodAndConstructorAnalysis { + pod: + PodAnalysis { + kind: TypeKind::Abstract, + .. + }, + .. + }, + .. + } if api + .cpp_name() + .as_ref() + .map(|n| n.contains("::")) + .unwrap_or_default() => + { + Err(ConvertError::AbstractNestedType) + } + _ => Ok(Box::new(std::iter::once(api))), + }); + results +} + +pub(crate) fn discard_ignored_functions(apis: ApiVec<FnPhase>) -> ApiVec<FnPhase> { + // Some APIs can't be generated, e.g. because they're protected. + // Now we've finished analyzing abstract types and constructors, we'll + // convert them to IgnoredItems. + let mut apis_new = ApiVec::new(); + convert_apis( + apis, + &mut apis_new, + |name, fun, analysis| { + analysis.ignore_reason.clone()?; + Ok(Box::new(std::iter::once(Api::Function { + name, + fun, + analysis, + }))) + }, + Api::struct_unchanged, + Api::enum_unchanged, + Api::typedef_unchanged, + ); + apis_new +}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/allocators.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/allocators.rs new file mode 100644 index 0000000..f796de7 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/allocators.rs
@@ -0,0 +1,110 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Code to create functions to alloc and free while unitialized. + +use syn::{parse_quote, punctuated::Punctuated, token::Comma, FnArg, ReturnType}; + +use crate::{ + conversion::{ + api::{Api, ApiName, CppVisibility, FuncToConvert, Provenance, References, TraitSynthesis}, + apivec::ApiVec, + }, + types::{make_ident, QualifiedName}, +}; + +use super::{ + fun::function_wrapper::{CppFunctionBody, CppFunctionKind}, + pod::PodPhase, +}; + +pub(crate) fn create_alloc_and_frees(apis: ApiVec<PodPhase>) -> ApiVec<PodPhase> { + apis.into_iter() + .flat_map(|api| -> Box<dyn Iterator<Item = Api<PodPhase>>> { + match &api { + Api::Struct { name, .. } => { + Box::new(create_alloc_and_free(name.name.clone()).chain(std::iter::once(api))) + } + Api::Subclass { name, .. } => { + Box::new(create_alloc_and_free(name.cpp()).chain(std::iter::once(api))) + } + _ => Box::new(std::iter::once(api)), + } + }) + .collect() +} + +fn create_alloc_and_free(ty_name: QualifiedName) -> impl Iterator<Item = Api<PodPhase>> { + let typ = ty_name.to_type_path(); + let free_inputs: Punctuated<FnArg, Comma> = parse_quote! { + arg0: *mut #typ + }; + let alloc_return: ReturnType = parse_quote! { + -> *mut #typ + }; + [ + ( + TraitSynthesis::AllocUninitialized(ty_name.clone()), + get_alloc_name(&ty_name), + Punctuated::new(), + alloc_return, + CppFunctionBody::AllocUninitialized(ty_name.clone()), + ), + ( + TraitSynthesis::FreeUninitialized(ty_name.clone()), + get_free_name(&ty_name), + free_inputs, + ReturnType::Default, + CppFunctionBody::FreeUninitialized(ty_name.clone()), + ), + ] + .into_iter() + .map( + move |(synthesis, name, inputs, output, cpp_function_body)| { + let ident = name.get_final_ident(); + let api_name = ApiName::new_from_qualified_name(name); + Api::Function { + name: api_name, + fun: Box::new(FuncToConvert { + ident, + doc_attr: None, + inputs, + output, + vis: parse_quote! { pub }, + virtualness: crate::conversion::api::Virtualness::None, + cpp_vis: CppVisibility::Public, + special_member: None, + unused_template_param: false, + references: References::default(), + original_name: None, + self_ty: None, + synthesized_this_type: None, + synthetic_cpp: Some((cpp_function_body, CppFunctionKind::Function)), + add_to_trait: Some(synthesis), + is_deleted: false, + provenance: Provenance::SynthesizedOther, + }), + analysis: (), + } + }, + ) +} + +pub(crate) fn get_alloc_name(ty_name: &QualifiedName) -> QualifiedName { + get_name(ty_name, "alloc") +} + +pub(crate) fn get_free_name(ty_name: &QualifiedName) -> QualifiedName { + get_name(ty_name, "free") +} + +fn get_name(ty_name: &QualifiedName, label: &str) -> QualifiedName { + let name = format!("{}_{}", ty_name.get_final_item(), label); + let name_id = make_ident(name); + QualifiedName::new(ty_name.get_namespace(), name_id) +}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/casts.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/casts.rs similarity index 82% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/casts.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/casts.rs index 5b5cf3c..330430f 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/casts.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/casts.rs
@@ -1,23 +1,20 @@ // Copyright 2022 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use itertools::Itertools; use quote::quote; use syn::{parse_quote, FnArg}; use crate::{ - conversion::api::{Api, ApiName, CastMutability, References, Synthesis}, + conversion::{ + api::{Api, ApiName, CastMutability, Provenance, References, TraitSynthesis}, + apivec::ApiVec, + }, types::{make_ident, QualifiedName}, }; @@ -28,9 +25,12 @@ /// But the related code may be useful in future so I'm keeping it around. const SUPPORT_MUTABLE_CASTS: bool = false; -use super::pod::{PodAnalysis, PodPhase}; +use super::{ + fun::function_wrapper::{CppFunctionBody, CppFunctionKind}, + pod::{PodAnalysis, PodPhase}, +}; -pub(crate) fn add_casts(apis: Vec<Api<PodPhase>>) -> Vec<Api<PodPhase>> { +pub(crate) fn add_casts(apis: ApiVec<PodPhase>) -> ApiVec<PodPhase> { apis.into_iter() .flat_map(|api| { let mut resultant_apis = match api { @@ -91,7 +91,6 @@ }; Api::Function { name: ApiName::new_from_qualified_name(name), - name_for_gc: None, fun: Box::new(crate::conversion::api::FuncToConvert { ident, doc_attr: None, @@ -108,11 +107,13 @@ original_name: None, self_ty: Some(from.clone()), synthesized_this_type: None, - synthesis: Some(Synthesis::Cast { + add_to_trait: Some(TraitSynthesis::Cast { to_type: to.clone(), mutable, }), + synthetic_cpp: Some((CppFunctionBody::Cast, CppFunctionKind::Function)), is_deleted: false, + provenance: Provenance::SynthesizedOther, }), analysis: (), }
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/constructor_deps.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/constructor_deps.rs new file mode 100644 index 0000000..4fe2825 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/constructor_deps.rs
@@ -0,0 +1,105 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::HashMap; + +use crate::{ + conversion::{ + api::{Api, ApiName, StructDetails, TypeKind}, + apivec::ApiVec, + convert_error::ConvertErrorWithContext, + error_reporter::convert_apis, + }, + types::QualifiedName, +}; + +use super::fun::{ + FnAnalysis, FnKind, FnPhase, FnPrePhase2, PodAndConstructorAnalysis, PodAndDepAnalysis, + TraitMethodKind, +}; + +/// We've now analyzed all functions (including both implicit and explicit +/// constructors). Decorate each struct with a note of its constructors, +/// which will later be used as edges in the garbage collection, because +/// typically any use of a type will require us to call its copy or move +/// constructor. The same applies to its alloc/free functions. +pub(crate) fn decorate_types_with_constructor_deps(apis: ApiVec<FnPrePhase2>) -> ApiVec<FnPhase> { + let mut constructors_and_allocators_by_type = find_important_constructors(&apis); + let mut results = ApiVec::new(); + convert_apis( + apis, + &mut results, + Api::fun_unchanged, + |name, details, pod| { + decorate_struct(name, details, pod, &mut constructors_and_allocators_by_type) + }, + Api::enum_unchanged, + Api::typedef_unchanged, + ); + results +} + +fn decorate_struct( + name: ApiName, + details: Box<StructDetails>, + fn_struct: PodAndConstructorAnalysis, + constructors_and_allocators_by_type: &mut HashMap<QualifiedName, Vec<QualifiedName>>, +) -> Result<Box<dyn Iterator<Item = Api<FnPhase>>>, ConvertErrorWithContext> { + let pod = fn_struct.pod; + let is_abstract = matches!(pod.kind, TypeKind::Abstract); + let constructor_and_allocator_deps = if is_abstract || pod.is_generic { + Vec::new() + } else { + constructors_and_allocators_by_type + .remove(&name.name) + .unwrap_or_default() + }; + Ok(Box::new(std::iter::once(Api::Struct { + name, + details, + analysis: PodAndDepAnalysis { + pod, + constructor_and_allocator_deps, + constructors: fn_struct.constructors, + }, + }))) +} + +fn find_important_constructors( + apis: &ApiVec<FnPrePhase2>, +) -> HashMap<QualifiedName, Vec<QualifiedName>> { + let mut results: HashMap<QualifiedName, Vec<QualifiedName>> = HashMap::new(); + for api in apis.iter() { + if let Api::Function { + name, + analysis: + FnAnalysis { + kind: + FnKind::TraitMethod { + kind: + TraitMethodKind::Alloc + | TraitMethodKind::Dealloc + | TraitMethodKind::CopyConstructor + | TraitMethodKind::MoveConstructor, + impl_for, + .. + }, + ignore_reason: Ok(_), + .. + }, + .. + } = api + { + results + .entry(impl_for.clone()) + .or_default() + .push(name.name.clone()) + } + } + results +}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/ctypes.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/ctypes.rs new file mode 100644 index 0000000..714ef0c --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/ctypes.rs
@@ -0,0 +1,36 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::HashMap; + +use syn::Ident; + +use crate::conversion::api::ApiName; +use crate::conversion::apivec::ApiVec; +use crate::types::Namespace; +use crate::{conversion::api::Api, known_types::known_types, types::QualifiedName}; + +use super::deps::HasDependencies; +use super::fun::FnPhase; + +/// Spot any variable-length C types (e.g. unsigned long) +/// used in the [Api]s and append those as extra APIs. +pub(crate) fn append_ctype_information(apis: &mut ApiVec<FnPhase>) { + let ctypes: HashMap<Ident, QualifiedName> = apis + .iter() + .flat_map(|api| api.deps()) + .filter(|ty| known_types().is_ctype(ty)) + .map(|ty| (ty.get_final_ident(), ty.clone())) + .collect(); + for (id, typename) in ctypes { + apis.push(Api::CType { + name: ApiName::new(&Namespace::new(), id), + typename, + }); + } +}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/deps.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/deps.rs new file mode 100644 index 0000000..26dd081b --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/deps.rs
@@ -0,0 +1,114 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use itertools::Itertools; + +use crate::{ + conversion::api::{Api, TypeKind}, + types::QualifiedName, +}; + +use super::{ + fun::{FnPhase, FnPrePhase1, PodAndDepAnalysis}, + pod::PodAnalysis, + tdef::TypedefAnalysis, +}; + +pub(crate) trait HasDependencies { + fn name(&self) -> &QualifiedName; + fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_>; + + fn format_deps(&self) -> String { + self.deps().join(",") + } +} + +impl HasDependencies for Api<FnPrePhase1> { + fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> { + match self { + Api::Typedef { + old_tyname, + analysis: TypedefAnalysis { deps, .. }, + .. + } => Box::new(old_tyname.iter().chain(deps.iter())), + Api::Struct { + analysis: + PodAnalysis { + kind: TypeKind::Pod, + bases, + field_deps, + .. + }, + .. + } => Box::new(field_deps.iter().chain(bases.iter())), + Api::Function { analysis, .. } => Box::new(analysis.deps.iter()), + Api::Subclass { + name: _, + superclass, + } => Box::new(std::iter::once(superclass)), + Api::RustSubclassFn { details, .. } => Box::new(details.dependencies.iter()), + _ => Box::new(std::iter::empty()), + } + } + + fn name(&self) -> &QualifiedName { + self.name() + } +} + +impl HasDependencies for Api<FnPhase> { + /// Any dependencies on other APIs which this API has. + fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> { + match self { + Api::Typedef { + old_tyname, + analysis: TypedefAnalysis { deps, .. }, + .. + } => Box::new(old_tyname.iter().chain(deps.iter())), + Api::Struct { + analysis: + PodAndDepAnalysis { + pod: + PodAnalysis { + kind: TypeKind::Pod, + bases, + field_deps, + .. + }, + constructor_and_allocator_deps, + .. + }, + .. + } => Box::new( + field_deps + .iter() + .chain(bases.iter()) + .chain(constructor_and_allocator_deps.iter()), + ), + Api::Struct { + analysis: + PodAndDepAnalysis { + constructor_and_allocator_deps, + .. + }, + .. + } => Box::new(constructor_and_allocator_deps.iter()), + Api::Function { analysis, .. } => Box::new(analysis.deps.iter()), + Api::Subclass { + name: _, + superclass, + } => Box::new(std::iter::once(superclass)), + Api::RustSubclassFn { details, .. } => Box::new(details.dependencies.iter()), + _ => Box::new(std::iter::empty()), + } + } + + fn name(&self) -> &QualifiedName { + self.name() + } +}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/depth_first.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/depth_first.rs similarity index 60% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/depth_first.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/depth_first.rs index 7056b50c..bcfc1af1 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/depth_first.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/depth_first.rs
@@ -1,41 +1,27 @@ // Copyright 2022 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::collections::{HashSet, VecDeque}; use std::fmt::Debug; use itertools::Itertools; -use crate::{conversion::api::Api, types::QualifiedName}; +use crate::types::QualifiedName; -use super::fun::FnPhase; +use super::deps::HasDependencies; /// Return APIs in a depth-first order, i.e. those with no dependencies first. -pub(super) fn depth_first(apis: &[Api<FnPhase>]) -> impl Iterator<Item = &Api<FnPhase>> { - depth_first_impl(apis) -} - -fn depth_first_impl<T: HasDependencies + Debug>(items: &[T]) -> impl Iterator<Item = &T> { - DepthFirstIter { - queue: items.iter().collect(), - yet_to_do: items.iter().map(|api| api.name()).collect(), - } -} - -trait HasDependencies { - fn name(&self) -> &QualifiedName; - fn deps(&self) -> Box<dyn Iterator<Item = QualifiedName> + '_>; +pub(super) fn depth_first<'a, T: HasDependencies + Debug + 'a>( + inputs: impl Iterator<Item = &'a T> + 'a, +) -> impl Iterator<Item = &'a T> { + let queue: VecDeque<_> = inputs.collect(); + let yet_to_do = queue.iter().map(|api| api.name()).collect(); + DepthFirstIter { queue, yet_to_do } } struct DepthFirstIter<'a, T: HasDependencies + Debug> { @@ -68,21 +54,11 @@ } } -impl HasDependencies for Api<FnPhase> { - fn name(&self) -> &QualifiedName { - self.name() - } - - fn deps(&self) -> Box<dyn Iterator<Item = QualifiedName> + '_> { - self.deps() - } -} - #[cfg(test)] mod test { use crate::types::QualifiedName; - use super::{depth_first_impl, HasDependencies}; + use super::{depth_first, HasDependencies}; #[derive(Debug)] struct Thing(QualifiedName, Vec<QualifiedName>); @@ -92,8 +68,8 @@ &self.0 } - fn deps(&self) -> Box<dyn Iterator<Item = QualifiedName> + '_> { - Box::new(self.1.iter().cloned()) + fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> { + Box::new(self.1.iter()) } } @@ -112,7 +88,7 @@ vec![QualifiedName::new_from_cpp_name("a")], ); let api_list = vec![a, b, c]; - let mut it = depth_first_impl(&api_list); + let mut it = depth_first(api_list.iter()); assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("a")); assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("c")); assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("b"));
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/bridge_name_tracker.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/bridge_name_tracker.rs similarity index 90% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/bridge_name_tracker.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/bridge_name_tracker.rs index 9658e28..7e81c59 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/bridge_name_tracker.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/bridge_name_tracker.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use crate::types::Namespace; use itertools::Itertools; @@ -82,6 +76,11 @@ found_name: &str, ns: &Namespace, ) -> String { + let found_name = if found_name == "new" { + "new_autocxx" + } else { + found_name + }; let count = self .next_cxx_bridge_name_for_prefix .entry(found_name.to_string())
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/function_wrapper.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/function_wrapper.rs similarity index 74% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/function_wrapper.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/function_wrapper.rs index aaa68ad..b260bfb 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/function_wrapper.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/function_wrapper.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use crate::{ conversion::api::SubclassName, @@ -21,18 +15,25 @@ #[derive(Clone, Debug)] pub(crate) enum CppConversionType { None, + Move, FromUniquePtrToValue, + FromPtrToValue, FromValueToUniquePtr, FromPtrToMove, } impl CppConversionType { + /// If we've found a function which does X to its parameter, what + /// is the opposite of X? This is used for subclasses where calls + /// from Rust to C++ might also involve calls from C++ to Rust. fn inverse(&self) -> Self { match self { CppConversionType::None => CppConversionType::None, - CppConversionType::FromUniquePtrToValue => CppConversionType::FromValueToUniquePtr, + CppConversionType::FromUniquePtrToValue | CppConversionType::FromPtrToValue => { + CppConversionType::FromValueToUniquePtr + } CppConversionType::FromValueToUniquePtr => CppConversionType::FromUniquePtrToValue, - CppConversionType::FromPtrToMove => panic!("Did not expect to have to invert move"), + _ => panic!("Did not expect to have to invert this conversion"), } } } @@ -45,6 +46,7 @@ FromPinMaybeUninitToPtr, FromPinMoveRefToPtr, FromTypeToPtr, + FromValueParamToPtr, } impl RustConversionType { @@ -105,6 +107,12 @@ pub(crate) fn converted_rust_type(&self) -> Type { match self.cpp_conversion { CppConversionType::FromUniquePtrToValue => self.make_unique_ptr_type(), + CppConversionType::FromPtrToValue => { + let innerty = &self.unwrapped_type; + parse_quote! { + *mut #innerty + } + } _ => self.unwrapped_type.clone(), } } @@ -120,6 +128,9 @@ !matches!(self.rust_conversion, RustConversionType::None) } + /// Subclass support involves calls from Rust -> C++, but + /// also from C++ -> Rust. Work out the correct argument conversion + /// type for the latter call, when given the former. pub(crate) fn inverse(&self) -> Self { Self { unwrapped_type: self.unwrapped_type.clone(), @@ -127,10 +138,16 @@ rust_conversion: self.rust_conversion.clone(), } } + + pub(crate) fn bridge_unsafe_needed(&self) -> bool { + matches!( + self.rust_conversion, + RustConversionType::FromValueParamToPtr + ) + } } #[derive(Clone)] - pub(crate) enum CppFunctionBody { FunctionCall(Namespace, Ident), StaticMethodCall(Namespace, Ident, Ident), @@ -139,10 +156,11 @@ ConstructSuperclass(String), Cast, Destructor(Namespace, Ident), + AllocUninitialized(QualifiedName), + FreeUninitialized(QualifiedName), } #[derive(Clone)] - pub(crate) enum CppFunctionKind { Function, Method,
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/implicit_constructors.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/implicit_constructors.rs new file mode 100644 index 0000000..a0356ca --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/implicit_constructors.rs
@@ -0,0 +1,659 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::{hash_map, HashMap}; + +use syn::Type; + +use crate::{ + conversion::{ + analysis::{depth_first::depth_first, pod::PodAnalysis, type_converter::TypeKind}, + api::{Api, ApiName, CppVisibility, FuncToConvert, SpecialMemberKind}, + apivec::ApiVec, + }, + known_types::{known_types, KnownTypeConstructorDetails}, + types::QualifiedName, +}; + +use super::{FnAnalysis, FnKind, FnPrePhase1, MethodKind, ReceiverMutability, TraitMethodKind}; + +/// Indicates what we found out about a category of special member function. +/// +/// In the end, we only care whether it's public and exists, but we track a bit more information to +/// support determining the information for dependent classes. +#[derive(Debug, Copy, Clone)] +pub(super) enum SpecialMemberFound { + /// This covers being deleted in any way: + /// * Explicitly deleted + /// * Implicitly defaulted when that means being deleted + /// * Explicitly defaulted when that means being deleted + /// + /// It also covers not being either user declared or implicitly defaulted. + NotPresent, + /// Implicit special member functions, indicated by this, are always public. + Implicit, + /// This covers being explicitly defaulted (when that is not deleted) or being user-defined. + Explicit(CppVisibility), +} + +impl SpecialMemberFound { + /// Returns whether code outside of subclasses can call this special member function. + pub fn callable_any(&self) -> bool { + matches!(self, Self::Explicit(CppVisibility::Public) | Self::Implicit) + } + + /// Returns whether code in a subclass can call this special member function. + pub fn callable_subclass(&self) -> bool { + matches!( + self, + Self::Explicit(CppVisibility::Public) + | Self::Explicit(CppVisibility::Protected) + | Self::Implicit + ) + } + + /// Returns whether this exists at all. Note that this will return true even if it's private, + /// which is generally not very useful, but does come into play for some rules around which + /// default special member functions are deleted vs don't exist. + pub fn exists(&self) -> bool { + matches!(self, Self::Explicit(_) | Self::Implicit) + } + + pub fn exists_implicit(&self) -> bool { + matches!(self, Self::Implicit) + } + + pub fn exists_explicit(&self) -> bool { + matches!(self, Self::Explicit(_)) + } +} + +/// Information about which special member functions exist based on the C++ rules. +/// +/// Not all of this information is used directly, but we need to track it to determine the +/// information we do need for classes which are used as members or base classes. +#[derive(Debug, Clone)] +pub(super) struct ItemsFound { + pub(super) default_constructor: SpecialMemberFound, + pub(super) destructor: SpecialMemberFound, + pub(super) const_copy_constructor: SpecialMemberFound, + /// Remember that [`const_copy_constructor`] may be used in place of this if it exists. + pub(super) non_const_copy_constructor: SpecialMemberFound, + pub(super) move_constructor: SpecialMemberFound, + + /// The full name of the type. We identify instances by [`QualifiedName`], because that's + /// the only thing which [`FnKind::Method`] has to tie it to, and that's unique enough for + /// identification. However, when generating functions for implicit special members, we need + /// the extra information here. + /// + /// Will always be `Some` if any of the other fields are [`SpecialMemberFound::Implict`], + /// otherwise optional. + pub(super) name: Option<ApiName>, +} + +impl ItemsFound { + /// Returns whether we should generate a default constructor wrapper, because bindgen won't do + /// one for the implicit default constructor which exists. + pub(super) fn implicit_default_constructor_needed(&self) -> bool { + self.default_constructor.exists_implicit() + } + + /// Returns whether we should generate a copy constructor wrapper, because bindgen won't do one + /// for the implicit copy constructor which exists. + pub(super) fn implicit_copy_constructor_needed(&self) -> bool { + let any_implicit_copy = self.const_copy_constructor.exists_implicit() + || self.non_const_copy_constructor.exists_implicit(); + let no_explicit_copy = !(self.const_copy_constructor.exists_explicit() + || self.non_const_copy_constructor.exists_explicit()); + any_implicit_copy && no_explicit_copy + } + + /// Returns whether we should generate a move constructor wrapper, because bindgen won't do one + /// for the implicit move constructor which exists. + pub(super) fn implicit_move_constructor_needed(&self) -> bool { + self.move_constructor.exists_implicit() + } + + /// Returns whether we should generate a destructor wrapper, because bindgen won't do one for + /// the implicit destructor which exists. + pub(super) fn implicit_destructor_needed(&self) -> bool { + self.destructor.exists_implicit() + } +} +#[derive(Hash, Eq, PartialEq)] +enum ExplicitKind { + DefaultConstructor, + ConstCopyConstructor, + NonConstCopyConstructor, + MoveConstructor, + OtherConstructor, + Destructor, + ConstCopyAssignmentOperator, + NonConstCopyAssignmentOperator, + MoveAssignmentOperator, +} + +/// Denotes a specific kind of explicit member function that we found. +#[derive(Hash, Eq, PartialEq)] +struct ExplicitType { + ty: QualifiedName, + kind: ExplicitKind, +} + +/// Includes information about an explicit special member function which was found. +// TODO: Add Defaulted(CppVisibility) for https://github.com/google/autocxx/issues/815. +#[derive(Copy, Clone, Debug)] +enum ExplicitFound { + UserDefined(CppVisibility), + /// Note that this always means explicitly deleted, because this enum only represents + /// explicit declarations. + Deleted, + /// Indicates that we found more than one explicit of this kind. This is possible with most of + /// them, and we just bail and mostly act as if they're deleted. We'd have to decide whether + /// they're ambiguous to use them, which is really complicated. + Multiple, +} + +/// Analyzes which constructors are present for each type. +/// +/// If a type has explicit constructors, bindgen will generate corresponding +/// constructor functions, which we'll have already converted to make_unique methods. +/// For types with implicit constructors, we enumerate them here. +/// +/// It is tempting to make this a separate analysis phase, to be run later than +/// the function analysis; but that would make the code much more complex as it +/// would need to output a `FnAnalysisBody`. By running it as part of this phase +/// we can simply generate the sort of thing bindgen generates, then ask +/// the existing code in this phase to figure out what to do with it. +pub(super) fn find_constructors_present( + apis: &ApiVec<FnPrePhase1>, +) -> HashMap<QualifiedName, ItemsFound> { + let explicits = find_explicit_items(apis); + + // These contain all the classes we've seen so far with the relevant properties on their + // constructors of each kind. We iterate via [`depth_first`], so analyzing later classes + // just needs to check these. + // + // Important only to ask for a depth-first analysis of structs, because + // when all APIs are considered there may be reference loops and that would + // panic. + // + // These analyses include all bases and members of each class. + let mut all_items_found: HashMap<QualifiedName, ItemsFound> = HashMap::new(); + + for api in depth_first(apis.iter()) { + if let Api::Struct { + name, + analysis: PodAnalysis { + bases, field_info, .. + }, + details, + .. + } = api + { + let find_explicit = |kind: ExplicitKind| -> Option<&ExplicitFound> { + explicits.get(&ExplicitType { + ty: name.name.clone(), + kind, + }) + }; + let get_items_found = |qn: &QualifiedName| -> Option<ItemsFound> { + if let Some(constructor_details) = known_types().get_constructor_details(qn) { + Some(known_type_items_found(constructor_details)) + } else { + all_items_found.get(qn).cloned() + } + }; + let bases_items_found: Vec<_> = bases.iter().map_while(get_items_found).collect(); + let fields_items_found: Vec<_> = field_info + .iter() + .filter_map(|field_info| match field_info.type_kind { + TypeKind::Regular | TypeKind::SubclassHolder(_) => match field_info.ty { + Type::Path(ref qn) => get_items_found(&QualifiedName::from_type_path(qn)), + _ => None, + }, + // TODO: https://github.com/google/autocxx/issues/865 Figure out how to + // differentiate between pointers and references coming from C++. Pointers + // have a default constructor. + TypeKind::Pointer | TypeKind::Reference | TypeKind::MutableReference => { + Some(ItemsFound { + default_constructor: SpecialMemberFound::NotPresent, + destructor: SpecialMemberFound::Implicit, + const_copy_constructor: SpecialMemberFound::Implicit, + non_const_copy_constructor: SpecialMemberFound::NotPresent, + move_constructor: SpecialMemberFound::Implicit, + name: Some(name.clone()), + }) + } + }) + .collect(); + let has_rvalue_reference_fields = details.has_rvalue_reference_fields; + + // Check that all the bases and field types are known first. This combined with + // iterating via [`depth_first`] means we can safely search in `items_found` for all of + // them. + // + // Conservatively, we will not acknowledge the existence of most defaulted or implicit + // special member functions for any struct/class where we don't fully understand all + // field types. However, we can still look for explictly declared versions and use + // those. See below for destructors. + // + // We need to extend our knowledge to understand the constructor behavior of things in + // known_types.rs, then we'll be able to cope with types which contain strings, + // unique_ptrs etc. + let items_found = if bases_items_found.len() != bases.len() + || fields_items_found.len() != field_info.len() + { + let is_explicit = |kind: ExplicitKind| -> SpecialMemberFound { + // TODO: For https://github.com/google/autocxx/issues/815, map + // ExplicitFound::Defaulted(_) to NotPresent. + match find_explicit(kind) { + None => SpecialMemberFound::NotPresent, + Some(ExplicitFound::Deleted | ExplicitFound::Multiple) => { + SpecialMemberFound::NotPresent + } + Some(ExplicitFound::UserDefined(visibility)) => { + SpecialMemberFound::Explicit(*visibility) + } + } + }; + let items_found = ItemsFound { + default_constructor: is_explicit(ExplicitKind::DefaultConstructor), + destructor: match find_explicit(ExplicitKind::Destructor) { + // Assume that unknown types have destructors. This is common, and allows + // use to generate UniquePtr wrappers with them. + // + // However, this will generate C++ code that doesn't compile if the unknown + // type does not have an accessible destructor. Maybe we should have a way + // to disable that? + // + // TODO: For https://github.com/google/autocxx/issues/815, map + // ExplicitFound::Defaulted(_) to Explicit. + None => SpecialMemberFound::Implicit, + // If there are multiple destructors, assume that one of them will be + // selected by overload resolution. + Some(ExplicitFound::Multiple) => { + SpecialMemberFound::Explicit(CppVisibility::Public) + } + Some(ExplicitFound::Deleted) => SpecialMemberFound::NotPresent, + Some(ExplicitFound::UserDefined(visibility)) => { + SpecialMemberFound::Explicit(*visibility) + } + }, + const_copy_constructor: is_explicit(ExplicitKind::ConstCopyConstructor), + non_const_copy_constructor: is_explicit(ExplicitKind::NonConstCopyConstructor), + move_constructor: is_explicit(ExplicitKind::MoveConstructor), + name: Some(name.clone()), + }; + log::info!( + "Special member functions (explicits only) found for {:?}: {:?}", + name, + items_found + ); + items_found + } else { + // If no user-declared constructors of any kind are provided for a class type (struct, class, or union), + // the compiler will always declare a default constructor as an inline public member of its class. + // + // The implicitly-declared or defaulted default constructor for class T is defined as deleted if any of the following is true: + // T has a member of reference type without a default initializer. + // T has a non-const-default-constructible const member without a default member initializer. + // T has a member (without a default member initializer) which has a deleted default constructor, or its default constructor is ambiguous or inaccessible from this constructor. + // T has a direct or virtual base which has a deleted default constructor, or it is ambiguous or inaccessible from this constructor. + // T has a direct or virtual base or a non-static data member which has a deleted destructor, or a destructor that is inaccessible from this constructor. + // T is a union with at least one variant member with non-trivial default constructor, and no variant member of T has a default member initializer. // we don't support unions anyway + // T is a non-union class with a variant member M with a non-trivial default constructor, and no variant member of the anonymous union containing M has a default member initializer. + // T is a union and all of its variant members are const. // we don't support unions anyway + // + // Variant members are the members of anonymous unions. + let default_constructor = { + let explicit = find_explicit(ExplicitKind::DefaultConstructor); + // TODO: For https://github.com/google/autocxx/issues/815, replace the first term with: + // explicit.map_or(true, |explicit_found| matches!(explicit_found, ExplicitFound::Defaulted(_))) + let have_defaulted = explicit.is_none() + && !explicits.iter().any(|(ExplicitType { ty, kind }, _)| { + ty == &name.name + && match *kind { + ExplicitKind::DefaultConstructor => false, + ExplicitKind::ConstCopyConstructor => true, + ExplicitKind::NonConstCopyConstructor => true, + ExplicitKind::MoveConstructor => true, + ExplicitKind::OtherConstructor => true, + ExplicitKind::Destructor => false, + ExplicitKind::ConstCopyAssignmentOperator => false, + ExplicitKind::NonConstCopyAssignmentOperator => false, + ExplicitKind::MoveAssignmentOperator => false, + } + }); + if have_defaulted { + let bases_allow = bases_items_found.iter().all(|items_found| { + items_found.destructor.callable_subclass() + && items_found.default_constructor.callable_subclass() + }); + // TODO: Allow member initializers for + // https://github.com/google/autocxx/issues/816. + let members_allow = fields_items_found.iter().all(|items_found| { + items_found.destructor.callable_any() + && items_found.default_constructor.callable_any() + }); + if !has_rvalue_reference_fields && bases_allow && members_allow { + // TODO: For https://github.com/google/autocxx/issues/815, grab the + // visibility from an explicit default if present. + SpecialMemberFound::Implicit + } else { + SpecialMemberFound::NotPresent + } + } else if let Some(ExplicitFound::UserDefined(visibility)) = explicit { + SpecialMemberFound::Explicit(*visibility) + } else { + SpecialMemberFound::NotPresent + } + }; + + // If no user-declared prospective destructor is provided for a class type (struct, class, or union), the compiler will always declare a destructor as an inline public member of its class. + // + // The implicitly-declared or explicitly defaulted destructor for class T is defined as deleted if any of the following is true: + // T has a non-static data member that cannot be destructed (has deleted or inaccessible destructor) + // T has direct or virtual base class that cannot be destructed (has deleted or inaccessible destructors) + // T is a union and has a variant member with non-trivial destructor. // we don't support unions anyway + // The implicitly-declared destructor is virtual (because the base class has a virtual destructor) and the lookup for the deallocation function (operator delete()) results in a call to ambiguous, deleted, or inaccessible function. + let destructor = { + let explicit = find_explicit(ExplicitKind::Destructor); + // TODO: For https://github.com/google/autocxx/issues/815, replace the condition with: + // explicit.map_or(true, |explicit_found| matches!(explicit_found, ExplicitFound::Defaulted(_))) + if explicit.is_none() { + let bases_allow = bases_items_found + .iter() + .all(|items_found| items_found.destructor.callable_subclass()); + let members_allow = fields_items_found + .iter() + .all(|items_found| items_found.destructor.callable_any()); + if bases_allow && members_allow { + // TODO: For https://github.com/google/autocxx/issues/815, grab the + // visibility from an explicit default if present. + SpecialMemberFound::Implicit + } else { + SpecialMemberFound::NotPresent + } + } else if let Some(ExplicitFound::UserDefined(visibility)) = explicit { + SpecialMemberFound::Explicit(*visibility) + } else { + SpecialMemberFound::NotPresent + } + }; + + // If no user-defined copy constructors are provided for a class type (struct, class, or union), + // the compiler will always declare a copy constructor as a non-explicit inline public member of its class. + // This implicitly-declared copy constructor has the form T::T(const T&) if all of the following are true: + // each direct and virtual base B of T has a copy constructor whose parameters are const B& or const volatile B&; + // each non-static data member M of T of class type or array of class type has a copy constructor whose parameters are const M& or const volatile M&. + // + // The implicitly-declared or defaulted copy constructor for class T is defined as deleted if any of the following conditions are true: + // T is a union-like class and has a variant member with non-trivial copy constructor; // we don't support unions anyway + // T has a user-defined move constructor or move assignment operator (this condition only causes the implicitly-declared, not the defaulted, copy constructor to be deleted). + // T has non-static data members that cannot be copied (have deleted, inaccessible, or ambiguous copy constructors); + // T has direct or virtual base class that cannot be copied (has deleted, inaccessible, or ambiguous copy constructors); + // T has direct or virtual base class or a non-static data member with a deleted or inaccessible destructor; + // T has a data member of rvalue reference type; + let (const_copy_constructor, non_const_copy_constructor) = { + let explicit_const = find_explicit(ExplicitKind::ConstCopyConstructor); + let explicit_non_const = find_explicit(ExplicitKind::NonConstCopyConstructor); + let explicit_move = find_explicit(ExplicitKind::MoveConstructor); + + // TODO: For https://github.com/google/autocxx/issues/815, replace both terms with something like: + // explicit.map_or(true, |explicit_found| matches!(explicit_found, ExplicitFound::Defaulted(_))) + let have_defaulted = explicit_const.is_none() && explicit_non_const.is_none(); + if have_defaulted { + // TODO: For https://github.com/google/autocxx/issues/815, ignore this if + // the relevant (based on bases_are_const) copy constructor is explicitly defaulted. + let class_allows = explicit_move.is_none() && !has_rvalue_reference_fields; + let bases_allow = bases_items_found.iter().all(|items_found| { + items_found.destructor.callable_subclass() + && (items_found.const_copy_constructor.callable_subclass() + || items_found.non_const_copy_constructor.callable_subclass()) + }); + let members_allow = fields_items_found.iter().all(|items_found| { + items_found.destructor.callable_any() + && (items_found.const_copy_constructor.callable_any() + || items_found.non_const_copy_constructor.callable_any()) + }); + if class_allows && bases_allow && members_allow { + // TODO: For https://github.com/google/autocxx/issues/815, grab the + // visibility and existence of const and non-const from an explicit default if present. + let dependencies_are_const = bases_items_found + .iter() + .chain(fields_items_found.iter()) + .all(|items_found| items_found.const_copy_constructor.exists()); + if dependencies_are_const { + (SpecialMemberFound::Implicit, SpecialMemberFound::NotPresent) + } else { + (SpecialMemberFound::NotPresent, SpecialMemberFound::Implicit) + } + } else { + ( + SpecialMemberFound::NotPresent, + SpecialMemberFound::NotPresent, + ) + } + } else { + ( + if let Some(ExplicitFound::UserDefined(visibility)) = explicit_const { + SpecialMemberFound::Explicit(*visibility) + } else { + SpecialMemberFound::NotPresent + }, + if let Some(ExplicitFound::UserDefined(visibility)) = explicit_non_const + { + SpecialMemberFound::Explicit(*visibility) + } else { + SpecialMemberFound::NotPresent + }, + ) + } + }; + + // If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true: + // there are no user-declared copy constructors; + // there are no user-declared copy assignment operators; + // there are no user-declared move assignment operators; + // there is no user-declared destructor. + // then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&). + // + // A class can have multiple move constructors, e.g. both T::T(const T&&) and T::T(T&&). If some user-defined move constructors are present, the user may still force the generation of the implicitly declared move constructor with the keyword default. + // + // The implicitly-declared or defaulted move constructor for class T is defined as deleted if any of the following is true: + // T has non-static data members that cannot be moved (have deleted, inaccessible, or ambiguous move constructors); + // T has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors); + // T has direct or virtual base class with a deleted or inaccessible destructor; + // T is a union-like class and has a variant member with non-trivial move constructor. // we don't support unions anyway + let move_constructor = { + let explicit = find_explicit(ExplicitKind::MoveConstructor); + // TODO: For https://github.com/google/autocxx/issues/815, replace relevant terms with something like: + // explicit.map_or(true, |explicit_found| matches!(explicit_found, ExplicitFound::Defaulted(_))) + let have_defaulted = !(explicit.is_some() + || find_explicit(ExplicitKind::ConstCopyConstructor).is_some() + || find_explicit(ExplicitKind::NonConstCopyConstructor).is_some() + || find_explicit(ExplicitKind::ConstCopyAssignmentOperator).is_some() + || find_explicit(ExplicitKind::NonConstCopyAssignmentOperator).is_some() + || find_explicit(ExplicitKind::MoveAssignmentOperator).is_some() + || find_explicit(ExplicitKind::Destructor).is_some()); + if have_defaulted { + let bases_allow = bases_items_found.iter().all(|items_found| { + items_found.destructor.callable_subclass() + && items_found.move_constructor.callable_subclass() + }); + let members_allow = fields_items_found + .iter() + .all(|items_found| items_found.move_constructor.callable_any()); + if bases_allow && members_allow { + // TODO: For https://github.com/google/autocxx/issues/815, grab the + // visibility from an explicit default if present. + SpecialMemberFound::Implicit + } else { + SpecialMemberFound::NotPresent + } + } else if let Some(ExplicitFound::UserDefined(visibility)) = explicit { + SpecialMemberFound::Explicit(*visibility) + } else { + SpecialMemberFound::NotPresent + } + }; + + let items_found = ItemsFound { + default_constructor, + destructor, + const_copy_constructor, + non_const_copy_constructor, + move_constructor, + name: Some(name.clone()), + }; + log::info!( + "Special member items found for {:?}: {:?}", + name, + items_found + ); + items_found + }; + assert!( + all_items_found + .insert(name.name.clone(), items_found) + .is_none(), + "Duplicate struct: {:?}", + name + ); + } + } + + all_items_found +} + +fn find_explicit_items(apis: &ApiVec<FnPrePhase1>) -> HashMap<ExplicitType, ExplicitFound> { + let mut result = HashMap::new(); + let mut merge_fun = |ty: QualifiedName, kind: ExplicitKind, fun: &FuncToConvert| match result + .entry(ExplicitType { ty, kind }) + { + hash_map::Entry::Vacant(entry) => { + entry.insert(if fun.is_deleted { + ExplicitFound::Deleted + } else { + ExplicitFound::UserDefined(fun.cpp_vis) + }); + } + hash_map::Entry::Occupied(mut entry) => { + entry.insert(ExplicitFound::Multiple); + } + }; + for api in apis.iter() { + match api { + Api::Function { + analysis: + FnAnalysis { + kind: FnKind::Method { impl_for, .. }, + param_details, + .. + }, + fun, + .. + } if matches!( + fun.special_member, + Some(SpecialMemberKind::AssignmentOperator) + ) => + { + let is_move_assignment_operator = !fun.references.rvalue_ref_params.is_empty(); + merge_fun( + impl_for.clone(), + if is_move_assignment_operator { + ExplicitKind::MoveAssignmentOperator + } else { + let receiver_mutability = ¶m_details + .iter() + .next() + .unwrap() + .self_type + .as_ref() + .unwrap() + .1; + match receiver_mutability { + ReceiverMutability::Const => ExplicitKind::ConstCopyAssignmentOperator, + ReceiverMutability::Mutable => { + ExplicitKind::NonConstCopyAssignmentOperator + } + } + }, + fun, + ) + } + Api::Function { + analysis: + FnAnalysis { + kind: + FnKind::Method { + impl_for, + method_kind, + .. + }, + .. + }, + fun, + .. + } => match method_kind { + MethodKind::Constructor { is_default: true } => { + Some(ExplicitKind::DefaultConstructor) + } + MethodKind::Constructor { is_default: false } => { + Some(ExplicitKind::OtherConstructor) + } + _ => None, + } + .map_or((), |explicit_kind| { + merge_fun(impl_for.clone(), explicit_kind, fun) + }), + Api::Function { + analysis: + FnAnalysis { + kind: FnKind::TraitMethod { impl_for, kind, .. }, + .. + }, + fun, + .. + } => match kind { + TraitMethodKind::Destructor => Some(ExplicitKind::Destructor), + // In `analyze_foreign_fn` we mark non-const copy constructors as not being copy + // constructors for now, so we don't have to worry about them. + TraitMethodKind::CopyConstructor => Some(ExplicitKind::ConstCopyConstructor), + TraitMethodKind::MoveConstructor => Some(ExplicitKind::MoveConstructor), + _ => None, + } + .map_or((), |explicit_kind| { + merge_fun(impl_for.clone(), explicit_kind, fun) + }), + _ => (), + } + } + result +} + +/// Returns the information for a given known type. +fn known_type_items_found(constructor_details: KnownTypeConstructorDetails) -> ItemsFound { + let exists_public = SpecialMemberFound::Explicit(CppVisibility::Public); + let exists_public_if = |exists| { + if exists { + exists_public + } else { + SpecialMemberFound::NotPresent + } + }; + ItemsFound { + default_constructor: exists_public, + destructor: exists_public, + const_copy_constructor: exists_public_if(constructor_details.has_const_copy_constructor), + non_const_copy_constructor: SpecialMemberFound::NotPresent, + move_constructor: exists_public_if(constructor_details.has_move_constructor), + name: None, + } +}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/mod.rs similarity index 61% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/mod.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/mod.rs index 11806f1..f22f749 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/mod.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/mod.rs
@@ -1,23 +1,15 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. mod bridge_name_tracker; pub(crate) mod function_wrapper; -mod implicit_constructor_rules; mod implicit_constructors; mod overload_tracker; -mod rust_name_tracker; mod subclass; use crate::{ @@ -27,11 +19,13 @@ type_converter::{self, add_analysis, TypeConversionContext, TypeConverter}, }, api::{ - ApiName, CastMutability, CppVisibility, FuncToConvert, References, SpecialMemberKind, - SubclassName, Synthesis, Virtualness, + ApiName, CastMutability, CppVisibility, FuncToConvert, NullPhase, Provenance, + References, SpecialMemberKind, SubclassName, TraitImplSignature, TraitSynthesis, + UnsafetyNeeded, Virtualness, }, - convert_error::ConvertErrorWithContext, + apivec::ApiVec, convert_error::ErrorContext, + convert_error::{ConvertErrorWithContext, ErrorContextType}, error_reporter::{convert_apis, report_any_error}, }, known_types::known_types, @@ -42,18 +36,16 @@ use autocxx_parser::{IncludeCppConfig, UnsafePolicy}; use function_wrapper::{CppFunction, CppFunctionBody, TypeConversionPolicy}; use itertools::Itertools; -use proc_macro2::{Span, TokenStream}; -use quote::{quote, ToTokens}; +use proc_macro2::Span; +use quote::quote; use syn::{ - parse_quote, - punctuated::Punctuated, - token::{Comma, Unsafe}, - FnArg, Ident, Pat, ReturnType, Type, TypePtr, Visibility, + parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, ReturnType, Type, + TypePtr, Visibility, }; use crate::{ conversion::{ - api::{AnalysisPhase, Api, TypeKind, UnanalyzedApi}, + api::{AnalysisPhase, Api, TypeKind}, ConvertError, }, types::{make_ident, validate_ident_ok_for_cxx, Namespace, QualifiedName}, @@ -62,10 +54,12 @@ use self::{ bridge_name_tracker::BridgeNameTracker, function_wrapper::RustConversionType, - implicit_constructors::find_missing_constructors, + implicit_constructors::{find_constructors_present, ItemsFound}, overload_tracker::OverloadTracker, - rust_name_tracker::RustNameTracker, - subclass::{create_subclass_constructor, create_subclass_fn_wrapper, create_subclass_function}, + subclass::{ + create_subclass_constructor, create_subclass_fn_wrapper, create_subclass_function, + create_subclass_trait_item, + }, }; use super::{ @@ -80,10 +74,10 @@ Mutable, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) enum MethodKind { Normal(ReceiverMutability), - Constructor, + Constructor { is_default: bool }, MakeUnique, Static, Virtual(ReceiverMutability), @@ -96,14 +90,13 @@ MoveConstructor, Cast, Destructor, + Alloc, + Dealloc, } #[derive(Clone)] pub(crate) struct TraitMethodDetails { - pub(crate) impl_for_specifics: TokenStream, - pub(crate) trait_signature: TokenStream, - /// The trait is 'unsafe' itself - pub(crate) trait_unsafety: Option<Unsafe>, + pub(crate) trt: TraitImplSignature, pub(crate) avoid_self: bool, pub(crate) method_name: Ident, /// For traits, where we're trying to implement a specific existing @@ -111,22 +104,25 @@ /// interface. pub(crate) parameter_reordering: Option<Vec<usize>>, /// The function we're calling from the trait requires unsafe even - /// though the trait isn't. + /// though the trait and its function aren't. pub(crate) trait_call_is_unsafe: bool, } #[derive(Clone)] pub(crate) enum FnKind { Function, - Method(QualifiedName, MethodKind), + Method { + method_kind: MethodKind, + impl_for: QualifiedName, + }, TraitMethod { kind: TraitMethodKind, /// The name of the type T for which we're implementing a trait, /// though we may be actually implementing the trait for &mut T or - /// similar.... + /// similar, so we store more details of both the type and the + /// method in `details` impl_for: QualifiedName, - /// ... so we also store the tokenstream of the type. - details: TraitMethodDetails, + details: Box<TraitMethodDetails>, }, } @@ -137,23 +133,21 @@ pub(crate) enum RustRenameStrategy { /// cxx::bridge name matches user expectations None, - /// We can rename using the #[rust_name] attribute in the cxx::bridge - RenameUsingRustAttr, /// Even the #[rust_name] attribute would cause conflicts, and we need /// to use a 'use XYZ as ABC' RenameInOutputMod(Ident), -} - -#[derive(Clone)] -pub(crate) enum UnsafetyNeeded { - None, - JustReceiver, - Always, + /// This function requires us to generate a Rust function to do + /// parameter conversion. + RenameUsingWrapperFunction, } #[derive(Clone)] pub(crate) struct FnAnalysis { + /// Each entry in the cxx::bridge needs to have a unique name, even if + /// (from the perspective of Rust and C++) things are in different + /// namespaces/mods. pub(crate) cxxbridge_name: Ident, + /// ... so record also the name under which we wish to expose it in Rust. pub(crate) rust_name: String, pub(crate) rust_rename_strategy: RustRenameStrategy, pub(crate) params: Punctuated<FnArg, Comma>, @@ -173,6 +167,8 @@ /// Whether this can be called by external code. Not so for /// protected methods. pub(crate) externally_callable: bool, + /// Whether we need to generate a Rust-side calling function + pub(crate) rust_wrapper_needed: bool, } #[derive(Clone)] @@ -182,7 +178,7 @@ pub(crate) self_type: Option<(QualifiedName, ReceiverMutability)>, pub(crate) was_reference: bool, pub(crate) deps: HashSet<QualifiedName>, - pub(crate) requires_unsafe: bool, + pub(crate) requires_unsafe: UnsafetyNeeded, } struct ReturnTypeAnalysis { @@ -203,18 +199,75 @@ } } -pub(crate) struct FnPhase; +pub(crate) struct PodAndConstructorAnalysis { + pub(crate) pod: PodAnalysis, + pub(crate) constructors: PublicConstructors, +} -impl AnalysisPhase for FnPhase { +/// An analysis phase where we've analyzed each function, but +/// haven't yet determined which constructors/etc. belong to each type. +pub(crate) struct FnPrePhase1; + +impl AnalysisPhase for FnPrePhase1 { type TypedefAnalysis = TypedefAnalysis; type StructAnalysis = PodAnalysis; type FunAnalysis = FnAnalysis; } +/// An analysis phase where we've analyzed each function, and identified +/// what implicit constructors/destructors are present in each type. +pub(crate) struct FnPrePhase2; + +impl AnalysisPhase for FnPrePhase2 { + type TypedefAnalysis = TypedefAnalysis; + type StructAnalysis = PodAndConstructorAnalysis; + type FunAnalysis = FnAnalysis; +} + +pub(crate) struct PodAndDepAnalysis { + pub(crate) pod: PodAnalysis, + pub(crate) constructor_and_allocator_deps: Vec<QualifiedName>, + pub(crate) constructors: PublicConstructors, +} + +/// Analysis phase after we've finished analyzing functions and determined +/// which constructors etc. belong to them. +pub(crate) struct FnPhase; + +/// Indicates which kinds of public constructors are known to exist for a type. +#[derive(Debug, Default, Copy, Clone)] +pub(crate) struct PublicConstructors { + pub(crate) move_constructor: bool, + pub(crate) destructor: bool, +} + +impl PublicConstructors { + fn from_items_found(items_found: &ItemsFound) -> Self { + Self { + move_constructor: items_found.move_constructor.callable_any(), + destructor: items_found.destructor.callable_any(), + } + } +} + +impl AnalysisPhase for FnPhase { + type TypedefAnalysis = TypedefAnalysis; + type StructAnalysis = PodAndDepAnalysis; + type FunAnalysis = FnAnalysis; +} + +/// Whether to allow highly optimized calls because this is a simple Rust->C++ call, +/// or to use a simpler set of policies because this is a subclass call where +/// we may have C++->Rust->C++ etc. +#[derive(Copy, Clone)] +enum TypeConversionSophistication { + Regular, + SimpleForSubclasses, +} + pub(crate) struct FnAnalyzer<'a> { unsafe_policy: UnsafePolicy, - rust_name_tracker: RustNameTracker, - extra_apis: Vec<UnanalyzedApi>, + extra_apis: ApiVec<NullPhase>, type_converter: TypeConverter<'a>, bridge_name_tracker: BridgeNameTracker, pod_safe_types: HashSet<QualifiedName>, @@ -222,18 +275,19 @@ overload_trackers_by_mod: HashMap<Namespace, OverloadTracker>, subclasses_by_superclass: HashMap<QualifiedName, Vec<SubclassName>>, nested_type_name_map: HashMap<QualifiedName, String>, + generic_types: HashSet<QualifiedName>, + existing_superclass_trait_api_names: HashSet<QualifiedName>, } impl<'a> FnAnalyzer<'a> { pub(crate) fn analyze_functions( - apis: Vec<Api<PodPhase>>, + apis: ApiVec<PodPhase>, unsafe_policy: UnsafePolicy, config: &'a IncludeCppConfig, - ) -> Vec<Api<FnPhase>> { + ) -> ApiVec<FnPrePhase2> { let mut me = Self { unsafe_policy, - rust_name_tracker: RustNameTracker::new(), - extra_apis: Vec::new(), + extra_apis: ApiVec::new(), type_converter: TypeConverter::new(config, &apis), bridge_name_tracker: BridgeNameTracker::new(), config, @@ -241,22 +295,25 @@ pod_safe_types: Self::build_pod_safe_type_set(&apis), subclasses_by_superclass: subclass::subclasses_by_superclass(&apis), nested_type_name_map: Self::build_nested_type_map(&apis), + generic_types: Self::build_generic_type_set(&apis), + existing_superclass_trait_api_names: HashSet::new(), }; - let mut results = Vec::new(); + let mut results = ApiVec::new(); convert_apis( apis, &mut results, - |name, fun, _, _| me.analyze_foreign_fn_and_subclasses(name, fun), + |name, fun, _| me.analyze_foreign_fn_and_subclasses(name, fun), Api::struct_unchanged, Api::enum_unchanged, Api::typedef_unchanged, ); - me.add_missing_constructors(&mut results); + let mut results = me.add_constructors_present(results); + me.add_make_uniques(&mut results); results.extend(me.extra_apis.into_iter().map(add_analysis)); results } - fn build_pod_safe_type_set(apis: &[Api<PodPhase>]) -> HashSet<QualifiedName> { + fn build_pod_safe_type_set(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> { apis.iter() .filter_map(|api| match api { Api::Struct { @@ -286,9 +343,24 @@ .collect() } + fn build_generic_type_set(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> { + apis.iter() + .filter_map(|api| match api { + Api::Struct { + analysis: + PodAnalysis { + is_generic: true, .. + }, + .. + } => Some(api.name().clone()), + _ => None, + }) + .collect() + } + /// Builds a mapping from a qualified type name to the last 'nest' /// of its name, if it has multiple elements. - fn build_nested_type_map(apis: &[Api<PodPhase>]) -> HashMap<QualifiedName, String> { + fn build_nested_type_map(apis: &ApiVec<PodPhase>) -> HashMap<QualifiedName, String> { apis.iter() .filter_map(|api| match api { Api::Struct { name, .. } | Api::Enum { name, .. } => { @@ -329,14 +401,14 @@ .get_unique_cxx_bridge_name(type_name, found_name, ns) } - fn ok_to_use_rust_name(&mut self, rust_name: &str) -> bool { - self.rust_name_tracker.ok_to_use_rust_name(rust_name) - } - fn is_on_allowlist(&self, type_name: &QualifiedName) -> bool { self.config.is_on_allowlist(&type_name.to_cpp_name()) } + fn is_generic_type(&self, type_name: &QualifiedName) -> bool { + self.generic_types.contains(type_name) + } + #[allow(clippy::if_same_then_else)] // clippy bug doesn't notice the two // closures below are different. fn should_be_unsafe( @@ -344,145 +416,262 @@ param_details: &[ArgumentAnalysis], kind: &FnKind, ) -> UnsafetyNeeded { - if self.unsafe_policy == UnsafePolicy::AllFunctionsUnsafe { - UnsafetyNeeded::Always - } else if param_details - .iter() - .any(|pd| pd.self_type.is_none() && pd.requires_unsafe) - { - UnsafetyNeeded::Always - } else if matches!( - kind, + let unsafest_non_self_param = UnsafetyNeeded::from_param_details(param_details, true); + let unsafest_param = UnsafetyNeeded::from_param_details(param_details, false); + match kind { + // Trait unsafety must always correspond to the norms for the + // trait we're implementing. FnKind::TraitMethod { - kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, + kind: + TraitMethodKind::CopyConstructor + | TraitMethodKind::MoveConstructor + | TraitMethodKind::Alloc + | TraitMethodKind::Dealloc, .. - } - ) { - UnsafetyNeeded::Always - } else if param_details.iter().any(|pd| pd.requires_unsafe) { - UnsafetyNeeded::JustReceiver - } else { - UnsafetyNeeded::None + } => UnsafetyNeeded::Always, + FnKind::TraitMethod { .. } => match unsafest_param { + UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge, + _ => unsafest_param, + }, + _ if self.unsafe_policy == UnsafePolicy::AllFunctionsUnsafe => UnsafetyNeeded::Always, + _ => match unsafest_non_self_param { + UnsafetyNeeded::Always => UnsafetyNeeded::Always, + UnsafetyNeeded::JustBridge => match unsafest_param { + UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge, + _ => unsafest_non_self_param, + }, + UnsafetyNeeded::None => match unsafest_param { + UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge, + _ => unsafest_param, + }, + }, } } - /// Analyze a given function, and any permutations of that function which - /// we might additionally generate (e.g. for subclasses.) - fn analyze_foreign_fn_and_subclasses( - &mut self, - name: ApiName, - fun: Box<FuncToConvert>, - ) -> Result<Box<dyn Iterator<Item = Api<FnPhase>>>, ConvertErrorWithContext> { - let initial_name = name.clone(); - let maybe_analysis_and_name = self.analyze_foreign_fn(name, &fun); + fn add_make_uniques(&mut self, apis: &mut ApiVec<FnPrePhase2>) { + let mut results = ApiVec::new(); - let (analysis, name) = match maybe_analysis_and_name { - None => return Ok(Box::new(std::iter::empty())), - Some((analysis, name)) => (analysis, name), - }; + // Pre-assemble a list of types with known destructors, to avoid having to + // do a O(n^2) nested loop. + let types_with_destructors: HashSet<_> = apis + .iter() + .filter_map(|api| match api { + Api::Function { + fun, + analysis: + FnAnalysis { + kind: FnKind::TraitMethod { impl_for, .. }, + .. + }, + .. + } if matches!( + **fun, + FuncToConvert { + special_member: Some(SpecialMemberKind::Destructor), + is_deleted: false, + cpp_vis: CppVisibility::Public, + .. + } + ) => + { + Some(impl_for) + } + _ => None, + }) + .cloned() + .collect(); - let mut results = Vec::new(); + for api in apis.iter() { + if let Api::Function { + name, + fun, + analysis: + analysis @ FnAnalysis { + kind: + FnKind::Method { + impl_for: sup, + method_kind: MethodKind::Constructor { .. }, + .. + }, + .. + }, + .. + } = api + { + let initial_name = name.clone(); + // If we don't have an accessible destructor, then std::unique_ptr cannot be + // instantiated for this C++ type. + if !types_with_destructors.contains(sup) { + continue; + } - // Consider whether we need to synthesize subclass items. - match &analysis.kind { - FnKind::Method(sup, MethodKind::Constructor) => { // Create a make_unique too - let make_unique_func = self.create_make_unique(&fun); - self.analyze_and_add_if_necessary(initial_name, make_unique_func, &mut results); + self.create_make_unique(fun, initial_name, &mut results); for sub in self.subclasses_by_superclass(sup) { // Create a subclass constructor. This is a synthesized function // which didn't exist in the original C++. let (subclass_constructor_func, subclass_constructor_name) = - create_subclass_constructor(sub, &analysis, sup, &fun); - self.analyze_and_add_if_necessary( + create_subclass_constructor(sub, analysis, sup, fun); + self.analyze_and_add( subclass_constructor_name.clone(), subclass_constructor_func.clone(), &mut results, + TypeConversionSophistication::Regular, ); // and its corresponding make_unique - let make_unique_func = self.create_make_unique(&subclass_constructor_func); - self.analyze_and_add_if_necessary( + self.create_make_unique( + &subclass_constructor_func, subclass_constructor_name, - make_unique_func, &mut results, ); } } - FnKind::Method( - sup, - MethodKind::Virtual(receiver_mutability) - | MethodKind::PureVirtual(receiver_mutability), - ) => { - for sub in self.subclasses_by_superclass(sup) { - // For each subclass, we need to create a plain-C++ method to call its superclass - // and a Rust/C++ bridge API to call _that_. - // What we're generating here is entirely about the subclass, so the - // superclass's namespace is irrelevant. We generate - // all subclasses in the root namespace. - let is_pure_virtual = matches!( - &analysis.kind, - FnKind::Method(_, MethodKind::PureVirtual(..)) - ); - let super_fn_name = - SubclassName::get_super_fn_name(&Namespace::new(), &analysis.rust_name); + } + apis.extend(results.into_iter()); + } - results.push(create_subclass_function( - &sub, - &analysis, - &name, - receiver_mutability, - sup, - if is_pure_virtual { - None - } else { - Some(&super_fn_name) - }, - )); + /// Analyze a given function, and any permutations of that function which + /// we might additionally generate (e.g. for subclasses.) + /// + /// Leaves the [`FnKind::Method::type_constructors`] at its default for [`add_constructors_present`] + /// to fill out. + fn analyze_foreign_fn_and_subclasses( + &mut self, + name: ApiName, + fun: Box<FuncToConvert>, + ) -> Result<Box<dyn Iterator<Item = Api<FnPrePhase1>>>, ConvertErrorWithContext> { + let (analysis, name) = + self.analyze_foreign_fn(name, &fun, TypeConversionSophistication::Regular, None); + let mut results = ApiVec::new(); - if !is_pure_virtual { - let maybe_wrap = create_subclass_fn_wrapper(sub, &super_fn_name, &fun); - let super_fn_name = ApiName::new_from_qualified_name(super_fn_name); - self.analyze_and_add_if_necessary(super_fn_name, maybe_wrap, &mut results); + // Consider whether we need to synthesize subclass items. + if let FnKind::Method { + impl_for: sup, + method_kind: + MethodKind::Virtual(receiver_mutability) | MethodKind::PureVirtual(receiver_mutability), + .. + } = &analysis.kind + { + let (simpler_analysis, _) = self.analyze_foreign_fn( + name.clone(), + &fun, + TypeConversionSophistication::SimpleForSubclasses, + Some(analysis.rust_name.clone()), + ); + for sub in self.subclasses_by_superclass(sup) { + // For each subclass, we need to create a plain-C++ method to call its superclass + // and a Rust/C++ bridge API to call _that_. + // What we're generating here is entirely about the subclass, so the + // superclass's namespace is irrelevant. We generate + // all subclasses in the root namespace. + let is_pure_virtual = matches!( + &simpler_analysis.kind, + FnKind::Method { + method_kind: MethodKind::PureVirtual(..), + .. } + ); + + let super_fn_call_name = + SubclassName::get_super_fn_name(&Namespace::new(), &analysis.rust_name); + let super_fn_api_name = SubclassName::get_super_fn_name( + &Namespace::new(), + &analysis.cxxbridge_name.to_string(), + ); + let trait_api_name = SubclassName::get_trait_api_name(sup, &analysis.rust_name); + + let mut subclass_fn_deps = vec![trait_api_name.clone()]; + if !is_pure_virtual { + // Create a C++ API representing the superclass implementation (allowing + // calls from Rust->C++) + let maybe_wrap = create_subclass_fn_wrapper(&sub, &super_fn_call_name, &fun); + let super_fn_name = ApiName::new_from_qualified_name(super_fn_api_name); + let super_fn_call_api_name = self.analyze_and_add( + super_fn_name, + maybe_wrap, + &mut results, + TypeConversionSophistication::SimpleForSubclasses, + ); + subclass_fn_deps.push(super_fn_call_api_name); + } + + // Create the Rust API representing the subclass implementation (allowing calls + // from C++ -> Rust) + results.push(create_subclass_function( + // RustSubclassFn + &sub, + &simpler_analysis, + &name, + receiver_mutability, + sup, + subclass_fn_deps, + )); + + // Create the trait item for the <superclass>_methods and <superclass>_supers + // traits. This is required per-superclass, not per-subclass, so don't + // create it if it already exists. + if !self + .existing_superclass_trait_api_names + .contains(&trait_api_name) + { + self.existing_superclass_trait_api_names + .insert(trait_api_name.clone()); + results.push(create_subclass_trait_item( + ApiName::new_from_qualified_name(trait_api_name), + &simpler_analysis, + receiver_mutability, + sup.clone(), + is_pure_virtual, + )); } } - _ => {} } results.push(Api::Function { fun, analysis, name, - name_for_gc: None, }); Ok(Box::new(results.into_iter())) } - fn analyze_and_add_if_necessary( + /// Adds an API, usually a synthesized API. Returns the final calculated API name, which can be used + /// for others to depend on this. + fn analyze_and_add<P: AnalysisPhase<FunAnalysis = FnAnalysis>>( &mut self, name: ApiName, new_func: Box<FuncToConvert>, - results: &mut Vec<Api<FnPhase>>, - ) { - let maybe_another_api = self.analyze_foreign_fn(name, &new_func); - if let Some((analysis, name)) = maybe_another_api { - results.push(Api::Function { - fun: new_func, - analysis, - name, - name_for_gc: None, - }); - } + results: &mut ApiVec<P>, + sophistication: TypeConversionSophistication, + ) -> QualifiedName { + let (analysis, name) = self.analyze_foreign_fn(name, &new_func, sophistication, None); + results.push(Api::Function { + fun: new_func, + analysis, + name: name.clone(), + }); + name.name } /// Take a constructor e.g. pub fn A_A(this: *mut root::A); /// and synthesize a make_unique e.g. pub fn make_unique() -> cxx::UniquePtr<A> - fn create_make_unique(&mut self, fun: &FuncToConvert) -> Box<FuncToConvert> { + fn create_make_unique( + &mut self, + fun: &FuncToConvert, + initial_name: ApiName, + results: &mut ApiVec<FnPrePhase2>, + ) { let mut new_fun = fun.clone(); - new_fun.synthesis = Some(Synthesis::MakeUnique); - Box::new(new_fun) + new_fun.provenance = Provenance::SynthesizedMakeUnique; + let make_unique_func = Box::new(new_fun); + self.analyze_and_add( + initial_name, + make_unique_func, + results, + TypeConversionSophistication::Regular, + ); } /// Determine how to materialize a function. @@ -504,7 +693,9 @@ &mut self, name: ApiName, fun: &FuncToConvert, - ) -> Option<(FnAnalysis, ApiName)> { + sophistication: TypeConversionSophistication, + predetermined_rust_name: Option<String>, + ) -> (FnAnalysis, ApiName) { let mut cpp_name = name.cpp_name_if_present().cloned(); let ns = name.name.get_namespace(); @@ -529,6 +720,7 @@ &fun.references, true, None, + sophistication, ) }) .partition(Result::is_ok); @@ -589,7 +781,7 @@ // Part two, work out if this is a function, or method, or whatever. // First determine if this is actually a trait implementation. let trait_details = self.trait_creation_details_for_synthetic_function( - &fun.synthesis, + &fun.add_to_trait, ns, &ideal_rust_name, &self_ty, @@ -597,16 +789,7 @@ let (kind, error_context, rust_name) = if let Some(trait_details) = trait_details { trait_details } else if let Some(self_ty) = self_ty { - // Some kind of method. - if !self.is_on_allowlist(&self_ty) { - // Bindgen will output methods for types which have been encountered - // virally as arguments on other allowlisted types. But we don't want - // to generate methods unless the user has specifically asked us to. - // It may, for instance, be a private type. - return None; - } - - // Method or static method. + // Some kind of method or static method. let type_ident = self_ty.get_final_item(); // bindgen generates methods with the name: // {class}_{method name} @@ -626,73 +809,106 @@ ) { let is_move = matches!(fun.special_member, Some(SpecialMemberKind::MoveConstructor)); - let (kind, method_name, trait_id) = if is_move { - ( - TraitMethodKind::MoveConstructor, - "move_new", - quote! { MoveNew }, - ) - } else { - ( - TraitMethodKind::CopyConstructor, - "copy_new", - quote! { CopyNew }, - ) - }; if let Some(constructor_suffix) = rust_name.strip_prefix(nested_type_ident) { rust_name = format!("new{}", constructor_suffix); } - rust_name = self.get_overload_name(ns, type_ident, rust_name); + rust_name = predetermined_rust_name + .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name)); let error_context = error_context_for_method(&self_ty, &rust_name); - let impl_for_specifics = Type::Path(self_ty.to_type_path()).to_token_stream(); - ( - FnKind::TraitMethod { - kind, - impl_for: self_ty, - details: TraitMethodDetails { - impl_for_specifics, - trait_signature: quote! { - autocxx::moveit::new:: #trait_id - }, - trait_unsafety: Some(parse_quote! { unsafe }), - avoid_self: true, - method_name: make_ident(method_name), - parameter_reordering: Some(vec![1, 0]), - trait_call_is_unsafe: false, + + // If this is 'None', then something weird is going on. We'll check for that + // later when we have enough context to generate useful errors. + let arg_is_reference = matches!( + param_details + .get(1) + .map(|param| ¶m.conversion.unwrapped_type), + Some(Type::Reference(_)) + ); + // Some exotic forms of copy constructor have const and/or volatile qualifiers. + // These are not sufficient to implement CopyNew, so we just treat them as regular + // constructors. We detect them by their argument being translated to Pin at this + // point. + if is_move || arg_is_reference { + let (kind, method_name, trait_id) = if is_move { + ( + TraitMethodKind::MoveConstructor, + "move_new", + quote! { MoveNew }, + ) + } else { + ( + TraitMethodKind::CopyConstructor, + "copy_new", + quote! { CopyNew }, + ) + }; + let ty = Type::Path(self_ty.to_type_path()); + ( + FnKind::TraitMethod { + kind, + impl_for: self_ty, + details: Box::new(TraitMethodDetails { + trt: TraitImplSignature { + ty, + trait_signature: parse_quote! { + autocxx::moveit::new:: #trait_id + }, + unsafety: Some(parse_quote! { unsafe }), + }, + avoid_self: true, + method_name: make_ident(method_name), + parameter_reordering: Some(vec![1, 0]), + trait_call_is_unsafe: false, + }), }, - }, - error_context, - rust_name, - ) + error_context, + rust_name, + ) + } else { + ( + FnKind::Method { + impl_for: self_ty, + method_kind: MethodKind::Constructor { is_default: false }, + }, + error_context, + rust_name, + ) + } } else if matches!(fun.special_member, Some(SpecialMemberKind::Destructor)) { - rust_name = self.get_overload_name(ns, type_ident, rust_name); + rust_name = predetermined_rust_name + .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name)); let error_context = error_context_for_method(&self_ty, &rust_name); - let impl_for_specifics = Type::Path(self_ty.to_type_path()).to_token_stream(); + let ty = Type::Path(self_ty.to_type_path()); ( FnKind::TraitMethod { kind: TraitMethodKind::Destructor, impl_for: self_ty, - details: TraitMethodDetails { - impl_for_specifics, - trait_signature: quote! { - Drop + details: Box::new(TraitMethodDetails { + trt: TraitImplSignature { + ty, + trait_signature: parse_quote! { + Drop + }, + unsafety: None, }, - trait_unsafety: None, avoid_self: false, method_name: make_ident("drop"), parameter_reordering: None, trait_call_is_unsafe: true, - }, + }), }, error_context, rust_name, ) } else { - let method_kind = if matches!(fun.synthesis, Some(Synthesis::MakeUnique)) { + let method_kind = if matches!(fun.provenance, Provenance::SynthesizedMakeUnique) { // We're re-running this routine for a function we already analyzed. // Previously we made a placement "new" (MethodKind::Constructor). // This time we've asked ourselves to synthesize a make_unique. - let constructor_suffix = rust_name.strip_prefix(nested_type_ident).unwrap(); + let constructor_suffix = rust_name + .strip_prefix(nested_type_ident) + .or_else(|| rust_name.strip_prefix("new")) + .unwrap(); rust_name = format!("make_unique{}", constructor_suffix); // Strip off the 'this' arg. params = params.into_iter().skip(1).collect(); @@ -711,7 +927,12 @@ // If there are multiple constructors, bindgen generates // new, new1, new2 etc. and we'll keep those suffixes. rust_name = format!("new{}", constructor_suffix); - MethodKind::Constructor + MethodKind::Constructor { + is_default: matches!( + fun.special_member, + Some(SpecialMemberKind::DefaultConstructor) + ), + } } else if is_static_method { MethodKind::Static } else { @@ -724,10 +945,14 @@ } }; // Disambiguate overloads. - let rust_name = self.get_overload_name(ns, type_ident, rust_name); + let rust_name = predetermined_rust_name + .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name)); let error_context = error_context_for_method(&self_ty, &rust_name); ( - FnKind::Method(self_ty, method_kind), + FnKind::Method { + impl_for: self_ty, + method_kind, + }, error_context, rust_name, ) @@ -738,7 +963,7 @@ let rust_name = self.get_function_overload_name(ns, ideal_rust_name); ( FnKind::Function, - ErrorContext::Item(make_ident(&rust_name)), + ErrorContext::new_for_item(make_ident(&rust_name)), rust_name, ) }; @@ -758,7 +983,10 @@ // pointer not a reference. For copy + move constructors, we also // enforce Rust-side conversions to comply with moveit traits. match kind { - FnKind::Method(_, MethodKind::Constructor) => { + FnKind::Method { + method_kind: MethodKind::Constructor { .. }, + .. + } => { self.reanalyze_parameter( 0, fun, @@ -767,6 +995,7 @@ &mut params, &mut param_details, None, + sophistication, ) .unwrap_or_else(&mut set_ignore_reason); } @@ -783,6 +1012,7 @@ &mut params, &mut param_details, Some(RustConversionType::FromTypeToPtr), + sophistication, ) .unwrap_or_else(&mut set_ignore_reason); } @@ -790,6 +1020,9 @@ kind: TraitMethodKind::CopyConstructor, .. } => { + if param_details.len() < 2 { + set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam); + } self.reanalyze_parameter( 0, fun, @@ -798,6 +1031,7 @@ &mut params, &mut param_details, Some(RustConversionType::FromPinMaybeUninitToPtr), + sophistication, ) .unwrap_or_else(&mut set_ignore_reason); } @@ -806,6 +1040,9 @@ kind: TraitMethodKind::MoveConstructor, .. } => { + if param_details.len() < 2 { + set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam); + } self.reanalyze_parameter( 0, fun, @@ -814,6 +1051,7 @@ &mut params, &mut param_details, Some(RustConversionType::FromPinMaybeUninitToPtr), + sophistication, ) .unwrap_or_else(&mut set_ignore_reason); self.reanalyze_parameter( @@ -824,6 +1062,7 @@ &mut params, &mut param_details, Some(RustConversionType::FromPinMoveRefToPtr), + sophistication, ) .unwrap_or_else(&mut set_ignore_reason); } @@ -874,11 +1113,29 @@ set_ignore_reason(ConvertError::UnusedTemplateParam) } else { match kind { - FnKind::Method(_, MethodKind::Static) => {} - FnKind::Method(ref self_ty, _) - if !known_types().is_cxx_acceptable_receiver(self_ty) => + FnKind::Method { + ref impl_for, + method_kind: + MethodKind::Constructor { .. } + | MethodKind::MakeUnique + | MethodKind::Normal(..) + | MethodKind::PureVirtual(..) + | MethodKind::Virtual(..), + .. + } if !known_types().is_cxx_acceptable_receiver(impl_for) => { + set_ignore_reason(ConvertError::UnsupportedReceiver); + } + FnKind::Method { ref impl_for, .. } if !self.is_on_allowlist(impl_for) => { + // Bindgen will output methods for types which have been encountered + // virally as arguments on other allowlisted types. But we don't want + // to generate methods unless the user has specifically asked us to. + // It may, for instance, be a private type. + set_ignore_reason(ConvertError::MethodOfNonAllowlistedType); + } + FnKind::Method { ref impl_for, .. } | FnKind::TraitMethod { ref impl_for, .. } + if self.is_generic_type(impl_for) => { - set_ignore_reason(ConvertError::UnsupportedReceiver) + set_ignore_reason(ConvertError::MethodOfGenericType); } _ => {} } @@ -889,7 +1146,7 @@ // namespace so we might need to prepend some stuff to make it unique. let cxxbridge_name = self.get_cxx_bridge_name( match kind { - FnKind::Method(ref self_ty, ..) => Some(self_ty.get_final_item()), + FnKind::Method { ref impl_for, .. } => Some(impl_for.get_final_item()), FnKind::Function => None, FnKind::TraitMethod { ref impl_for, .. } => Some(impl_for.get_final_item()), }, @@ -903,9 +1160,13 @@ // Analyze the return type, just as we previously did for the // parameters. - let mut return_analysis = if let FnKind::Method(ref self_ty, MethodKind::MakeUnique) = kind + let mut return_analysis = if let FnKind::Method { + ref impl_for, + method_kind: MethodKind::MakeUnique, + .. + } = kind { - let constructed_type = self_ty.to_type_path(); + let constructed_type = impl_for.to_type_path(); ReturnTypeAnalysis { rt: parse_quote! { -> #constructed_type @@ -914,7 +1175,7 @@ #constructed_type })), was_reference: false, - deps: std::iter::once(self_ty).cloned().collect(), + deps: std::iter::once(impl_for).cloned().collect(), } } else { self.convert_return_type(&fun.output, ns, &fun.references) @@ -944,17 +1205,20 @@ let effective_cpp_name = cpp_name.as_ref().unwrap_or(&rust_name); let cpp_name_incompatible_with_cxx = validate_ident_ok_for_rust(effective_cpp_name).is_err(); - let synthetic_cpp_function_contents = synthesic_cpp_need(fun); // If possible, we'll put knowledge of the C++ API directly into the cxx::bridge // mod. However, there are various circumstances where cxx can't work with the existing // C++ API and we need to create a C++ wrapper function which is more cxx-compliant. // That wrapper function is included in the cxx::bridge, and calls through to the // original function. let wrapper_function_needed = match kind { - FnKind::Method(_, MethodKind::Static) - | FnKind::Method(_, MethodKind::Constructor) - | FnKind::Method(_, MethodKind::Virtual(_)) - | FnKind::Method(_, MethodKind::PureVirtual(_)) + FnKind::Method { + method_kind: + MethodKind::Static + | MethodKind::Constructor { .. } + | MethodKind::Virtual(_) + | MethodKind::PureVirtual(_), + .. + } | FnKind::TraitMethod { kind: TraitMethodKind::CopyConstructor @@ -962,11 +1226,11 @@ | TraitMethodKind::Destructor, .. } => true, - FnKind::Method(..) if cxxbridge_name != rust_name => true, + FnKind::Method { .. } if cxxbridge_name != rust_name => true, _ if param_conversion_needed => true, _ if ret_type_conversion_needed => true, _ if cpp_name_incompatible_with_cxx => true, - _ if synthetic_cpp_function_contents.is_some() => true, + _ if fun.synthetic_cpp.is_some() => true, _ => false, }; @@ -980,19 +1244,24 @@ "_" }; cxxbridge_name = make_ident(&format!("{}{}autocxx_wrapper", cxxbridge_name, joiner)); - let (payload, cpp_function_kind) = match synthetic_cpp_function_contents { + let (payload, cpp_function_kind) = match fun.synthetic_cpp.as_ref().cloned() { Some((payload, cpp_function_kind)) => (payload, cpp_function_kind), None => match kind { - FnKind::Method(_, MethodKind::MakeUnique) => { - (CppFunctionBody::MakeUnique, CppFunctionKind::Function) + FnKind::Method { + method_kind: MethodKind::MakeUnique, + .. + } => (CppFunctionBody::MakeUnique, CppFunctionKind::Function), + FnKind::Method { + ref impl_for, + method_kind: MethodKind::Constructor { .. }, + .. } - FnKind::Method(ref self_ty, MethodKind::Constructor) | FnKind::TraitMethod { kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, - impl_for: ref self_ty, + ref impl_for, .. } => ( - CppFunctionBody::PlacementNew(ns.clone(), self_ty.get_final_ident()), + CppFunctionBody::PlacementNew(ns.clone(), impl_for.get_final_ident()), CppFunctionKind::Constructor, ), FnKind::TraitMethod { @@ -1003,15 +1272,19 @@ CppFunctionBody::Destructor(ns.clone(), impl_for.get_final_ident()), CppFunctionKind::Function, ), - FnKind::Method(ref self_ty, MethodKind::Static) => ( + FnKind::Method { + ref impl_for, + method_kind: MethodKind::Static, + .. + } => ( CppFunctionBody::StaticMethodCall( ns.clone(), - self_ty.get_final_ident(), + impl_for.get_final_ident(), cpp_construction_ident, ), CppFunctionKind::Function, ), - FnKind::Method(..) => ( + FnKind::Method { .. } => ( CppFunctionBody::FunctionCall(ns.clone(), cpp_construction_ident), CppFunctionKind::Method, ), @@ -1034,8 +1307,13 @@ for pd in ¶m_details { let type_name = pd.conversion.converted_rust_type(); let arg_name = if pd.self_type.is_some() - && !matches!(kind, FnKind::Method(_, MethodKind::MakeUnique)) - { + && !matches!( + kind, + FnKind::Method { + method_kind: MethodKind::MakeUnique, + .. + } + ) { parse_quote!(autocxx_gen_this) } else { pd.name.clone() @@ -1064,35 +1342,30 @@ let vis = fun.vis.clone(); + let any_param_needs_rust_conversion = param_details + .iter() + .any(|pd| pd.conversion.rust_work_needed()); + + let rust_wrapper_needed = match kind { + FnKind::TraitMethod { .. } => true, + FnKind::Method { .. } => any_param_needs_rust_conversion || cxxbridge_name != rust_name, + _ => any_param_needs_rust_conversion, + }; + // Naming, part two. // Work out our final naming strategy. validate_ident_ok_for_cxx(&cxxbridge_name.to_string()).unwrap_or_else(set_ignore_reason); let rust_name_ident = make_ident(&rust_name); - let (id, rust_rename_strategy) = match kind { - FnKind::Method(..) | FnKind::TraitMethod { .. } => { - (rust_name_ident, RustRenameStrategy::None) + let rust_rename_strategy = match kind { + _ if rust_wrapper_needed => RustRenameStrategy::RenameUsingWrapperFunction, + FnKind::Function if cxxbridge_name != rust_name => { + RustRenameStrategy::RenameInOutputMod(rust_name_ident) } - FnKind::Function => { - // Keep the original Rust name the same so callers don't - // need to know about all of these shenanigans. - // There is a global space of rust_names even if they're in - // different namespaces. - let rust_name_ok = self.ok_to_use_rust_name(&rust_name); - if cxxbridge_name == rust_name { - (rust_name_ident, RustRenameStrategy::None) - } else if rust_name_ok { - (rust_name_ident, RustRenameStrategy::RenameUsingRustAttr) - } else { - ( - cxxbridge_name.clone(), - RustRenameStrategy::RenameInOutputMod(rust_name_ident), - ) - } - } + _ => RustRenameStrategy::None, }; let analysis = FnAnalysis { - cxxbridge_name, + cxxbridge_name: cxxbridge_name.clone(), rust_name: rust_name.clone(), rust_rename_strategy, params, @@ -1106,9 +1379,10 @@ deps, ignore_reason, externally_callable, + rust_wrapper_needed, }; - let name = ApiName::new_with_cpp_name(ns, id, cpp_name); - Some((analysis, name)) + let name = ApiName::new_with_cpp_name(ns, cxxbridge_name, cpp_name); + (analysis, name) } /// Applies a specific `force_rust_conversion` to the parameter at index @@ -1121,8 +1395,9 @@ ns: &Namespace, rust_name: &str, params: &mut Punctuated<FnArg, Comma>, - param_details: &mut Vec<ArgumentAnalysis>, + param_details: &mut [ArgumentAnalysis], force_rust_conversion: Option<RustConversionType>, + sophistication: TypeConversionSophistication, ) -> Result<(), ConvertError> { self.convert_fn_arg( fun.inputs.iter().nth(param_idx).unwrap(), @@ -1132,6 +1407,7 @@ &fun.references, false, force_rust_conversion, + sophistication, ) .map(|(new_arg, new_analysis)| { param_details[param_idx] = new_analysis; @@ -1158,39 +1434,39 @@ /// of a trait, rather than a function/method. fn trait_creation_details_for_synthetic_function( &mut self, - synthesis: &Option<Synthesis>, + synthesis: &Option<TraitSynthesis>, ns: &Namespace, ideal_rust_name: &str, self_ty: &Option<QualifiedName>, ) -> Option<(FnKind, ErrorContext, String)> { synthesis.as_ref().and_then(|synthesis| match synthesis { - Synthesis::Cast { to_type, mutable } => { + TraitSynthesis::Cast { to_type, mutable } => { let rust_name = self.get_function_overload_name(ns, ideal_rust_name.to_string()); let from_type = self_ty.as_ref().unwrap(); let from_type_path = from_type.to_type_path(); let to_type = to_type.to_type_path(); - let (trait_signature, impl_for_specifics, method_name) = match *mutable { + let (trait_signature, ty, method_name) = match *mutable { CastMutability::ConstToConst => ( - quote! { + parse_quote! { AsRef < #to_type > }, - from_type_path.to_token_stream(), + Type::Path(from_type_path), "as_ref", ), CastMutability::MutToConst => ( - quote! { + parse_quote! { AsRef < #to_type > }, - quote! { + parse_quote! { &'a mut ::std::pin::Pin < &'a mut #from_type_path > }, "as_ref", ), CastMutability::MutToMut => ( - quote! { + parse_quote! { autocxx::PinMut < #to_type > }, - quote! { + parse_quote! { ::std::pin::Pin < &'a mut #from_type_path > }, "pin_mut", @@ -1201,24 +1477,68 @@ FnKind::TraitMethod { kind: TraitMethodKind::Cast, impl_for: from_type.clone(), - details: TraitMethodDetails { - impl_for_specifics, - trait_signature, - trait_unsafety: None, + details: Box::new(TraitMethodDetails { + trt: TraitImplSignature { + ty, + trait_signature, + unsafety: None, + }, avoid_self: false, method_name, parameter_reordering: None, trait_call_is_unsafe: false, - }, + }), }, - ErrorContext::Item(make_ident(&rust_name)), + ErrorContext::new_for_item(make_ident(&rust_name)), rust_name, )) } - _ => None, + TraitSynthesis::AllocUninitialized(ty) => self.generate_alloc_or_deallocate( + ideal_rust_name, + ty, + "allocate_uninitialized_cpp_storage", + TraitMethodKind::Alloc, + ), + TraitSynthesis::FreeUninitialized(ty) => self.generate_alloc_or_deallocate( + ideal_rust_name, + ty, + "free_uninitialized_cpp_storage", + TraitMethodKind::Dealloc, + ), }) } + fn generate_alloc_or_deallocate( + &mut self, + ideal_rust_name: &str, + ty: &QualifiedName, + method_name: &str, + kind: TraitMethodKind, + ) -> Option<(FnKind, ErrorContext, String)> { + let rust_name = + self.get_function_overload_name(ty.get_namespace(), ideal_rust_name.to_string()); + let typ = ty.to_type_path(); + Some(( + FnKind::TraitMethod { + impl_for: ty.clone(), + details: Box::new(TraitMethodDetails { + trt: TraitImplSignature { + ty: Type::Path(typ), + trait_signature: parse_quote! { autocxx::moveit::MakeCppStorage }, + unsafety: Some(parse_quote! { unsafe }), + }, + avoid_self: false, + method_name: make_ident(method_name), + parameter_reordering: None, + trait_call_is_unsafe: false, + }), + kind, + }, + ErrorContext::new_for_item(make_ident(&rust_name)), + rust_name, + )) + } + fn get_function_overload_name(&mut self, ns: &Namespace, ideal_rust_name: String) -> String { let overload_tracker = self.overload_trackers_by_mod.entry(ns.clone()).or_default(); overload_tracker.get_function_real_name(ideal_rust_name) @@ -1241,6 +1561,7 @@ references: &References, treat_this_as_reference: bool, force_rust_conversion: Option<RustConversionType>, + sophistication: TypeConversionSophistication, ) -> Result<(FnArg, ArgumentAnalysis), ConvertError> { Ok(match arg { FnArg::Typed(pt) => { @@ -1312,9 +1633,18 @@ &subclass_holder.cloned(), treat_as_rvalue_reference, force_rust_conversion, + sophistication, ); pt.pat = Box::new(new_pat.clone()); pt.ty = new_ty; + let requires_unsafe = + if matches!(annotated_type.kind, type_converter::TypeKind::Pointer) { + UnsafetyNeeded::Always + } else if conversion.bridge_unsafe_needed() { + UnsafetyNeeded::JustBridge + } else { + UnsafetyNeeded::None + }; ( FnArg::Typed(pt), ArgumentAnalysis { @@ -1327,10 +1657,7 @@ | type_converter::TypeKind::MutableReference ), deps: annotated_type.types_encountered, - requires_unsafe: matches!( - annotated_type.kind, - type_converter::TypeKind::Pointer - ), + requires_unsafe, }, ) } @@ -1344,6 +1671,7 @@ is_subclass_holder: &Option<Ident>, is_rvalue_ref: bool, force_rust_conversion: Option<RustConversionType>, + sophistication: TypeConversionSophistication, ) -> TypeConversionPolicy { if let Some(holder_id) = is_subclass_holder { let subclass = SubclassName::from_holder_name(holder_id); @@ -1353,7 +1681,7 @@ }; TypeConversionPolicy { unwrapped_type: ty, - cpp_conversion: CppConversionType::None, + cpp_conversion: CppConversionType::Move, rust_conversion: RustConversionType::ToBoxedUpHolder(subclass), } }; @@ -1363,7 +1691,15 @@ let ty = ty.clone(); let tn = QualifiedName::from_type_path(p); if self.pod_safe_types.contains(&tn) { - TypeConversionPolicy::new_unconverted(ty) + if known_types().lacks_copy_constructor(&tn) { + TypeConversionPolicy { + unwrapped_type: ty, + cpp_conversion: CppConversionType::Move, + rust_conversion: RustConversionType::None, + } + } else { + TypeConversionPolicy::new_unconverted(ty) + } } else if known_types().convertible_from_strs(&tn) && !self.config.exclude_utilities() { @@ -1372,12 +1708,21 @@ cpp_conversion: CppConversionType::FromUniquePtrToValue, rust_conversion: RustConversionType::FromStr, } - } else { + } else if matches!( + sophistication, + TypeConversionSophistication::SimpleForSubclasses + ) { TypeConversionPolicy { unwrapped_type: ty, cpp_conversion: CppConversionType::FromUniquePtrToValue, rust_conversion: RustConversionType::None, } + } else { + TypeConversionPolicy { + unwrapped_type: ty, + cpp_conversion: CppConversionType::FromPtrToValue, + rust_conversion: RustConversionType::FromValueParamToPtr, + } } } _ => { @@ -1444,18 +1789,24 @@ /// If a type has explicit constructors, bindgen will generate corresponding /// constructor functions, which we'll have already converted to make_unique methods. /// C++ mandates the synthesis of certain implicit constructors, to which we - /// need ro create bindings too. We do that here. + /// need to create bindings too. We do that here. /// It is tempting to make this a separate analysis phase, to be run later than /// the function analysis; but that would make the code much more complex as it /// would need to output a `FnAnalysisBody`. By running it as part of this phase /// we can simply generate the sort of thing bindgen generates, then ask /// the existing code in this phase to figure out what to do with it. - fn add_missing_constructors(&mut self, apis: &mut Vec<Api<FnPhase>>) { - if self.config.exclude_impls { - return; - } - let implicit_constructors_needed = find_missing_constructors(apis); - for (self_ty, implicit_constructors_needed) in implicit_constructors_needed { + /// + /// Also fills out the [`PodAndConstructorAnalysis::constructors`] fields with information useful + /// for further analysis phases. + fn add_constructors_present(&mut self, mut apis: ApiVec<FnPrePhase1>) -> ApiVec<FnPrePhase2> { + let all_items_found = find_constructors_present(&apis); + for (self_ty, items_found) in all_items_found.iter() { + if self.config.exclude_impls { + // Remember that `find_constructors_present` mutates `apis`, so we always have to + // call that, even if we don't do anything with the return value. This is kind of + // messy, see the comment on this function for why. + continue; + } if self .config .is_on_constructor_blocklist(&self_ty.to_cpp_name()) @@ -1463,21 +1814,21 @@ continue; } let path = self_ty.to_type_path(); - if implicit_constructors_needed.default_constructor { - self.synthesize_constructor( - self_ty.clone(), + if items_found.implicit_default_constructor_needed() { + self.synthesize_special_member( + items_found, None, - apis, + &mut apis, SpecialMemberKind::DefaultConstructor, parse_quote! { this: *mut #path }, References::default(), ); } - if implicit_constructors_needed.move_constructor { - self.synthesize_constructor( - self_ty.clone(), + if items_found.implicit_move_constructor_needed() { + self.synthesize_special_member( + items_found, Some("move"), - apis, + &mut apis, SpecialMemberKind::MoveConstructor, parse_quote! { this: *mut #path, other: *mut #path }, References { @@ -1486,15 +1837,11 @@ }, ) } - // C++ synthesizes two different implicit copy constructors, but moveit - // supports only one, so we'll always synthesize that one. - if implicit_constructors_needed.copy_constructor_taking_const_t - || implicit_constructors_needed.copy_constructor_taking_t - { - self.synthesize_constructor( - self_ty.clone(), + if items_found.implicit_copy_constructor_needed() { + self.synthesize_special_member( + items_found, Some("const_copy"), - apis, + &mut apis, SpecialMemberKind::CopyConstructor, parse_quote! { this: *mut #path, other: *const #path }, References { @@ -1503,83 +1850,139 @@ }, ) } + if items_found.implicit_destructor_needed() { + self.synthesize_special_member( + items_found, + None, + &mut apis, + SpecialMemberKind::Destructor, + parse_quote! { this: *mut #path }, + References::default(), + ); + } } + + // Also, annotate each type with the constructors we found. + let mut results = ApiVec::new(); + convert_apis( + apis, + &mut results, + Api::fun_unchanged, + |name, details, analysis| { + let items_found = all_items_found.get(&name.name); + Ok(Box::new(std::iter::once(Api::Struct { + name, + details, + analysis: PodAndConstructorAnalysis { + pod: analysis, + constructors: if let Some(items_found) = items_found { + PublicConstructors::from_items_found(items_found) + } else { + PublicConstructors::default() + }, + }, + }))) + }, + Api::enum_unchanged, + Api::typedef_unchanged, + ); + results } - fn synthesize_constructor( + #[allow(clippy::too_many_arguments)] // it's true, but sticking with it for now + fn synthesize_special_member( &mut self, - self_ty: QualifiedName, + items_found: &ItemsFound, label: Option<&str>, - apis: &mut Vec<Api<FnPhase>>, + apis: &mut ApiVec<FnPrePhase1>, special_member: SpecialMemberKind, inputs: Punctuated<FnArg, Comma>, references: References, ) { + let self_ty = items_found.name.as_ref().unwrap(); let ident = match label { - Some(label) => make_ident(format!( + Some(label) => make_ident(self.config.uniquify_name_per_mod(&format!( "{}_synthetic_{}_ctor", - self_ty.get_final_item(), + self_ty.name.get_final_item(), label - )), - None => self_ty.get_final_ident(), + ))), + None => self_ty.name.get_final_ident(), }; - let fake_api_name = ApiName::new(self_ty.get_namespace(), ident.clone()); + let cpp_name = if matches!(special_member, SpecialMemberKind::DefaultConstructor) { + // Constructors (other than move or copy) are identified in `analyze_foreign_fn` by + // being suffixed with the cpp_name, so we have to produce that. + self.nested_type_name_map + .get(&self_ty.name) + .cloned() + .or_else(|| Some(self_ty.name.get_final_item().to_string())) + } else { + None + }; + let fake_api_name = + ApiName::new_with_cpp_name(self_ty.name.get_namespace(), ident.clone(), cpp_name); + let self_ty = &self_ty.name; let ns = self_ty.get_namespace().clone(); - let items = report_any_error(&ns, apis, || { - self.analyze_foreign_fn_and_subclasses( - fake_api_name, - Box::new(FuncToConvert { - self_ty: Some(self_ty), - ident, - doc_attr: None, - inputs, - output: ReturnType::Default, - vis: parse_quote! { pub }, - virtualness: Virtualness::None, - cpp_vis: CppVisibility::Public, - special_member: Some(special_member), - unused_template_param: false, - references, - original_name: None, - synthesized_this_type: None, - synthesis: None, - is_deleted: false, - }), - ) - }); - apis.extend(items.into_iter().flatten()); + let mut any_errors = ApiVec::new(); + apis.extend( + report_any_error(&ns, &mut any_errors, || { + self.analyze_foreign_fn_and_subclasses( + fake_api_name, + Box::new(FuncToConvert { + self_ty: Some(self_ty.clone()), + ident, + doc_attr: None, + inputs, + output: ReturnType::Default, + vis: parse_quote! { pub }, + virtualness: Virtualness::None, + cpp_vis: CppVisibility::Public, + special_member: Some(special_member), + unused_template_param: false, + references, + original_name: None, + synthesized_this_type: None, + is_deleted: false, + add_to_trait: None, + synthetic_cpp: None, + provenance: Provenance::SynthesizedOther, + }), + ) + }) + .into_iter() + .flatten(), + ); + apis.append(&mut any_errors); } } fn error_context_for_method(self_ty: &QualifiedName, rust_name: &str) -> ErrorContext { - ErrorContext::Method { - self_ty: self_ty.get_final_ident(), - method: make_ident(rust_name), - } -} - -fn synthesic_cpp_need(fun: &FuncToConvert) -> Option<(CppFunctionBody, CppFunctionKind)> { - match fun.synthesis { - Some(Synthesis::Cast { .. }) => Some((CppFunctionBody::Cast, CppFunctionKind::Function)), - _ => None, - } + ErrorContext::new_for_method(self_ty.get_final_ident(), make_ident(rust_name)) } impl Api<FnPhase> { - pub(crate) fn typename_for_allowlist(&self) -> QualifiedName { + pub(crate) fn name_for_allowlist(&self) -> QualifiedName { match &self { - Api::Function { - name_for_gc: Some(name), - .. - } => name.clone(), Api::Function { analysis, .. } => match analysis.kind { - FnKind::Method(ref self_ty, _) => self_ty.clone(), + FnKind::Method { ref impl_for, .. } => impl_for.clone(), FnKind::TraitMethod { ref impl_for, .. } => impl_for.clone(), FnKind::Function => { QualifiedName::new(self.name().get_namespace(), make_ident(&analysis.rust_name)) } }, Api::RustSubclassFn { subclass, .. } => subclass.0.name.clone(), + Api::IgnoredItem { + name, + ctx: Some(ctx), + .. + } => match ctx.get_type() { + ErrorContextType::Method { self_ty, .. } => { + QualifiedName::new(name.name.get_namespace(), self_ty.clone()) + } + ErrorContextType::Item(id) => { + QualifiedName::new(name.name.get_namespace(), id.clone()) + } + _ => name.name.clone(), + }, _ => self.name().clone(), } } @@ -1605,6 +2008,16 @@ | Api::CType { .. } | Api::RustSubclassFn { .. } | Api::Subclass { .. } + | Api::Struct { + analysis: PodAndDepAnalysis { + pod: PodAnalysis { + kind: TypeKind::Pod, + .. + }, + .. + }, + .. + } ) } @@ -1618,35 +2031,4 @@ _ => Some(self.name().get_final_ident()), } } - - /// Any dependencies on other APIs which this API has. - pub(crate) fn deps(&self) -> Box<dyn Iterator<Item = QualifiedName> + '_> { - match self { - Api::Typedef { - old_tyname, - analysis: TypedefAnalysis { deps, .. }, - .. - } => Box::new(old_tyname.iter().chain(deps.iter()).cloned()), - Api::Struct { - analysis: - PodAnalysis { - kind: TypeKind::Pod, - field_types, - .. - }, - .. - } => Box::new(field_types.iter().cloned()), - Api::Function { analysis, .. } => Box::new(analysis.deps.iter().cloned()), - Api::Subclass { - name: _, - superclass, - } => Box::new(std::iter::once(superclass.clone())), - Api::RustSubclassFn { details, .. } => Box::new(details.dependency.iter().cloned()), - _ => Box::new(std::iter::empty()), - } - } - - pub(crate) fn format_deps(&self) -> String { - self.deps().join(",") - } }
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/overload_tracker.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/overload_tracker.rs similarity index 81% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/overload_tracker.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/overload_tracker.rs index 46f4d2f..6fc532c 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/overload_tracker.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/overload_tracker.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::collections::HashMap;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/subclass.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/subclass.rs similarity index 61% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/subclass.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/subclass.rs index 1d84851e..3bde56a 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/subclass.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/subclass.rs
@@ -1,16 +1,10 @@ // Copyright 2021 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::collections::HashMap; @@ -19,8 +13,10 @@ use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability}; use crate::conversion::analysis::pod::PodPhase; use crate::conversion::api::{ - CppVisibility, FuncToConvert, RustSubclassFnDetails, SubclassName, Synthesis, Virtualness, + CppVisibility, FuncToConvert, Provenance, RustSubclassFnDetails, SubclassConstructorDetails, + SubclassName, SuperclassMethod, UnsafetyNeeded, Virtualness, }; +use crate::conversion::apivec::ApiVec; use crate::{ conversion::{ analysis::fun::function_wrapper::{ @@ -31,10 +27,10 @@ types::{make_ident, Namespace, QualifiedName}, }; -use super::{FnAnalysis, FnPhase}; +use super::{FnAnalysis, FnPrePhase1}; pub(super) fn subclasses_by_superclass( - apis: &[Api<PodPhase>], + apis: &ApiVec<PodPhase>, ) -> HashMap<QualifiedName, Vec<SubclassName>> { let mut subclasses_per_superclass: HashMap<QualifiedName, Vec<SubclassName>> = HashMap::new(); @@ -50,7 +46,7 @@ } pub(super) fn create_subclass_fn_wrapper( - sub: SubclassName, + sub: &SubclassName, super_fn_name: &QualifiedName, fun: &FuncToConvert, ) -> Box<FuncToConvert> { @@ -69,19 +65,48 @@ unused_template_param: fun.unused_template_param, original_name: None, references: fun.references.clone(), - synthesis: fun.synthesis.clone(), + add_to_trait: fun.add_to_trait.clone(), is_deleted: fun.is_deleted, + synthetic_cpp: None, + provenance: Provenance::SynthesizedOther, }) } +pub(super) fn create_subclass_trait_item( + name: ApiName, + analysis: &FnAnalysis, + receiver_mutability: &ReceiverMutability, + receiver: QualifiedName, + is_pure_virtual: bool, +) -> Api<FnPrePhase1> { + let param_names = analysis + .param_details + .iter() + .map(|pd| pd.name.clone()) + .collect(); + Api::SubclassTraitItem { + name, + details: SuperclassMethod { + name: make_ident(&analysis.rust_name), + params: analysis.params.clone(), + ret_type: analysis.ret_type.clone(), + param_names, + receiver_mutability: receiver_mutability.clone(), + requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false), + is_pure_virtual, + receiver, + }, + } +} + pub(super) fn create_subclass_function( sub: &SubclassName, analysis: &super::FnAnalysis, name: &ApiName, receiver_mutability: &ReceiverMutability, superclass: &QualifiedName, - dependency: Option<&QualifiedName>, -) -> Api<FnPhase> { + dependencies: Vec<QualifiedName>, +) -> Api<FnPrePhase1> { let cpp = sub.cpp(); let holder_name = sub.holder(); let rust_call_name = make_ident(format!( @@ -99,7 +124,13 @@ } else { CppFunctionKind::ConstMethod }; - let subclass_function: Api<FnPhase> = Api::RustSubclassFn { + let argument_conversion = analysis + .param_details + .iter() + .skip(1) + .map(|p| p.conversion.clone()) + .collect(); + Api::RustSubclassFn { name: ApiName::new_in_root_namespace(rust_call_name.clone()), subclass: sub.clone(), details: Box::new(RustSubclassFnDetails { @@ -108,30 +139,27 @@ method_name: make_ident(&analysis.rust_name), cpp_impl: CppFunction { payload: CppFunctionBody::FunctionCall(Namespace::new(), rust_call_name), - wrapper_function_name: name.name.get_final_ident(), + wrapper_function_name: make_ident(&analysis.rust_name), original_cpp_name: name.cpp_name(), return_conversion: analysis.ret_conversion.clone(), - argument_conversion: analysis - .param_details - .iter() - .skip(1) - .map(|p| p.conversion.clone()) - .collect(), + argument_conversion, kind, pass_obs_field: true, qualification: Some(cpp), }, superclass: superclass.clone(), receiver_mutability: receiver_mutability.clone(), - dependency: dependency.cloned(), - requires_unsafe: analysis.param_details.iter().any(|pd| pd.requires_unsafe), + dependencies, + requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false), is_pure_virtual: matches!( analysis.kind, - FnKind::Method(_, MethodKind::PureVirtual(..)) + FnKind::Method { + method_kind: MethodKind::PureVirtual(..), + .. + } ), }), - }; - subclass_function + } } pub(super) fn create_subclass_constructor( @@ -142,36 +170,33 @@ ) -> (Box<FuncToConvert>, ApiName) { let holder = sub.holder(); let cpp = sub.cpp(); - let synthesis = Some({ - let wrapper_function_name = cpp.get_final_ident(); - let initial_arg = TypeConversionPolicy::new_unconverted(parse_quote! { - rust::Box< #holder > - }); - let args = std::iter::once(initial_arg).chain( - analysis - .param_details - .iter() - .skip(1) // skip placement new destination - .map(|aa| aa.conversion.clone()), - ); - let cpp_impl = CppFunction { - payload: CppFunctionBody::ConstructSuperclass(sup.to_cpp_name()), - wrapper_function_name, - return_conversion: None, - argument_conversion: args.collect(), - kind: CppFunctionKind::SynthesizedConstructor, - pass_obs_field: false, - qualification: Some(cpp.clone()), - original_cpp_name: cpp.to_cpp_name(), - }; - Synthesis::SubclassConstructor { - subclass: sub.clone(), - cpp_impl: Box::new(cpp_impl), - is_trivial: analysis.param_details.len() == 1, // just placement new - // destination, no other parameters - } + let wrapper_function_name = cpp.get_final_ident(); + let initial_arg = TypeConversionPolicy::new_unconverted(parse_quote! { + rust::Box< #holder > }); - + let args = std::iter::once(initial_arg).chain( + analysis + .param_details + .iter() + .skip(1) // skip placement new destination + .map(|aa| aa.conversion.clone()), + ); + let cpp_impl = CppFunction { + payload: CppFunctionBody::ConstructSuperclass(sup.to_cpp_name()), + wrapper_function_name, + return_conversion: None, + argument_conversion: args.collect(), + kind: CppFunctionKind::SynthesizedConstructor, + pass_obs_field: false, + qualification: Some(cpp.clone()), + original_cpp_name: cpp.to_cpp_name(), + }; + let subclass_constructor_details = Box::new(SubclassConstructorDetails { + subclass: sub.clone(), + is_trivial: analysis.param_details.len() == 1, // just placement new + // destination, no other parameters + cpp_impl, + }); let subclass_constructor_name = make_ident(format!("{}_{}", cpp.get_final_item(), cpp.get_final_item())); let mut existing_params = fun.inputs.clone(); @@ -208,8 +233,10 @@ references: fun.references.clone(), synthesized_this_type: Some(cpp.clone()), self_ty: Some(cpp), - synthesis, + add_to_trait: None, is_deleted: fun.is_deleted, + synthetic_cpp: None, + provenance: Provenance::SynthesizedSubclassConstructor(subclass_constructor_details), }); let subclass_constructor_name = ApiName::new_with_cpp_name( &Namespace::new(),
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/gc.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/gc.rs similarity index 68% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/gc.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/gc.rs index dc1e807..0393459 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/gc.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/gc.rs
@@ -1,24 +1,21 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::collections::{HashMap, HashSet}; use autocxx_parser::IncludeCppConfig; -use crate::{conversion::api::Api, types::QualifiedName}; +use crate::{ + conversion::{api::Api, apivec::ApiVec}, + types::QualifiedName, +}; -use super::fun::FnPhase; +use super::{deps::HasDependencies, fun::FnPhase}; /// This is essentially mark-and-sweep garbage collection of the /// [Api]s that we've discovered. Why do we do this, you might wonder? @@ -38,32 +35,32 @@ /// don't care about the other parameter types passed into those /// APIs either. pub(crate) fn filter_apis_by_following_edges_from_allowlist( - mut apis: Vec<Api<FnPhase>>, + apis: ApiVec<FnPhase>, config: &IncludeCppConfig, -) -> Vec<Api<FnPhase>> { +) -> ApiVec<FnPhase> { let mut todos: Vec<QualifiedName> = apis .iter() .filter(|api| { - let tnforal = api.typename_for_allowlist(); + let tnforal = api.name_for_allowlist(); config.is_on_allowlist(&tnforal.to_cpp_name()) }) .map(Api::name) .cloned() .collect(); - let mut by_typename: HashMap<QualifiedName, Vec<Api<FnPhase>>> = HashMap::new(); - for api in apis.drain(..) { + let mut by_typename: HashMap<QualifiedName, ApiVec<FnPhase>> = HashMap::new(); + for api in apis.into_iter() { let tn = api.name().clone(); by_typename.entry(tn).or_default().push(api); } let mut done = HashSet::new(); - let mut output = Vec::new(); + let mut output = ApiVec::new(); while !todos.is_empty() { let todo = todos.remove(0); if done.contains(&todo) { continue; } if let Some(mut these_apis) = by_typename.remove(&todo) { - todos.extend(these_apis.iter().flat_map(|api| api.deps())); + todos.extend(these_apis.iter().flat_map(|api| api.deps().cloned())); output.append(&mut these_apis); } // otherwise, probably an intrinsic e.g. uint32_t. done.insert(todo);
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/mod.rs new file mode 100644 index 0000000..a4abe099 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/mod.rs
@@ -0,0 +1,24 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub(crate) mod abstract_types; +pub(crate) mod allocators; +pub(crate) mod casts; +pub(crate) mod constructor_deps; +pub(crate) mod ctypes; +pub(crate) mod deps; +mod depth_first; +pub(crate) mod fun; +pub(crate) mod gc; +mod name_check; +pub(crate) mod pod; // hey, that rhymes +pub(crate) mod remove_ignored; +pub(crate) mod tdef; +mod type_converter; + +pub(crate) use name_check::check_names;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/name_check.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/name_check.rs similarity index 83% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/name_check.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/name_check.rs index 85ef8a9..0167438 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/name_check.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/name_check.rs
@@ -1,16 +1,10 @@ // Copyright 2021 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::collections::HashMap; @@ -19,6 +13,7 @@ use crate::{ conversion::{ api::{Api, SubclassName}, + apivec::ApiVec, error_reporter::convert_item_apis, ConvertError, }, @@ -29,14 +24,14 @@ /// Do some final checks that the names we've come up with can be represented /// within cxx. -pub(crate) fn check_names(apis: Vec<Api<FnPhase>>) -> Vec<Api<FnPhase>> { +pub(crate) fn check_names(apis: ApiVec<FnPhase>) -> ApiVec<FnPhase> { // If any items have names which can't be represented by cxx, // abort. This check should ideally be done at the times we fill in the // `name` field of each `api` in the first place, at parse time, though // as the `name` field of each API may change during various analysis phases, // currently it seems better to do it here to ensure we respect // the output of any such changes. - let mut intermediate = Vec::new(); + let mut intermediate = ApiVec::new(); convert_item_apis(apis, &mut intermediate, |api| match api { Api::Typedef { ref name, .. } | Api::ForwardDeclaration { ref name, .. } @@ -75,20 +70,21 @@ | Api::RustType { .. } | Api::RustSubclassFn { .. } | Api::RustFn { .. } + | Api::SubclassTraitItem { .. } | Api::IgnoredItem { .. } => Ok(Box::new(std::iter::once(api))), }); // Reject any names which are duplicates within the cxx bridge mod, // that has a flat namespace. let mut names_found: HashMap<Ident, usize> = HashMap::new(); - for api in &intermediate { + for api in intermediate.iter() { let my_name = api.cxxbridge_name(); if let Some(name) = my_name { let e = names_found.entry(name).or_default(); *e += 1usize; } } - let mut results = Vec::new(); + let mut results = ApiVec::new(); convert_item_apis(intermediate, &mut results, |api| { let my_name = api.cxxbridge_name(); if let Some(name) = my_name {
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/byvalue_checker.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/byvalue_checker.rs similarity index 94% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/byvalue_checker.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/byvalue_checker.rs index f1499cf..b6d1c6d9 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/byvalue_checker.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/byvalue_checker.rs
@@ -1,17 +1,12 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use crate::conversion::apivec::ApiVec; use crate::{conversion::ConvertError, known_types::known_types}; use crate::{ conversion::{ @@ -75,7 +70,7 @@ /// Scan APIs to work out which are by-value safe. Constructs a [ByValueChecker] /// that others can use to query the results. pub(crate) fn new_from_apis( - apis: &[Api<TypedefPhase>], + apis: &ApiVec<TypedefPhase>, config: &IncludeCppConfig, ) -> Result<ByValueChecker, ConvertError> { let mut byvalue_checker = ByValueChecker::new(); @@ -86,8 +81,8 @@ .results .insert(tn, StructDetails::new(safety)); } - for api in apis { - match &api { + for api in apis.iter() { + match api { Api::Typedef { analysis, .. } => { let name = api.name(); let typedef_type = match analysis.kind {
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/mod.rs similarity index 62% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/mod.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/mod.rs index 735e9c3..4b5cd72 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/mod.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/mod.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. mod byvalue_checker; @@ -18,15 +12,13 @@ use autocxx_parser::IncludeCppConfig; use byvalue_checker::ByValueChecker; -use syn::{FnArg, ItemEnum, ItemStruct, Type, TypePtr, Visibility}; +use syn::{ItemEnum, ItemStruct, Type, Visibility}; use crate::{ conversion::{ - analysis::type_converter::{add_analysis, TypeConversionContext, TypeConverter}, - api::{ - AnalysisPhase, Api, ApiName, CppVisibility, FuncToConvert, SpecialMemberKind, - StructDetails, TypeKind, UnanalyzedApi, - }, + analysis::type_converter::{self, add_analysis, TypeConversionContext, TypeConverter}, + api::{AnalysisPhase, Api, ApiName, CppVisibility, NullPhase, StructDetails, TypeKind}, + apivec::ApiVec, convert_error::{ConvertErrorWithContext, ErrorContext}, error_reporter::convert_apis, parse::BindgenSemanticAttributes, @@ -37,6 +29,11 @@ use super::tdef::{TypedefAnalysis, TypedefPhase}; +pub(crate) struct FieldInfo { + pub(crate) ty: Type, + pub(crate) type_kind: type_converter::TypeKind, +} + pub(crate) struct PodAnalysis { pub(crate) kind: TypeKind, pub(crate) bases: HashSet<QualifiedName>, @@ -45,8 +42,9 @@ /// because otherwise we don't know whether they're /// abstract or not. pub(crate) castable_bases: HashSet<QualifiedName>, - pub(crate) field_types: HashSet<QualifiedName>, - pub(crate) movable: bool, + pub(crate) field_deps: HashSet<QualifiedName>, + pub(crate) field_info: Vec<FieldInfo>, + pub(crate) is_generic: bool, } pub(crate) struct PodPhase; @@ -63,19 +61,17 @@ /// and an object which can be used to query the POD status of any /// type whether or not it's one of the [Api]s. pub(crate) fn analyze_pod_apis( - apis: Vec<Api<TypedefPhase>>, + apis: ApiVec<TypedefPhase>, config: &IncludeCppConfig, -) -> Result<Vec<Api<PodPhase>>, ConvertError> { +) -> Result<ApiVec<PodPhase>, ConvertError> { // This next line will return an error if any of the 'generate_pod' // directives from the user can't be met because, for instance, // a type contains a std::string or some other type which can't be // held safely by value in Rust. let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?; - // We'll also note which types have deleted move constructors. - let deleted_move_constructors = find_deleted_move_and_copy_constructors(&apis); - let mut extra_apis = Vec::new(); + let mut extra_apis = ApiVec::new(); let mut type_converter = TypeConverter::new(config, &apis); - let mut results = Vec::new(); + let mut results = ApiVec::new(); convert_apis( apis, &mut results, @@ -88,7 +84,6 @@ name, details, config, - &deleted_move_constructors, ) }, analyze_enum, @@ -96,8 +91,8 @@ ); // Conceivably, the process of POD-analysing the first set of APIs could result // in us creating new APIs to concretize generic types. - let extra_apis: Vec<Api<PodPhase>> = extra_apis.into_iter().map(add_analysis).collect(); - let mut more_extra_apis = Vec::new(); + let extra_apis: ApiVec<PodPhase> = extra_apis.into_iter().map(add_analysis).collect(); + let mut more_extra_apis = ApiVec::new(); convert_apis( extra_apis, &mut results, @@ -110,7 +105,6 @@ name, details, config, - &deleted_move_constructors, ) }, analyze_enum, @@ -132,29 +126,29 @@ fn analyze_struct( byvalue_checker: &ByValueChecker, type_converter: &mut TypeConverter, - extra_apis: &mut Vec<UnanalyzedApi>, + extra_apis: &mut ApiVec<NullPhase>, name: ApiName, mut details: Box<StructDetails>, config: &IncludeCppConfig, - deleted_move_constructors: &HashSet<QualifiedName>, ) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> { - let movable = !deleted_move_constructors.contains(&name.name); let id = name.name.get_final_ident(); if details.vis != CppVisibility::Public { return Err(ConvertErrorWithContext( ConvertError::NonPublicNestedType, - Some(ErrorContext::Item(id)), + Some(ErrorContext::new_for_item(id)), )); } let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs); metadata.check_for_fatal_attrs(&id)?; let bases = get_bases(&details.item); - let mut field_types = HashSet::new(); + let mut field_deps = HashSet::new(); + let mut field_info = Vec::new(); let field_conversion_errors = get_struct_field_types( type_converter, name.name.get_namespace(), &details.item, - &mut field_types, + &mut field_deps, + &mut field_info, extra_apis, ); let type_kind = if byvalue_checker.is_pod(&name.name) { @@ -163,11 +157,14 @@ if details.has_rvalue_reference_fields { return Err(ConvertErrorWithContext( ConvertError::RValueReferenceField, - Some(ErrorContext::Item(id)), + Some(ErrorContext::new_for_item(id)), )); } if let Some(err) = field_conversion_errors.into_iter().next() { - return Err(ConvertErrorWithContext(err, Some(ErrorContext::Item(id)))); + return Err(ConvertErrorWithContext( + err, + Some(ErrorContext::new_for_item(id)), + )); } TypeKind::Pod } else { @@ -180,6 +177,7 @@ .filter(|base| config.is_on_allowlist(&base.to_cpp_name())) .cloned() .collect(); + let is_generic = !details.item.generics.params.is_empty(); Ok(Box::new(std::iter::once(Api::Struct { name, details, @@ -187,8 +185,9 @@ kind: type_kind, bases: bases.into_keys().collect(), castable_bases, - field_types, - movable, + field_deps, + field_info, + is_generic, }, }))) } @@ -197,17 +196,31 @@ type_converter: &mut TypeConverter, ns: &Namespace, s: &ItemStruct, - deps: &mut HashSet<QualifiedName>, - extra_apis: &mut Vec<UnanalyzedApi>, + field_deps: &mut HashSet<QualifiedName>, + field_info: &mut Vec<FieldInfo>, + extra_apis: &mut ApiVec<NullPhase>, ) -> Vec<ConvertError> { let mut convert_errors = Vec::new(); for f in &s.fields { let annotated = type_converter.convert_type(f.ty.clone(), ns, &TypeConversionContext::CxxInnerType); match annotated { - Ok(r) => { - extra_apis.extend(r.extra_apis); - deps.extend(r.types_encountered); + Ok(mut r) => { + extra_apis.append(&mut r.extra_apis); + // Skip base classes represented as fields. Anything which wants to include bases can chain + // those to the list we're building. + if !f + .ident + .as_ref() + .map(|id| id.to_string().starts_with("_base")) + .unwrap_or(false) + { + field_deps.extend(r.types_encountered); + field_info.push(FieldInfo { + ty: r.ty, + type_kind: r.kind, + }); + } } Err(e) => convert_errors.push(e), }; @@ -232,40 +245,3 @@ }) .collect() } - -fn find_deleted_move_and_copy_constructors(apis: &[Api<TypedefPhase>]) -> HashSet<QualifiedName> { - // Remove any deleted move + copy constructors from the API list and list the types - // that they construct. - apis.iter().filter_map(|api| match api { - Api::Function { ref fun, .. } => match &**fun { - FuncToConvert { - special_member: - Some(SpecialMemberKind::MoveConstructor | SpecialMemberKind::CopyConstructor), - is_deleted: true, - inputs, - .. - } => match is_a_pointer_arg(inputs.iter().next()) { - Some(ty) => Some(ty), - _ => panic!("found special constructor member with something other than a pointer first arg"), - }, - _ => None - }, - _ => None, - }).collect() -} - -/// Determine if a function argument is a pointer, and if so, to what. -/// It's unfortunate that we need to do this during the POD analysis but -/// for now, it's the best way to identify special constructor members. -fn is_a_pointer_arg(arg: Option<&FnArg>) -> Option<QualifiedName> { - arg.and_then(|arg| match arg { - FnArg::Receiver(..) => None, - FnArg::Typed(pt) => match &*pt.ty { - Type::Ptr(TypePtr { elem, .. }) => match &**elem { - Type::Path(typ) => Some(QualifiedName::from_type_path(typ)), - _ => None, - }, - _ => None, - }, - }) -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/remove_ignored.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/remove_ignored.rs similarity index 60% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/remove_ignored.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/remove_ignored.rs index 6d9670a..ef20390 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/remove_ignored.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/remove_ignored.rs
@@ -1,20 +1,16 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::collections::HashSet; +use super::deps::HasDependencies; use super::fun::{FnAnalysis, FnKind, FnPhase}; +use crate::conversion::apivec::ApiVec; use crate::conversion::{convert_error::ErrorContext, ConvertError}; use crate::{conversion::api::Api, known_types}; @@ -22,16 +18,10 @@ /// We also eliminate any APIs that depend on some type that we just don't /// know about at all. In either case, we don't simply remove the type, but instead /// replace it with an error marker. -pub(crate) fn filter_apis_by_ignored_dependents(mut apis: Vec<Api<FnPhase>>) -> Vec<Api<FnPhase>> { - let (ignored_items, valid_items): (Vec<&Api<_>>, Vec<&Api<_>>) = apis.iter().partition(|api| { - matches!( - api, - Api::IgnoredItem { - ctx: ErrorContext::Item(..), - .. - } - ) - }); +pub(crate) fn filter_apis_by_ignored_dependents(mut apis: ApiVec<FnPhase>) -> ApiVec<FnPhase> { + let (ignored_items, valid_items): (Vec<&Api<_>>, Vec<&Api<_>>) = apis + .iter() + .partition(|api| matches!(api, Api::IgnoredItem { .. })); let mut ignored_items: HashSet<_> = ignored_items .into_iter() .map(|api| api.name().clone()) @@ -46,17 +36,22 @@ apis = apis .into_iter() .map(|api| { - if api.deps().any(|dep| ignored_items.contains(&dep)) { + let ignored_dependents: HashSet<_> = api + .deps() + .filter(|dep| ignored_items.contains(dep)) + .cloned() + .collect(); + if !ignored_dependents.is_empty() { iterate_again = true; ignored_items.insert(api.name().clone()); - create_ignore_item(api, ConvertError::IgnoredDependent) + create_ignore_item(api, ConvertError::IgnoredDependent(ignored_dependents)) } else { let mut missing_deps = api.deps().filter(|dep| { !valid_types.contains(dep) && !known_types().is_known_type(dep) }); let first = missing_deps.next(); std::mem::drop(missing_deps); - if let Some(missing_dep) = first { + if let Some(missing_dep) = first.cloned() { create_ignore_item(api, ConvertError::UnknownDependentType(missing_dep)) } else { api @@ -78,15 +73,23 @@ Api::Function { analysis: FnAnalysis { - kind: FnKind::Method(self_ty, _), + kind: FnKind::TraitMethod { .. }, .. }, .. - } => ErrorContext::Method { - self_ty: self_ty.get_final_ident(), - method: id, - }, - _ => ErrorContext::Item(id), + } => None, + Api::Function { + analysis: + FnAnalysis { + kind: + FnKind::Method { + impl_for: self_ty, .. + }, + .. + }, + .. + } => Some(ErrorContext::new_for_method(self_ty.get_final_ident(), id)), + _ => Some(ErrorContext::new_for_item(id)), }, } }
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/tdef.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/tdef.rs similarity index 78% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/tdef.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/tdef.rs index 1cef4fa4..941ddcc 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/tdef.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/tdef.rs
@@ -1,16 +1,10 @@ // Copyright 2021 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::collections::HashSet; @@ -20,7 +14,8 @@ use crate::{ conversion::{ analysis::type_converter::{add_analysis, Annotated, TypeConversionContext, TypeConverter}, - api::{AnalysisPhase, Api, ApiName, TypedefKind, UnanalyzedApi}, + api::{AnalysisPhase, Api, ApiName, NullPhase, TypedefKind}, + apivec::ApiVec, convert_error::{ConvertErrorWithContext, ErrorContext}, error_reporter::convert_apis, parse::BindgenSemanticAttributes, @@ -47,11 +42,11 @@ #[allow(clippy::needless_collect)] // we need the extra collect because the closure borrows extra_apis pub(crate) fn convert_typedef_targets( config: &IncludeCppConfig, - apis: Vec<UnanalyzedApi>, -) -> Vec<Api<TypedefPhase>> { + apis: ApiVec<NullPhase>, +) -> ApiVec<TypedefPhase> { let mut type_converter = TypeConverter::new(config, &apis); - let mut extra_apis = Vec::new(); - let mut results = Vec::new(); + let mut extra_apis = ApiVec::new(); + let mut results = ApiVec::new(); convert_apis( apis, &mut results, @@ -88,7 +83,7 @@ ity: ItemType, old_tyname: Option<QualifiedName>, type_converter: &mut TypeConverter, - extra_apis: &mut Vec<UnanalyzedApi>, + extra_apis: &mut ApiVec<NullPhase>, ) -> Result<Api<TypedefPhase>, ConvertErrorWithContext> { let mut converted_type = ity.clone(); let metadata = BindgenSemanticAttributes::new_retaining_others(&mut converted_type.attrs); @@ -101,14 +96,14 @@ match type_conversion_results { Err(err) => Err(ConvertErrorWithContext( err, - Some(ErrorContext::Item(name.name.get_final_ident())), + Some(ErrorContext::new_for_item(name.name.get_final_ident())), )), Ok(Annotated { ty: syn::Type::Path(ref typ), .. }) if QualifiedName::from_type_path(typ) == name.name => Err(ConvertErrorWithContext( ConvertError::InfinitelyRecursiveTypedef(name.name.clone()), - Some(ErrorContext::Item(name.name.get_final_ident())), + Some(ErrorContext::new_for_item(name.name.get_final_ident())), )), Ok(mut final_type) => { converted_type.ty = Box::new(final_type.ty.clone());
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/type_converter.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/type_converter.rs similarity index 91% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/type_converter.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/type_converter.rs index d661a1b..1112a51b 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/type_converter.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/type_converter.rs
@@ -1,20 +1,15 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use crate::{ conversion::{ - api::{AnalysisPhase, Api, ApiName, TypedefKind, UnanalyzedApi}, + api::{AnalysisPhase, Api, ApiName, NullPhase, TypedefKind, UnanalyzedApi}, + apivec::ApiVec, codegen_cpp::type_to_cpp::type_to_cpp, ConvertError, }, @@ -34,6 +29,7 @@ use super::tdef::TypedefAnalysis; /// Certain kinds of type may require special handling by callers. +#[derive(Debug)] pub(crate) enum TypeKind { Regular, Pointer, @@ -47,7 +43,7 @@ pub(crate) struct Annotated<T> { pub(crate) ty: T, pub(crate) types_encountered: HashSet<QualifiedName>, - pub(crate) extra_apis: Vec<UnanalyzedApi>, + pub(crate) extra_apis: ApiVec<NullPhase>, pub(crate) kind: TypeKind, } @@ -55,7 +51,7 @@ fn new( ty: T, types_encountered: HashSet<QualifiedName>, - extra_apis: Vec<UnanalyzedApi>, + extra_apis: ApiVec<NullPhase>, kind: TypeKind, ) -> Self { Self { @@ -120,12 +116,12 @@ } impl<'a> TypeConverter<'a> { - pub(crate) fn new<A: AnalysisPhase>(config: &'a IncludeCppConfig, apis: &[Api<A>]) -> Self + pub(crate) fn new<A: AnalysisPhase>(config: &'a IncludeCppConfig, apis: &ApiVec<A>) -> Self where A::TypedefAnalysis: TypedefTarget, { Self { - types_found: Self::find_types(apis), + types_found: find_types(apis), typedefs: Self::find_typedefs(apis), concrete_templates: Self::find_concrete_templates(apis), forward_declarations: Self::find_incomplete_types(apis), @@ -261,7 +257,7 @@ return Ok(Annotated::new( Type::Ptr(resolved_tp.clone()), deps, - Vec::new(), + ApiVec::new(), TypeKind::Pointer, )) } @@ -269,7 +265,7 @@ return Ok(Annotated::new( other.clone(), deps, - Vec::new(), + ApiVec::new(), TypeKind::Regular, )) } @@ -290,7 +286,7 @@ None => typ, }; - let mut extra_apis = Vec::new(); + let mut extra_apis = ApiVec::new(); let mut kind = TypeKind::Regular; // Finally let's see if it's generic. @@ -343,7 +339,7 @@ { let mut new_pun = Punctuated::new(); let mut types_encountered = HashSet::new(); - let mut extra_apis = Vec::new(); + let mut extra_apis = ApiVec::new(); for arg in pun.into_iter() { new_pun.push(match arg { GenericArgument::Type(t) => { @@ -524,29 +520,7 @@ } } - fn find_types<A: AnalysisPhase>(apis: &[Api<A>]) -> HashSet<QualifiedName> { - apis.iter() - .filter_map(|api| match api { - Api::ForwardDeclaration { .. } - | Api::ConcreteType { .. } - | Api::Typedef { .. } - | Api::Enum { .. } - | Api::Struct { .. } - | Api::Subclass { .. } - | Api::RustType { .. } => Some(api.name()), - Api::StringConstructor { .. } - | Api::Function { .. } - | Api::Const { .. } - | Api::CType { .. } - | Api::RustSubclassFn { .. } - | Api::IgnoredItem { .. } - | Api::RustFn { .. } => None, - }) - .cloned() - .collect() - } - - fn find_typedefs<A: AnalysisPhase>(apis: &[Api<A>]) -> HashMap<QualifiedName, Type> + fn find_typedefs<A: AnalysisPhase>(apis: &ApiVec<A>) -> HashMap<QualifiedName, Type> where A::TypedefAnalysis: TypedefTarget, { @@ -562,7 +536,7 @@ } fn find_concrete_templates<A: AnalysisPhase>( - apis: &[Api<A>], + apis: &ApiVec<A>, ) -> HashMap<String, QualifiedName> { apis.iter() .filter_map(|api| match &api { @@ -574,7 +548,7 @@ .collect() } - fn find_incomplete_types<A: AnalysisPhase>(apis: &[Api<A>]) -> HashSet<QualifiedName> { + fn find_incomplete_types<A: AnalysisPhase>(apis: &ApiVec<A>) -> HashSet<QualifiedName> { apis.iter() .filter_map(|api| match api { Api::ForwardDeclaration { .. } => Some(api.name()), @@ -623,3 +597,26 @@ } } } + +pub(crate) fn find_types<A: AnalysisPhase>(apis: &ApiVec<A>) -> HashSet<QualifiedName> { + apis.iter() + .filter_map(|api| match api { + Api::ForwardDeclaration { .. } + | Api::ConcreteType { .. } + | Api::Typedef { .. } + | Api::Enum { .. } + | Api::Struct { .. } + | Api::Subclass { .. } + | Api::RustType { .. } => Some(api.name()), + Api::StringConstructor { .. } + | Api::Function { .. } + | Api::Const { .. } + | Api::CType { .. } + | Api::RustSubclassFn { .. } + | Api::IgnoredItem { .. } + | Api::SubclassTraitItem { .. } + | Api::RustFn { .. } => None, + }) + .cloned() + .collect() +}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/api.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/api.rs similarity index 77% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/api.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/api.rs index 798c03f..7eb8ac67 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/api.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/api.rs
@@ -1,29 +1,30 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::collections::HashSet; use crate::types::{make_ident, Namespace, QualifiedName}; use autocxx_parser::RustPath; +use itertools::Itertools; +use quote::ToTokens; use syn::{ - parse::Parse, punctuated::Punctuated, token::Comma, Attribute, FnArg, Ident, ImplItem, - ItemConst, ItemEnum, ItemStruct, ItemType, ItemUse, LitBool, LitInt, ReturnType, Signature, - Type, Visibility, + parse::Parse, + punctuated::Punctuated, + token::{Comma, Unsafe}, + Attribute, FnArg, Ident, ItemConst, ItemEnum, ItemStruct, ItemType, ItemUse, LitBool, LitInt, + Pat, ReturnType, Signature, Type, Visibility, }; use super::{ - analysis::fun::{function_wrapper::CppFunction, ReceiverMutability}, + analysis::fun::{ + function_wrapper::{CppFunction, CppFunctionBody, CppFunctionKind}, + ReceiverMutability, + }, convert_error::{ConvertErrorWithContext, ErrorContext}, ConvertError, }; @@ -39,12 +40,6 @@ // in which case we'll err on the side of caution. } -/// An entry which needs to go into an `impl` block for a given type. -pub(crate) struct ImplBlockDetails { - pub(crate) item: ImplItem, - pub(crate) ty: Ident, -} - /// C++ visibility. #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub(crate) enum CppVisibility { @@ -101,20 +96,41 @@ MutToMut, } -/// Indicates that this function didn't exist originally in the C++ -/// but we've created it afresh. +/// Indicates that this function (which is synthetic) should +/// be a trait implementation rather than a method or free function. #[derive(Clone)] -pub(crate) enum Synthesis { - MakeUnique, - SubclassConstructor { - subclass: SubclassName, - cpp_impl: Box<CppFunction>, - is_trivial: bool, - }, +pub(crate) enum TraitSynthesis { Cast { to_type: QualifiedName, mutable: CastMutability, }, + AllocUninitialized(QualifiedName), + FreeUninitialized(QualifiedName), +} + +/// Details of a subclass constructor. +/// TODO: zap this; replace with an extra API. +#[derive(Clone)] +pub(crate) struct SubclassConstructorDetails { + pub(crate) subclass: SubclassName, + pub(crate) is_trivial: bool, + /// Implementation of the constructor _itself_ as distinct + /// from any wrapper function we create to call it. + pub(crate) cpp_impl: CppFunction, +} + +/// Contributions to traits representing C++ superclasses that +/// we may implement as Rust subclasses. +#[derive(Clone)] +pub(crate) struct SuperclassMethod { + pub(crate) name: Ident, + pub(crate) receiver: QualifiedName, + pub(crate) params: Punctuated<FnArg, Comma>, + pub(crate) param_names: Vec<Pat>, + pub(crate) ret_type: ReturnType, + pub(crate) receiver_mutability: ReceiverMutability, + pub(crate) requires_unsafe: UnsafetyNeeded, + pub(crate) is_pure_virtual: bool, } /// Information about references (as opposed to pointers) to be found @@ -138,7 +154,46 @@ } } -#[derive(Clone, Hash, Eq, PartialEq)] +#[derive(Clone)] +pub(crate) struct TraitImplSignature { + pub(crate) ty: Type, + pub(crate) trait_signature: Type, + /// The trait is 'unsafe' itself + pub(crate) unsafety: Option<Unsafe>, +} + +impl Eq for TraitImplSignature {} + +impl PartialEq for TraitImplSignature { + fn eq(&self, other: &Self) -> bool { + totokens_equal(&self.unsafety, &other.unsafety) + && totokens_equal(&self.ty, &other.ty) + && totokens_equal(&self.trait_signature, &other.trait_signature) + } +} + +fn totokens_to_string<T: ToTokens>(a: &T) -> String { + a.to_token_stream().to_string() +} + +fn totokens_equal<T: ToTokens>(a: &T, b: &T) -> bool { + totokens_to_string(a) == totokens_to_string(b) +} + +fn hash_totokens<T: ToTokens, H: std::hash::Hasher>(a: &T, state: &mut H) { + use std::hash::Hash; + totokens_to_string(a).hash(state) +} + +impl std::hash::Hash for TraitImplSignature { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + hash_totokens(&self.ty, state); + hash_totokens(&self.trait_signature, state); + hash_totokens(&self.unsafety, state); + } +} + +#[derive(Clone, Debug)] pub(crate) enum SpecialMemberKind { DefaultConstructor, CopyConstructor, @@ -147,6 +202,14 @@ AssignmentOperator, } +#[derive(Clone)] +pub(crate) enum Provenance { + Bindgen, + SynthesizedOther, + SynthesizedMakeUnique, + SynthesizedSubclassConstructor(Box<SubclassConstructorDetails>), +} + /// A C++ function for which we need to generate bindings, but haven't /// yet analyzed in depth. This is little more than a `ForeignItemFn` /// broken down into its constituent parts, plus some metadata from the @@ -159,6 +222,7 @@ /// `synthesized_*` members which are not filled in from bindgen. #[derive(Clone)] pub(crate) struct FuncToConvert { + pub(crate) provenance: Provenance, pub(crate) ident: Ident, pub(crate) doc_attr: Option<Attribute>, pub(crate) inputs: Punctuated<FnArg, Comma>, @@ -177,9 +241,11 @@ /// method receiver, e.g. because we're making a subclass /// constructor, fill it in here. pub(crate) synthesized_this_type: Option<QualifiedName>, + /// If this function should actually belong to a trait. + pub(crate) add_to_trait: Option<TraitSynthesis>, /// If Some, this function didn't really exist in the original /// C++ and instead we're synthesizing it. - pub(crate) synthesis: Option<Synthesis>, + pub(crate) synthetic_cpp: Option<(CppFunctionBody, CppFunctionKind)>, pub(crate) is_deleted: bool, } @@ -244,6 +310,15 @@ .unwrap_or_else(|| self.name.get_final_item().to_string()) } + pub(crate) fn qualified_cpp_name(&self) -> String { + let cpp_name = self.cpp_name(); + self.name + .ns_segment_iter() + .cloned() + .chain(std::iter::once(cpp_name)) + .join("::") + } + pub(crate) fn cpp_name_if_present(&self) -> Option<&String> { self.cpp_name.as_ref() } @@ -294,6 +369,17 @@ fn with_suffix(&self, suffix: &str) -> Ident { make_ident(format!("{}{}", self.0.name.get_final_item(), suffix)) } + pub(crate) fn get_trait_api_name(sup: &QualifiedName, method_name: &str) -> QualifiedName { + QualifiedName::new( + sup.get_namespace(), + make_ident(format!( + "{}_{}_trait_item", + sup.get_final_item(), + method_name + )), + ) + } + // TODO this and the following should probably include both class name and method name pub(crate) fn get_super_fn_name(superclass_namespace: &Namespace, id: &str) -> QualifiedName { let id = make_ident(format!("{}_super", id)); QualifiedName::new(superclass_namespace, id) @@ -343,7 +429,6 @@ /// A function. May include some analysis. Function { name: ApiName, - name_for_gc: Option<QualifiedName>, fun: Box<FuncToConvert>, analysis: T::FunAnalysis, }, @@ -382,7 +467,7 @@ IgnoredItem { name: ApiName, err: ConvertError, - ctx: ErrorContext, + ctx: Option<ErrorContext>, }, /// A Rust type which is not a C++ type. RustType { name: ApiName, path: RustPath }, @@ -403,6 +488,12 @@ name: SubclassName, superclass: QualifiedName, }, + /// Contributions to the traits representing superclass methods that we might + /// subclass in Rust. + SubclassTraitItem { + name: ApiName, + details: SuperclassMethod, + }, } pub(crate) struct RustSubclassFnDetails { @@ -412,11 +503,18 @@ pub(crate) method_name: Ident, pub(crate) superclass: QualifiedName, pub(crate) receiver_mutability: ReceiverMutability, - pub(crate) dependency: Option<QualifiedName>, - pub(crate) requires_unsafe: bool, + pub(crate) dependencies: Vec<QualifiedName>, + pub(crate) requires_unsafe: UnsafetyNeeded, pub(crate) is_pure_virtual: bool, } +#[derive(Clone, Debug)] +pub(crate) enum UnsafetyNeeded { + None, + JustBridge, + Always, +} + impl<T: AnalysisPhase> Api<T> { pub(crate) fn name_info(&self) -> &ApiName { match self { @@ -434,6 +532,7 @@ Api::RustFn { name, .. } => name, Api::RustSubclassFn { name, .. } => name, Api::Subclass { name, .. } => &name.0, + Api::SubclassTraitItem { name, .. } => name, } } @@ -461,6 +560,12 @@ .unwrap_or_else(|| self.name().get_final_item()) } + /// If this API turns out to have the same QualifiedName as another, + /// whether it's OK to just discard it? + pub(crate) fn discard_duplicates(&self) -> bool { + matches!(self, Api::IgnoredItem { .. }) + } + pub(crate) fn valid_types(&self) -> Box<dyn Iterator<Item = QualifiedName>> { match self { Api::Subclass { name, .. } => Box::new( @@ -521,7 +626,6 @@ name: ApiName, fun: Box<FuncToConvert>, analysis: T::FunAnalysis, - name_for_gc: Option<QualifiedName>, ) -> Result<Box<dyn Iterator<Item = Api<T>>>, ConvertErrorWithContext> where T: 'static, @@ -530,7 +634,6 @@ name, fun, analysis, - name_for_gc, }))) }
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/apivec.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/apivec.rs new file mode 100644 index 0000000..9f234ef --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/apivec.rs
@@ -0,0 +1,137 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +use std::collections::HashSet; + +use crate::{ + conversion::{api::ApiName, convert_error::ErrorContext, ConvertError}, + types::QualifiedName, +}; + +use super::api::{AnalysisPhase, Api}; + +/// Newtype wrapper for a list of APIs, which enforces the invariant +/// that each API has a unique name. +/// +/// Specifically, each API should have a unique [`QualifiedName`] which is kept +/// within an [`ApiName`]. The [`QualifiedName`] is used to refer to this API +/// from others, e.g. to represent edges in the graph used for garbage collection, +/// so that's why this uniqueness is so important. +/// +/// At present, this type also refuses to allow mutation of an API once it +/// has been added to a set. This is because the autocxx engine is +/// fundamentally organized into lots of analysis phases, each one _adding_ +/// fields rather than mutating earlier fields. The idea here is that it's +/// impossible for stupid future maintainers (i.e. me) to make errors by +/// referring to fields before they're filled in. If a field exists, it's +/// correct. +/// +/// While this is currently the case, it's possible that in future we could +/// see legitimate reasons to break this latter invariant and allow mutation +/// of APIs within an existing `ApiVec`. But it's extremely important that +/// the naming-uniqueness-invariant remains, so any such mutation should +/// allow mutation only of other fields, not the name. +pub(crate) struct ApiVec<P: AnalysisPhase> { + apis: Vec<Api<P>>, + names: HashSet<QualifiedName>, +} + +impl<P: AnalysisPhase> ApiVec<P> { + pub(crate) fn push(&mut self, api: Api<P>) { + let name = api.name(); + let already_contains = self.already_contains(name); + if already_contains { + if api.discard_duplicates() { + // This is already an IgnoredItem or something else where + // we can silently drop it. + log::info!("Discarding duplicate API for {}", name); + } else { + log::info!( + "Duplicate API for {} - removing all of them and replacing with an IgnoredItem.", + name + ); + self.retain(|api| api.name() != name); + self.push(Api::IgnoredItem { + name: ApiName::new_from_qualified_name(name.clone()), + err: ConvertError::DuplicateItemsFoundInParsing, + ctx: Some(ErrorContext::new_for_item(name.get_final_ident())), + }) + } + } else { + self.names.insert(name.clone()); + self.apis.push(api) + } + } + + fn already_contains(&self, name: &QualifiedName) -> bool { + self.names.contains(name) + } + + pub(crate) fn new() -> Self { + Self::default() + } + + pub(crate) fn append(&mut self, more: &mut ApiVec<P>) { + self.extend(more.apis.drain(..)) + } + + pub(crate) fn extend(&mut self, it: impl Iterator<Item = Api<P>>) { + // Could be optimized in future + for api in it { + self.push(api) + } + } + + pub(crate) fn iter(&self) -> impl Iterator<Item = &Api<P>> { + self.apis.iter() + } + + pub(crate) fn into_iter(self) -> impl Iterator<Item = Api<P>> { + self.apis.into_iter() + } + + pub(crate) fn is_empty(&self) -> bool { + self.apis.is_empty() + } + + pub fn retain<F>(&mut self, f: F) + where + F: FnMut(&Api<P>) -> bool, + { + self.apis.retain(f); + self.names.clear(); + self.names + .extend(self.apis.iter().map(|api| api.name()).cloned()); + } +} + +impl<P: AnalysisPhase> Default for ApiVec<P> { + fn default() -> Self { + Self { + apis: Default::default(), + names: Default::default(), + } + } +} + +impl<P: AnalysisPhase> FromIterator<Api<P>> for ApiVec<P> { + fn from_iter<I: IntoIterator<Item = Api<P>>>(iter: I) -> Self { + let mut this = ApiVec::new(); + for i in iter { + // Could be optimized in future + this.push(i); + } + this + } +}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs new file mode 100644 index 0000000..a2ea8bf --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs
@@ -0,0 +1,82 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::conversion::{ + analysis::fun::function_wrapper::{CppConversionType, TypeConversionPolicy}, + ConvertError, +}; + +use super::type_to_cpp::{type_to_cpp, CppNameMap}; + +impl TypeConversionPolicy { + pub(super) fn unconverted_type( + &self, + cpp_name_map: &CppNameMap, + ) -> Result<String, ConvertError> { + match self.cpp_conversion { + CppConversionType::FromUniquePtrToValue => self.unique_ptr_wrapped_type(cpp_name_map), + CppConversionType::FromPtrToValue => { + Ok(format!("{}*", self.unwrapped_type_as_string(cpp_name_map)?)) + } + _ => self.unwrapped_type_as_string(cpp_name_map), + } + } + + pub(super) fn converted_type(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> { + match self.cpp_conversion { + CppConversionType::FromValueToUniquePtr => self.unique_ptr_wrapped_type(cpp_name_map), + _ => self.unwrapped_type_as_string(cpp_name_map), + } + } + + fn unwrapped_type_as_string(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> { + type_to_cpp(&self.unwrapped_type, cpp_name_map) + } + + fn unique_ptr_wrapped_type( + &self, + original_name_map: &CppNameMap, + ) -> Result<String, ConvertError> { + Ok(format!( + "std::unique_ptr<{}>", + self.unwrapped_type_as_string(original_name_map)? + )) + } + + pub(super) fn cpp_conversion( + &self, + var_name: &str, + cpp_name_map: &CppNameMap, + is_return: bool, + ) -> Result<String, ConvertError> { + // If is_return we want to avoid unnecessary std::moves because they + // make RVO less effective + Ok(match self.cpp_conversion { + CppConversionType::None => var_name.to_string(), + CppConversionType::Move => { + format!("std::move({})", var_name) + } + CppConversionType::FromUniquePtrToValue | CppConversionType::FromPtrToMove => { + format!("std::move(*{})", var_name) + } + CppConversionType::FromValueToUniquePtr => format!( + "std::make_unique<{}>({})", + self.unconverted_type(cpp_name_map)?, + var_name + ), + CppConversionType::FromPtrToValue => { + let dereference = format!("*{}", var_name); + if is_return { + dereference + } else { + format!("std::move({})", dereference) + } + } + }) + } +}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/mod.rs similarity index 82% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/mod.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/mod.rs index b05859a2..a60adc7 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/mod.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/mod.rs
@@ -1,18 +1,13 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. mod function_wrapper_cpp; +mod new_and_delete_prelude; pub(crate) mod type_to_cpp; use crate::{ @@ -26,15 +21,19 @@ use type_to_cpp::{original_name_map_from_apis, type_to_cpp, CppNameMap}; use self::type_to_cpp::{ - identifier_using_original_name_map, namespaced_name_using_original_name_map, + final_ident_using_original_name_map, namespaced_name_using_original_name_map, }; use super::{ - analysis::fun::{ - function_wrapper::{CppFunction, CppFunctionBody}, - FnPhase, + analysis::{ + fun::{ + function_wrapper::{CppFunction, CppFunctionBody}, + FnPhase, PodAndDepAnalysis, + }, + pod::PodAnalysis, }, - api::{Api, SubclassName, Synthesis}, + api::{Api, Provenance, SubclassName, TypeKind}, + apivec::ApiVec, ConvertError, }; @@ -43,28 +42,27 @@ System(&'static str), CxxH, CxxgenH, + NewDeletePrelude, } impl Header { fn include_stmt(&self, cpp_codegen_options: &CppCodegenOptions) -> String { let blank = "".to_string(); - format!( - "#include {}", - match self { - Self::System(name) => format!("<{}>", name), - Self::CxxH => { - let prefix = cpp_codegen_options.path_to_cxx_h.as_ref().unwrap_or(&blank); - format!("\"{}cxx.h\"", prefix) - } - Self::CxxgenH => { - let prefix = cpp_codegen_options - .path_to_cxxgen_h - .as_ref() - .unwrap_or(&blank); - format!("\"{}cxxgen.h\"", prefix) - } + match self { + Self::System(name) => format!("#include <{}>", name), + Self::CxxH => { + let prefix = cpp_codegen_options.path_to_cxx_h.as_ref().unwrap_or(&blank); + format!("#include \"{}cxx.h\"", prefix) } - ) + Self::CxxgenH => { + let prefix = cpp_codegen_options + .path_to_cxxgen_h + .as_ref() + .unwrap_or(&blank); + format!("#include \"{}cxxgen.h\"", prefix) + } + Header::NewDeletePrelude => new_and_delete_prelude::NEW_AND_DELETE_PRELUDE.to_string(), + } } fn is_system(&self) -> bool { @@ -97,7 +95,7 @@ inclusions: String, original_name_map: CppNameMap, config: &'a IncludeCppConfig, - cpp_codegen_options: &'a CppCodegenOptions, + cpp_codegen_options: &'a CppCodegenOptions<'a>, } struct SubclassFunction<'a> { @@ -108,7 +106,7 @@ impl<'a> CppCodeGenerator<'a> { pub(crate) fn generate_cpp_code( inclusions: String, - apis: &[Api<FnPhase>], + apis: &ApiVec<FnPhase>, config: &'a IncludeCppConfig, cpp_codegen_options: &CppCodegenOptions, ) -> Result<Option<CppFilePair>, ConvertError> { @@ -161,14 +159,11 @@ fun, .. } => { - if let Some(Synthesis::SubclassConstructor { - subclass, cpp_impl, .. - }) = &fun.synthesis - { + if let Provenance::SynthesizedSubclassConstructor(details) = &fun.provenance { constructors_by_subclass - .entry(subclass.clone()) + .entry(details.subclass.clone()) .or_default() - .push(cpp_impl); + .push(&details.cpp_impl); } self.generate_cpp_function(cpp_wrapper)? } @@ -189,6 +184,21 @@ is_pure_virtual: details.is_pure_virtual, }); } + Api::Struct { + name, + analysis: + PodAndDepAnalysis { + pod: + PodAnalysis { + kind: TypeKind::Pod, + .. + }, + .. + }, + .. + } => { + self.generate_pod_assertion(name.qualified_cpp_name()); + } _ => panic!("Should have filtered on needs_cpp_codegen"), } } @@ -220,7 +230,10 @@ headers, self.inclusions, type_definitions, declarations ); log::info!("Additional C++ decls:\n{}", declarations); - let header_name = format!("autocxxgen_{}.h", self.config.get_mod_name()); + let header_name = self + .cpp_codegen_options + .header_namer + .name_header(self.config.get_mod_name().to_string()); let implementation = if self .additional_functions .iter() @@ -273,6 +286,24 @@ s } + fn generate_pod_assertion(&mut self, name: String) { + // These assertions are generated by cxx for trivial ExternTypes but + // *only if* such types are used as trivial types in the cxx::bridge. + // It's possible for types which we generate to be used even without + // passing through the cxx::bridge, and as we generate Drop impls, that + // can result in destructors for nested types being called multiple times + // if we represent them as trivial types. So generate an extra + // assertion to make sure. + let declaration = Some(format!("static_assert(::rust::IsRelocatable<{}>::value, \"type {} should be trivially move constructible and trivially destructible to be used with generate_pod! in autocxx\");", name, name)); + self.additional_functions.push(AdditionalFunction { + type_definition: None, + declaration, + definition: None, + headers: vec![Header::CxxH], + cpp_headers: Vec::new(), + }) + } + fn generate_string_constructor(&mut self) { let makestring_name = self.config.get_makestring_name(); let declaration = Some(format!("inline std::unique_ptr<std::string> {}(::rust::Str str) {{ return std::make_unique<std::string>(std::string(str)); }}", makestring_name)); @@ -418,24 +449,32 @@ } else { arg_list.join(", ") }; - let (mut underlying_function_call, field_assignments) = match &details.payload { - CppFunctionBody::MakeUnique | CppFunctionBody::Cast => (arg_list, "".to_string()), + let (mut underlying_function_call, field_assignments, need_allocators) = match &details + .payload + { + CppFunctionBody::MakeUnique | CppFunctionBody::Cast => { + (arg_list, "".to_string(), false) + } CppFunctionBody::PlacementNew(ns, id) => { let ty_id = QualifiedName::new(ns, id.clone()); - let ty_id = - namespaced_name_using_original_name_map(&ty_id, &self.original_name_map); + let ty_id = self.namespaced_name(&ty_id); ( format!("new ({}) {}({})", receiver.unwrap(), ty_id, arg_list), "".to_string(), + false, ) } CppFunctionBody::Destructor(ns, id) => { let ty_id = QualifiedName::new(ns, id.clone()); - let ty_id = identifier_using_original_name_map(&ty_id, &self.original_name_map); - (format!("{}->~{}()", arg_list, ty_id), "".to_string()) + let ty_id = final_ident_using_original_name_map(&ty_id, &self.original_name_map); + (format!("{}->~{}()", arg_list, ty_id), "".to_string(), false) } CppFunctionBody::FunctionCall(ns, id) => match receiver { - Some(receiver) => (format!("{}.{}({})", receiver, id, arg_list), "".to_string()), + Some(receiver) => ( + format!("{}.{}({})", receiver, id, arg_list), + "".to_string(), + false, + ), None => { let underlying_function_call = ns .into_iter() @@ -445,6 +484,7 @@ ( format!("{}({})", underlying_function_call, arg_list), "".to_string(), + false, ) } }, @@ -457,9 +497,23 @@ ( format!("{}({})", underlying_function_call, arg_list), "".to_string(), + false, ) } - CppFunctionBody::ConstructSuperclass(_) => ("".to_string(), arg_list), + CppFunctionBody::ConstructSuperclass(_) => ("".to_string(), arg_list, false), + CppFunctionBody::AllocUninitialized(ty) => { + let namespaced_ty = self.namespaced_name(ty); + ( + format!("new_appropriately<{}>(1);", namespaced_ty,), + "".to_string(), + true, + ) + } + CppFunctionBody::FreeUninitialized(ty) => ( + format!("delete_appropriately<{}>(arg0);", self.namespaced_name(ty)), + "".to_string(), + true, + ), }; if let Some(ret) = &details.return_conversion { underlying_function_call = format!( @@ -509,15 +563,23 @@ None, ) }; + let mut headers = vec![Header::System("memory")]; + if need_allocators { + headers.push(Header::NewDeletePrelude); + } Ok(AdditionalFunction { type_definition: None, declaration, definition, - headers: vec![Header::System("memory")], + headers, cpp_headers: Vec::new(), }) } + fn namespaced_name(&self, name: &QualifiedName) -> String { + namespaced_name_using_original_name_map(name, &self.original_name_map) + } + fn generate_ctype_typedef(&mut self, tn: &QualifiedName) { let cpp_name = tn.to_cpp_name(); self.generate_typedef(tn, cpp_name)
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/new_and_delete_prelude.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/new_and_delete_prelude.rs new file mode 100644 index 0000000..67e9e8cc --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/new_and_delete_prelude.rs
@@ -0,0 +1,44 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use indoc::indoc; + +/// This is logic to call either an overloaded operator new/delete +/// or the standard one. +/// The SFINAE magic here is: int is a better match than long, +/// and so the versions which match class-specific operator new/delete +/// will be used in preference to the general global ::operator new/delete. +pub(super) static NEW_AND_DELETE_PRELUDE: &str = indoc! {" + #include <stddef.h> + #ifndef AUTOCXX_NEW_AND_DELETE_PRELUDE + #define AUTOCXX_NEW_AND_DELETE_PRELUDE + // Mechanics to call custom operator new and delete + template <typename T> + auto delete_imp(T *ptr, int) -> decltype((void)T::operator delete(ptr)) { + T::operator delete(ptr); + } + template <typename T> void delete_imp(T *ptr, long) { ::operator delete(ptr); } + template <typename T> void delete_appropriately(T *obj) { + // 0 is a better match for the first 'delete_imp' so will match + // preferentially. + delete_imp(obj, 0); + } + template <typename T> + auto new_imp(size_t count, int) -> decltype(T::operator new(count)) { + return T::operator new(count); + } + template <typename T> void *new_imp(size_t count, long) { + return ::operator new(count); + } + template <typename T> T *new_appropriately(size_t count) { + // 0 is a better match for the first 'delete_imp' so will match + // preferentially. + return static_cast<T *>(new_imp<T>(count, 0)); + } + #endif // AUTOCXX_NEW_AND_DELETE_PRELUDE +"};
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/type_to_cpp.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/type_to_cpp.rs similarity index 81% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/type_to_cpp.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/type_to_cpp.rs index a132869..526609d 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/type_to_cpp.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/type_to_cpp.rs
@@ -1,19 +1,13 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use crate::{ - conversion::{api::Api, AnalysisPhase, ConvertError}, + conversion::{apivec::ApiVec, AnalysisPhase, ConvertError}, types::QualifiedName, }; use itertools::Itertools; @@ -27,7 +21,7 @@ /// in the QualifiedName. pub(crate) type CppNameMap = HashMap<QualifiedName, String>; -pub(crate) fn original_name_map_from_apis<T: AnalysisPhase>(apis: &[Api<T>]) -> CppNameMap { +pub(crate) fn original_name_map_from_apis<T: AnalysisPhase>(apis: &ApiVec<T>) -> CppNameMap { apis.iter() .filter_map(|api| { api.cpp_name() @@ -52,14 +46,22 @@ } } -pub(crate) fn identifier_using_original_name_map( +pub(crate) fn final_ident_using_original_name_map( qual_name: &QualifiedName, original_name_map: &CppNameMap, ) -> String { - original_name_map - .get(qual_name) - .cloned() - .unwrap_or_else(|| qual_name.get_final_cpp_item()) + match original_name_map.get(qual_name) { + Some(original_name) => { + // If we have an original name, this may be a nested struct + // (e.g. A::B). The final ident here is just 'B' so... + original_name + .rsplit_once("::") + .map_or(original_name.clone(), |(_, original_name)| { + original_name.to_string() + }) + } + None => qual_name.get_final_cpp_item(), + } } pub(crate) fn type_to_cpp(ty: &Type, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/fun_codegen.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/fun_codegen.rs new file mode 100644 index 0000000..8ce6166 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/fun_codegen.rs
@@ -0,0 +1,413 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::HashSet; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parse::Parser, + parse_quote, + punctuated::Punctuated, + token::{Comma, Unsafe}, + Attribute, FnArg, ForeignItem, Ident, ImplItem, Item, ReturnType, +}; + +use super::{ + unqualify::{unqualify_params, unqualify_ret_type}, + ImplBlockDetails, RsCodegenResult, TraitImplBlockDetails, Use, +}; +use crate::{ + conversion::{ + analysis::fun::{ + ArgumentAnalysis, FnAnalysis, FnKind, MethodKind, RustRenameStrategy, + TraitMethodDetails, + }, + api::UnsafetyNeeded, + codegen_rs::lifetime::add_lifetime_to_all_params, + }, + types::{Namespace, QualifiedName}, +}; +use crate::{ + conversion::{api::FuncToConvert, codegen_rs::lifetime::add_explicit_lifetime_if_necessary}, + types::make_ident, +}; + +impl UnsafetyNeeded { + pub(crate) fn bridge_token(&self) -> Option<Unsafe> { + match self { + UnsafetyNeeded::None => None, + _ => Some(parse_quote! { unsafe }), + } + } + + pub(crate) fn wrapper_token(&self) -> Option<Unsafe> { + match self { + UnsafetyNeeded::Always => Some(parse_quote! { unsafe }), + _ => None, + } + } + + pub(crate) fn from_param_details(params: &[ArgumentAnalysis], ignore_receiver: bool) -> Self { + params.iter().fold(UnsafetyNeeded::None, |accumulator, pd| { + if matches!(accumulator, UnsafetyNeeded::Always) { + UnsafetyNeeded::Always + } else if pd.self_type.is_some() && ignore_receiver { + if matches!( + pd.requires_unsafe, + UnsafetyNeeded::Always | UnsafetyNeeded::JustBridge + ) { + UnsafetyNeeded::JustBridge + } else { + accumulator + } + } else if matches!(pd.requires_unsafe, UnsafetyNeeded::Always) { + UnsafetyNeeded::Always + } else if matches!(accumulator, UnsafetyNeeded::JustBridge) + || matches!(pd.requires_unsafe, UnsafetyNeeded::JustBridge) + { + UnsafetyNeeded::JustBridge + } else { + UnsafetyNeeded::None + } + }) + } +} + +pub(super) fn gen_function( + ns: &Namespace, + fun: FuncToConvert, + analysis: FnAnalysis, + cpp_call_name: String, + non_pod_types: &HashSet<QualifiedName>, +) -> RsCodegenResult { + if analysis.ignore_reason.is_err() || !analysis.externally_callable { + return RsCodegenResult::default(); + } + let cxxbridge_name = analysis.cxxbridge_name; + let rust_name = &analysis.rust_name; + let ret_type = analysis.ret_type; + let param_details = analysis.param_details; + let wrapper_function_needed = analysis.cpp_wrapper.is_some(); + let params = analysis.params; + let vis = analysis.vis; + let kind = analysis.kind; + let doc_attr = fun.doc_attr; + + let mut cpp_name_attr = Vec::new(); + let mut impl_entry = None; + let mut trait_impl_entry = None; + let mut bindgen_mod_items = Vec::new(); + let always_unsafe_due_to_trait_definition = match kind { + FnKind::TraitMethod { ref details, .. } => details.trait_call_is_unsafe, + _ => false, + }; + let fn_generator = FnGenerator { + param_details: ¶m_details, + cxxbridge_name: &cxxbridge_name, + rust_name, + unsafety: &analysis.requires_unsafe, + always_unsafe_due_to_trait_definition, + doc_attr: &doc_attr, + non_pod_types, + }; + // In rare occasions, we might need to give an explicit lifetime. + let (lifetime_tokens, params, ret_type) = + add_explicit_lifetime_if_necessary(¶m_details, params, &ret_type, non_pod_types); + + if analysis.rust_wrapper_needed { + match kind { + FnKind::Method { + ref impl_for, + method_kind: MethodKind::Constructor { .. }, + .. + } => { + // Constructor. + impl_entry = Some(fn_generator.generate_constructor_impl(impl_for)); + } + FnKind::Method { + ref impl_for, + ref method_kind, + .. + } => { + // Method, or static method. + impl_entry = Some(fn_generator.generate_method_impl( + matches!( + method_kind, + MethodKind::MakeUnique | MethodKind::Constructor { .. } + ), + impl_for, + &ret_type, + )); + } + FnKind::TraitMethod { ref details, .. } => { + trait_impl_entry = Some(fn_generator.generate_trait_impl(details, &ret_type)); + } + _ => { + // Generate plain old function + bindgen_mod_items.push(fn_generator.generate_function_impl(&ret_type)); + } + } + } + + let materialization = match kind { + FnKind::Method { .. } | FnKind::TraitMethod { .. } => None, + FnKind::Function => match analysis.rust_rename_strategy { + _ if analysis.rust_wrapper_needed => { + Some(Use::SpecificNameFromBindgen(make_ident(rust_name))) + } + RustRenameStrategy::RenameInOutputMod(ref alias) => { + Some(Use::UsedFromCxxBridgeWithAlias(alias.clone())) + } + _ => Some(Use::UsedFromCxxBridge), + }, + }; + if cxxbridge_name != cpp_call_name && !wrapper_function_needed { + cpp_name_attr = Attribute::parse_outer + .parse2(quote!( + #[cxx_name = #cpp_call_name] + )) + .unwrap(); + } + + // Finally - namespace support. All the Types in everything + // above this point are fully qualified. We need to unqualify them. + // We need to do that _after_ the above wrapper_function_needed + // work, because it relies upon spotting fully qualified names like + // std::unique_ptr. However, after it's done its job, all such + // well-known types should be unqualified already (e.g. just UniquePtr) + // and the following code will act to unqualify only those types + // which the user has declared. + let params = unqualify_params(params); + let ret_type = unqualify_ret_type(ret_type.into_owned()); + // And we need to make an attribute for the namespace that the function + // itself is in. + let namespace_attr = if ns.is_empty() || wrapper_function_needed { + Vec::new() + } else { + let namespace_string = ns.to_string(); + Attribute::parse_outer + .parse2(quote!( + #[namespace = #namespace_string] + )) + .unwrap() + }; + // At last, actually generate the cxx::bridge entry. + let bridge_unsafety = analysis.requires_unsafe.bridge_token(); + let extern_c_mod_item = ForeignItem::Fn(parse_quote!( + #(#namespace_attr)* + #(#cpp_name_attr)* + #doc_attr + #vis #bridge_unsafety fn #cxxbridge_name #lifetime_tokens ( #params ) #ret_type; + )); + RsCodegenResult { + extern_c_mod_items: vec![extern_c_mod_item], + bindgen_mod_items, + impl_entry, + trait_impl_entry, + materializations: materialization.into_iter().collect(), + ..Default::default() + } +} + +/// Knows how to generate a given function. +#[derive(Clone)] +struct FnGenerator<'a> { + param_details: &'a [ArgumentAnalysis], + cxxbridge_name: &'a Ident, + rust_name: &'a str, + unsafety: &'a UnsafetyNeeded, + always_unsafe_due_to_trait_definition: bool, + doc_attr: &'a Option<Attribute>, + non_pod_types: &'a HashSet<QualifiedName>, +} + +impl<'a> FnGenerator<'a> { + fn generate_arg_lists( + &self, + avoid_self: bool, + ) -> (Punctuated<FnArg, Comma>, Vec<TokenStream>, Vec<TokenStream>) { + let mut wrapper_params: Punctuated<FnArg, Comma> = Punctuated::new(); + let mut local_variables = Vec::new(); + let mut arg_list = Vec::new(); + let wrap_unsafe_calls = self.should_wrap_unsafe_calls(); + for pd in self.param_details { + let type_name = pd.conversion.rust_wrapper_unconverted_type(); + let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self { + parse_quote!(self) + } else { + pd.name.clone() + }; + let param_mutability = pd.conversion.rust_conversion.requires_mutability(); + wrapper_params.push(parse_quote!( + #param_mutability #wrapper_arg_name: #type_name + )); + let (local_variable, actual_arg) = pd + .conversion + .rust_conversion(wrapper_arg_name, wrap_unsafe_calls); + arg_list.push(actual_arg); + local_variables.extend(local_variable.into_iter()); + } + (wrapper_params, local_variables, arg_list) + } + + /// Generate an 'impl Type { methods-go-here }' item + fn generate_method_impl( + &self, + avoid_self: bool, + impl_block_type_name: &QualifiedName, + ret_type: &ReturnType, + ) -> Box<ImplBlockDetails> { + let (wrapper_params, local_variables, arg_list) = self.generate_arg_lists(avoid_self); + let (lifetime_tokens, wrapper_params, ret_type) = add_explicit_lifetime_if_necessary( + self.param_details, + wrapper_params, + ret_type, + self.non_pod_types, + ); + let rust_name = make_ident(self.rust_name); + let unsafety = self.unsafety.wrapper_token(); + let doc_attr = self.doc_attr; + let cxxbridge_name = self.cxxbridge_name; + let call_body = self.wrap_call_with_unsafe(quote! { + cxxbridge::#cxxbridge_name ( #(#arg_list),* ) + }); + Box::new(ImplBlockDetails { + item: ImplItem::Method(parse_quote! { + #doc_attr + pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type { + #(#local_variables),* + #call_body + } + }), + ty: impl_block_type_name.get_final_ident(), + }) + } + + /// Generate an 'impl Trait for Type { methods-go-here }' in its entrety. + fn generate_trait_impl( + &self, + details: &TraitMethodDetails, + ret_type: &ReturnType, + ) -> Box<TraitImplBlockDetails> { + let (mut wrapper_params, local_variables, arg_list) = + self.generate_arg_lists(details.avoid_self); + if let Some(parameter_reordering) = &details.parameter_reordering { + wrapper_params = Self::reorder_parameters(wrapper_params, parameter_reordering); + } + let (lifetime_tokens, wrapper_params, ret_type) = add_explicit_lifetime_if_necessary( + self.param_details, + wrapper_params, + ret_type, + self.non_pod_types, + ); + let doc_attr = self.doc_attr; + let unsafety = self.unsafety.wrapper_token(); + let cxxbridge_name = self.cxxbridge_name; + let key = details.trt.clone(); + let method_name = &details.method_name; + let call_body = self.wrap_call_with_unsafe(quote! { + cxxbridge::#cxxbridge_name ( #(#arg_list),* ) + }); + let item = parse_quote! { + #doc_attr + #unsafety fn #method_name #lifetime_tokens ( #wrapper_params ) #ret_type { + #(#local_variables),* + #call_body + } + }; + Box::new(TraitImplBlockDetails { item, key }) + } + + fn should_wrap_unsafe_calls(&self) -> bool { + matches!(self.unsafety, UnsafetyNeeded::JustBridge) + || self.always_unsafe_due_to_trait_definition + } + + fn wrap_call_with_unsafe(&self, call: TokenStream) -> TokenStream { + if self.should_wrap_unsafe_calls() { + quote! { + unsafe { + #call + } + } + } else { + call + } + } + + /// Generate a 'impl Type { methods-go-here }' item which is a constructor + /// for use with moveit traits. + fn generate_constructor_impl( + &self, + impl_block_type_name: &QualifiedName, + ) -> Box<ImplBlockDetails> { + let (wrapper_params, local_variables, arg_list) = self.generate_arg_lists(true); + let mut wrapper_params: Punctuated<FnArg, Comma> = + wrapper_params.into_iter().skip(1).collect(); + let ptr_arg_name = &arg_list[0]; + let rust_name = make_ident(&self.rust_name); + let any_references = self.param_details.iter().any(|pd| pd.was_reference); + let (lifetime_param, lifetime_addition) = if any_references { + add_lifetime_to_all_params(&mut wrapper_params); + (quote! { <'a> }, quote! { + 'a }) + } else { + (quote! {}, quote! {}) + }; + let cxxbridge_name = self.cxxbridge_name; + let body = quote! { + #(#local_variables),* + autocxx::moveit::new::by_raw(move |#ptr_arg_name| { + let #ptr_arg_name = #ptr_arg_name.get_unchecked_mut().as_mut_ptr(); + cxxbridge::#cxxbridge_name(#(#arg_list),* ) + }) + }; + let body = self.wrap_call_with_unsafe(body); + let doc_attr = self.doc_attr; + let unsafety = self.unsafety.wrapper_token(); + Box::new(ImplBlockDetails { + item: ImplItem::Method(parse_quote! { + #doc_attr + pub #unsafety fn #rust_name #lifetime_param ( #wrapper_params ) -> impl autocxx::moveit::new::New<Output=Self> #lifetime_addition { + #body + } + }), + ty: impl_block_type_name.get_final_ident(), + }) + } + + /// Generate a function call wrapper + fn generate_function_impl(&self, ret_type: &ReturnType) -> Item { + let (wrapper_params, local_variables, arg_list) = self.generate_arg_lists(false); + let rust_name = make_ident(self.rust_name); + let doc_attr = self.doc_attr; + let unsafety = self.unsafety.wrapper_token(); + let cxxbridge_name = self.cxxbridge_name; + let body = self.wrap_call_with_unsafe(quote! { + cxxbridge::#cxxbridge_name ( #(#arg_list),* ) + }); + Item::Fn(parse_quote! { + #doc_attr + pub #unsafety fn #rust_name ( #wrapper_params ) #ret_type { + #(#local_variables),* + #body + } + }) + } + + fn reorder_parameters( + params: Punctuated<FnArg, Comma>, + parameter_ordering: &[usize], + ) -> Punctuated<FnArg, Comma> { + let old_params = params.into_iter().collect::<Vec<_>>(); + parameter_ordering + .iter() + .map(|n| old_params.get(*n).unwrap().clone()) + .collect() + } +}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/function_wrapper_rs.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/function_wrapper_rs.rs new file mode 100644 index 0000000..882300c --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/function_wrapper_rs.rs
@@ -0,0 +1,133 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use proc_macro2::TokenStream; +use syn::{Pat, Type, TypePtr}; + +use crate::{ + conversion::analysis::fun::function_wrapper::{RustConversionType, TypeConversionPolicy}, + types::make_ident, +}; +use quote::quote; +use syn::parse_quote; + +impl TypeConversionPolicy { + pub(super) fn rust_wrapper_unconverted_type(&self) -> Type { + match self.rust_conversion { + RustConversionType::None => self.converted_rust_type(), + RustConversionType::ToBoxedUpHolder(ref sub) => { + let id = sub.id(); + parse_quote! { autocxx::subclass::CppSubclassRustPeerHolder< + super::super::super:: #id> + } + } + RustConversionType::FromStr => parse_quote! { impl ToCppString }, + RustConversionType::FromPinMaybeUninitToPtr => { + let ty = match &self.unwrapped_type { + Type::Ptr(TypePtr { elem, .. }) => &*elem, + _ => panic!("Not a ptr"), + }; + parse_quote! { + ::std::pin::Pin<&mut ::std::mem::MaybeUninit< #ty >> + } + } + RustConversionType::FromPinMoveRefToPtr => { + let ty = match &self.unwrapped_type { + Type::Ptr(TypePtr { elem, .. }) => &*elem, + _ => panic!("Not a ptr"), + }; + parse_quote! { + ::std::pin::Pin<autocxx::moveit::MoveRef< '_, #ty >> + } + } + RustConversionType::FromTypeToPtr => { + let ty = match &self.unwrapped_type { + Type::Ptr(TypePtr { elem, .. }) => &*elem, + _ => panic!("Not a ptr"), + }; + parse_quote! { &mut #ty } + } + RustConversionType::FromValueParamToPtr => { + let ty = &self.unwrapped_type; + parse_quote! { impl autocxx::ValueParam<#ty> } + } + } + } + + pub(super) fn rust_conversion( + &self, + var: Pat, + wrap_in_unsafe: bool, + ) -> (Option<TokenStream>, TokenStream) { + match self.rust_conversion { + RustConversionType::None => (None, quote! { #var }), + RustConversionType::FromStr => (None, quote! ( #var .into_cpp() )), + RustConversionType::ToBoxedUpHolder(ref sub) => { + let holder_type = sub.holder(); + ( + None, + quote! { + Box::new(#holder_type(#var)) + }, + ) + } + RustConversionType::FromPinMaybeUninitToPtr => ( + None, + quote! { + #var.get_unchecked_mut().as_mut_ptr() + }, + ), + RustConversionType::FromPinMoveRefToPtr => ( + None, + quote! { + { let r: &mut _ = ::std::pin::Pin::into_inner_unchecked(#var.as_mut()); + r + } + }, + ), + RustConversionType::FromTypeToPtr => ( + None, + quote! { + #var + }, + ), + RustConversionType::FromValueParamToPtr => { + let var_name = if let Pat::Ident(pti) = &var { + &pti.ident + } else { + panic!("Unexpected non-ident parameter name"); + }; + let space_var_name = make_ident(format!("{}_space", var_name)); + let call = quote! { #space_var_name.as_mut().populate(#var_name); }; + let call = if wrap_in_unsafe { + quote! { + unsafe { + #call + } + } + } else { + call + }; + // This is the usual trick to put something on the stack, then + // immediately shadow the variable name so it can't be accessed or moved. + ( + Some(quote! { + let mut #space_var_name = autocxx::ValueParamHandler::default(); + let mut #space_var_name = unsafe { + std::pin::Pin::new_unchecked(&mut #space_var_name) + }; + #call + }), + quote! { + #space_var_name.get_ptr() + }, + ) + } + } + } +}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/impl_item_creator.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/impl_item_creator.rs new file mode 100644 index 0000000..89ef896 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/impl_item_creator.rs
@@ -0,0 +1,41 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use autocxx_parser::IncludeCppConfig; +use syn::{parse_quote, Ident, Item}; + +pub(crate) fn create_impl_items( + id: &Ident, + movable: bool, + destroyable: bool, + config: &IncludeCppConfig, +) -> Vec<Item> { + if config.exclude_impls { + return vec![]; + } + let mut results = Vec::new(); + if destroyable { + results.extend([ + Item::Impl(parse_quote! { + impl UniquePtr<#id> {} + }), + Item::Impl(parse_quote! { + impl SharedPtr<#id> {} + }), + Item::Impl(parse_quote! { + impl WeakPtr<#id> {} + }), + ]); + } + if movable { + results.push(Item::Impl(parse_quote! { + impl CxxVector<#id> {} + })) + } + results +}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/lifetime.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/lifetime.rs similarity index 61% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/lifetime.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/lifetime.rs index ffca951..1693d04 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/lifetime.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/lifetime.rs
@@ -1,34 +1,36 @@ // Copyright 2021 Google LLC // -// 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 -// -// https://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. -use crate::conversion::analysis::fun::{ArgumentAnalysis, ReceiverMutability}; +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use crate::{ + conversion::analysis::fun::{ArgumentAnalysis, ReceiverMutability}, + types::QualifiedName, +}; use proc_macro2::TokenStream; use quote::quote; -use std::borrow::Cow; +use std::{borrow::Cow, collections::HashSet}; use syn::{ parse_quote, punctuated::Punctuated, token::Comma, FnArg, GenericArgument, PatType, Path, - PathSegment, ReturnType, Type, TypePath, + PathSegment, ReturnType, Type, TypePath, TypeReference, }; /// Function which can add explicit lifetime parameters to function signatures /// where necessary, based on analysis of parameters and return types. -/// This is necessary only in one case - where the parameter is a Pin<&mut T> -/// and the return type is some kind of reference - because lifetime elision -/// is not smart enough to see inside a Pin. +/// This is necessary only in two cases: +/// 1) where the parameter is a Pin<&mut T> +/// and the return type is some kind of reference - because lifetime elision +/// is not smart enough to see inside a Pin. +/// 2) as a workaround for https://github.com/dtolnay/cxx/issues/1024, where the +/// input parameter is a non-POD type but the output reference is a POD or +/// built-in type pub(crate) fn add_explicit_lifetime_if_necessary<'r>( param_details: &[ArgumentAnalysis], mut params: Punctuated<FnArg, Comma>, ret_type: &'r ReturnType, + non_pod_types: &HashSet<QualifiedName>, ) -> ( Option<TokenStream>, Punctuated<FnArg, Comma>, @@ -37,7 +39,11 @@ let has_mutable_receiver = param_details .iter() .any(|pd| matches!(pd.self_type, Some((_, ReceiverMutability::Mutable)))); - if !has_mutable_receiver { + + let non_pod_ref_param = reference_parameter_is_non_pod_reference(¶ms, non_pod_types); + let ret_type_pod = return_type_is_pod_or_known_type_reference(ret_type, non_pod_types); + let hits_1024_bug = non_pod_ref_param && ret_type_pod; + if !(has_mutable_receiver || hits_1024_bug) { return (None, params, Cow::Borrowed(ret_type)); } let new_return_type = match ret_type { @@ -71,7 +77,8 @@ path: Path { segments, .. }, .. }) => add_lifetime_to_pinned_reference(segments).unwrap(), - _ => panic!("Expected Pin<T>"), + Type::Reference(tyr) => add_lifetime_to_reference(tyr), + _ => panic!("Expected Pin<&mut T> or &T"), }, _ => panic!("Unexpected fnarg"), } @@ -82,6 +89,44 @@ } } +fn reference_parameter_is_non_pod_reference( + params: &Punctuated<FnArg, Comma>, + non_pod_types: &HashSet<QualifiedName>, +) -> bool { + params.iter().any(|param| match param { + FnArg::Typed(PatType { ty, .. }) => match ty.as_ref() { + Type::Reference(TypeReference { elem, .. }) => match elem.as_ref() { + Type::Path(typ) => { + let qn = QualifiedName::from_type_path(typ); + non_pod_types.contains(&qn) + } + _ => false, + }, + _ => false, + }, + _ => false, + }) +} + +fn return_type_is_pod_or_known_type_reference( + ret_type: &ReturnType, + non_pod_types: &HashSet<QualifiedName>, +) -> bool { + match ret_type { + ReturnType::Type(_, boxed_type) => match boxed_type.as_ref() { + Type::Reference(rtr) => match rtr.elem.as_ref() { + Type::Path(typ) => { + let qn = QualifiedName::from_type_path(typ); + !non_pod_types.contains(&qn) + } + _ => false, + }, + _ => false, + }, + _ => false, + } +} + #[derive(Debug)] enum AddLifetimeError { WasNotPin,
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/mod.rs similarity index 85% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/mod.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/mod.rs index 321db2b8..78c48cf 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/mod.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/mod.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. mod fun_codegen; mod function_wrapper_rs; @@ -24,22 +18,21 @@ use autocxx_parser::IncludeCppConfig; +use itertools::Itertools; use proc_macro2::{Span, TokenStream}; use syn::{ parse_quote, punctuated::Punctuated, token::Comma, Attribute, Expr, FnArg, ForeignItem, - ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, Pat, ReturnType, TraitItem, + ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, TraitItem, }; use crate::{ conversion::{ - analysis::fun::MethodKind, codegen_rs::{ non_pod_struct::{make_non_pod, new_non_pod_struct}, unqualify::{unqualify_params, unqualify_ret_type}, }, doc_attr::get_doc_attr, }, - known_types::known_types, types::{make_ident, Namespace, QualifiedName}, }; use impl_item_creator::create_impl_items; @@ -50,20 +43,33 @@ }; use super::{ - analysis::fun::{FnAnalysis, FnKind}, - api::{Layout, RustSubclassFnDetails, Synthesis}, + analysis::{ + fun::{FnPhase, PodAndDepAnalysis, ReceiverMutability}, + pod::PodAnalysis, + }, + api::{AnalysisPhase, Api, SubclassName, TypeKind, TypedefKind}, + convert_error::ErrorContextType, +}; +use super::{ + api::{Layout, Provenance, RustSubclassFnDetails, SuperclassMethod, TraitImplSignature}, + apivec::ApiVec, codegen_cpp::type_to_cpp::{ namespaced_name_using_original_name_map, original_name_map_from_apis, CppNameMap, }, }; -use super::{ - analysis::fun::{FnPhase, ReceiverMutability}, - api::{AnalysisPhase, Api, ImplBlockDetails, SubclassName, TypeKind, TypedefKind}, -}; use super::{convert_error::ErrorContext, ConvertError}; use quote::{quote, ToTokens}; -unzip_n::unzip_n!(pub 4); +/// An entry which needs to go into an `impl` block for a given type. +struct ImplBlockDetails { + item: ImplItem, + ty: Ident, +} + +struct TraitImplBlockDetails { + item: TraitItem, + key: TraitImplSignature, +} /// Whether and how this item should be exposed in the mods constructed /// for actual end-user use. @@ -123,16 +129,6 @@ .to_vec() } -struct SuperclassMethod { - name: Ident, - params: Punctuated<FnArg, Comma>, - param_names: Vec<Pat>, - ret_type: ReturnType, - receiver_mutability: ReceiverMutability, - requires_unsafe: bool, - is_pure_virtual: bool, -} - /// Type which handles generation of Rust code. /// In practice, much of the "generation" involves connecting together /// existing lumps of code within the Api structures. @@ -141,32 +137,36 @@ bindgen_mod: ItemMod, original_name_map: CppNameMap, config: &'a IncludeCppConfig, + header_name: Option<String>, } impl<'a> RsCodeGenerator<'a> { /// Generate code for a set of APIs that was discovered during parsing. pub(crate) fn generate_rs_code( - all_apis: Vec<Api<FnPhase>>, + all_apis: ApiVec<FnPhase>, include_list: &'a [String], bindgen_mod: ItemMod, config: &'a IncludeCppConfig, + header_name: Option<String>, ) -> Vec<Item> { let c = Self { include_list, bindgen_mod, original_name_map: original_name_map_from_apis(&all_apis), config, + header_name, }; c.rs_codegen(all_apis) } - fn rs_codegen(mut self, all_apis: Vec<Api<FnPhase>>) -> Vec<Item> { + fn rs_codegen(mut self, all_apis: ApiVec<FnPhase>) -> Vec<Item> { // ... and now let's start to generate the output code. // First off, when we generate structs we may need to add some methods // if they're superclasses. let methods_by_superclass = self.accumulate_superclass_methods(&all_apis); let subclasses_with_a_single_trivial_constructor = find_trivially_constructed_subclasses(&all_apis); + let non_pod_types = find_non_pod_types(&all_apis); // Now let's generate the Rust code. let (rs_codegen_results_and_namespaces, additional_cpp_needs): (Vec<_>, Vec<_>) = all_apis .into_iter() @@ -177,6 +177,7 @@ api, &methods_by_superclass, &subclasses_with_a_single_trivial_constructor, + &non_pod_types, ); ((name, gen), more_cpp_needed) }) @@ -192,18 +193,22 @@ // sub-mods by namespace. From here on, things are flat. let (_, rs_codegen_results): (Vec<_>, Vec<_>) = rs_codegen_results_and_namespaces.into_iter().unzip(); - let (extern_c_mod_items, extern_rust_mod_items, all_items, bridge_items) = - rs_codegen_results - .into_iter() - .map(|api| { - ( - api.extern_c_mod_items, - api.extern_rust_mod_items, - api.global_items, - api.bridge_items, - ) - }) - .unzip_n_vec(); + let (extern_c_mod_items, extern_rust_mod_items, all_items, bridge_items): ( + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + ) = rs_codegen_results + .into_iter() + .map(|api| { + ( + api.extern_c_mod_items, + api.extern_rust_mod_items, + api.global_items, + api.bridge_items, + ) + }) + .multiunzip(); // Items for the [cxx::bridge] mod... let mut bridge_items: Vec<Item> = bridge_items.into_iter().flatten().collect(); // Things to include in the "extern "C"" mod passed within the cxx::bridge @@ -259,7 +264,7 @@ fn accumulate_superclass_methods( &self, - apis: &[Api<FnPhase>], + apis: &ApiVec<FnPhase>, ) -> HashMap<QualifiedName, Vec<SuperclassMethod>> { let mut results = HashMap::new(); results.extend( @@ -267,39 +272,11 @@ .superclasses() .map(|sc| (QualifiedName::new_from_cpp_name(sc), Vec::new())), ); - for api in apis { - if let Api::Function { - name, - analysis: - FnAnalysis { - kind: FnKind::Method(receiver, method_kind), - params, - ret_type, - param_details, - .. - }, - .. - } = api - { - match method_kind { - MethodKind::Virtual(receiver_mutability) - | MethodKind::PureVirtual(receiver_mutability) => { - let list = results.get_mut(receiver); - if let Some(list) = list { - let param_names = - param_details.iter().map(|pd| pd.name.clone()).collect(); - list.push(SuperclassMethod { - name: name.name.get_final_ident(), - params: params.clone(), - ret_type: ret_type.clone(), - param_names, - receiver_mutability: receiver_mutability.clone(), - requires_unsafe: param_details.iter().any(|pd| pd.requires_unsafe), - is_pure_virtual: matches!(method_kind, MethodKind::PureVirtual(..)), - }) - } - } - _ => {} // otherwise, this is not a superclass of anything specified as a subclass!(..) + for api in apis.iter() { + if let Api::SubclassTraitItem { details, .. } = api { + let list = results.get_mut(&details.receiver); + if let Some(list) = list { + list.push(details.clone()); } } } @@ -316,7 +293,7 @@ fn build_include_foreign_items(&self, has_additional_cpp_needs: bool) -> Vec<ForeignItem> { let extra_inclusion = if has_additional_cpp_needs { - Some(format!("autocxxgen_{}.h", self.config.get_mod_name())) + Some(self.header_name.clone().unwrap()) } else { None }; @@ -412,6 +389,7 @@ ns: &Namespace, ) { let mut impl_entries_by_type: HashMap<_, Vec<_>> = HashMap::new(); + let mut trait_impl_entries_by_trait_and_ty: HashMap<_, Vec<_>> = HashMap::new(); for item in ns_entries.entries() { output_items.extend(item.1.bindgen_mod_items.iter().cloned()); if let Some(impl_entry) = &item.1.impl_entry { @@ -420,6 +398,12 @@ .or_default() .push(&impl_entry.item); } + if let Some(trait_impl_entry) = &item.1.trait_impl_entry { + trait_impl_entries_by_trait_and_ty + .entry(trait_impl_entry.key.clone()) + .or_default() + .push(&trait_impl_entry.item); + } } for (ty, entries) in impl_entries_by_type.into_iter() { output_items.push(Item::Impl(parse_quote! { @@ -428,6 +412,16 @@ } })) } + for (key, entries) in trait_impl_entries_by_trait_and_ty.into_iter() { + let unsafety = key.unsafety; + let ty = key.ty; + let trt = key.trait_signature; + output_items.push(Item::Impl(parse_quote! { + #unsafety impl #trt for #ty { + #(#entries)* + } + })) + } for (child_name, child_ns_entries) in ns_entries.children() { let new_ns = ns.push((*child_name).clone()); let child_id = make_ident(child_name); @@ -467,6 +461,7 @@ api: Api<FnPhase>, associated_methods: &HashMap<QualifiedName, Vec<SuperclassMethod>>, subclasses_with_a_single_trivial_constructor: &HashSet<QualifiedName>, + non_pod_types: &HashSet<QualifiedName>, ) -> RsCodegenResult { let name = api.name().clone(); let id = name.get_final_ident(); @@ -485,9 +480,13 @@ ..Default::default() } } - Api::Function { fun, analysis, .. } => { - gen_function(name.get_namespace(), *fun, analysis, cpp_call_name) - } + Api::Function { fun, analysis, .. } => gen_function( + name.get_namespace(), + *fun, + analysis, + cpp_call_name, + non_pod_types, + ), Api::Const { const_item, .. } => RsCodegenResult { bindgen_mod_items: vec![Item::Const(const_item)], materializations: vec![Use::UsedFromBindgen], @@ -509,8 +508,9 @@ self.generate_type( &name, id, - analysis.kind, - analysis.movable, + analysis.pod.kind, + analysis.constructors.move_constructor, + analysis.constructors.destructor, || Some((Item::Struct(details.item), doc_attr)), associated_methods, layout, @@ -523,6 +523,7 @@ id, TypeKind::Pod, true, + true, || Some((Item::Enum(item), doc_attr)), associated_methods, None, @@ -533,6 +534,7 @@ id, TypeKind::Abstract, false, // assume for now that these types can't be kept in a Vector + true, // assume for now that these types can be put in a smart pointer || None, associated_methods, None, @@ -572,7 +574,12 @@ subclasses_with_a_single_trivial_constructor.contains(&name.0.name); self.generate_subclass(name, &superclass, methods, generate_peer_constructor) } - Api::IgnoredItem { err, ctx, .. } => Self::generate_error_entry(err, ctx), + Api::IgnoredItem { + err, + ctx: Some(ctx), + .. + } => Self::generate_error_entry(err, ctx), + Api::IgnoredItem { .. } | Api::SubclassTraitItem { .. } => RsCodegenResult::default(), } } @@ -635,7 +642,7 @@ let peer_fn = make_ident(peer_fn); *(params.iter_mut().next().unwrap()) = first_param; let param_names = m.param_names.iter().skip(1); - let unsafe_token = get_unsafe_token(m.requires_unsafe); + let unsafe_token = m.requires_unsafe.wrapper_token(); parse_quote! { #unsafe_token fn #cpp_super_method_name(#params) #ret { use autocxx::subclass::CppSubclass; @@ -698,9 +705,10 @@ }); RsCodegenResult { extern_c_mod_items, - // For now we just assume we can't keep subclasses in vectors. - // That's the reason for the 'false' - bridge_items: create_impl_items(&cpp_id, false, self.config), + // For now we just assume we can't keep subclasses in vectors, but we can put them in + // smart pointers. + // That's the reason for the 'false' and 'true' + bridge_items: create_impl_items(&cpp_id, false, true, self.config), bindgen_mod_items, materializations: vec![Use::Custom(Box::new(parse_quote! { pub use cxxbridge::#cpp_id; @@ -725,7 +733,7 @@ ) -> RsCodegenResult { let params = details.params; let ret = details.ret; - let unsafe_token = get_unsafe_token(details.requires_unsafe); + let unsafe_token = details.requires_unsafe.wrapper_token(); let global_def = quote! { #unsafe_token fn #api_name(#params) #ret }; let params = unqualify_params(params); let ret = unqualify_ret_type(ret); @@ -789,6 +797,7 @@ id: Ident, type_kind: TypeKind, movable: bool, + destroyable: bool, item_creator: F, associated_methods: &HashMap<QualifiedName, Vec<SuperclassMethod>>, layout: Option<Layout>, @@ -835,7 +844,7 @@ bindgen_mod_items.push(item); RsCodegenResult { global_items: self.generate_extern_type_impl(type_kind, name), - bridge_items: create_impl_items(&id, movable, self.config), + bridge_items: create_impl_items(&id, movable, destroyable, self.config), extern_c_mod_items: vec![self.generate_cxxbridge_type(name, true, None)], bindgen_mod_items, materializations, @@ -880,7 +889,7 @@ ReceiverMutability::Mutable => parse_quote!(&mut self), }; let ret_type = &method.ret_type; - let unsafe_token = get_unsafe_token(method.requires_unsafe); + let unsafe_token = method.requires_unsafe.wrapper_token(); if method.is_pure_virtual { ( None, @@ -935,67 +944,43 @@ /// generated. fn generate_error_entry(err: ConvertError, ctx: ErrorContext) -> RsCodegenResult { let err = format!("autocxx bindings couldn't be generated: {}", err); - let (impl_entry, materialization) = match ctx { - ErrorContext::Item(id) => { - let id = Self::sanitize_error_ident(&id).unwrap_or(id); - ( - None, - Some(Use::Custom(Box::new(parse_quote! { + let (impl_entry, bindgen_mod_item, materialization) = match ctx.into_type() { + ErrorContextType::Item(id) => ( + // Populate within bindgen mod because impl blocks may attach. + None, + Some(parse_quote! { + #[doc = #err] + pub struct #id; + }), + Some(Use::SpecificNameFromBindgen(id)), + ), + ErrorContextType::SanitizedItem(id) => ( + // Guaranteed to be no impl blocks - populate directly in output mod. + None, + None, + Some(Use::Custom(Box::new(parse_quote! { + #[doc = #err] + pub struct #id; + }))), + ), + ErrorContextType::Method { self_ty, method } => ( + Some(Box::new(ImplBlockDetails { + item: parse_quote! { #[doc = #err] - pub struct #id; - }))), - ) - } - ErrorContext::Method { self_ty, method } - if Self::sanitize_error_ident(&self_ty).is_none() => - { - // This could go wrong if a separate error has caused - // us to drop an entire type as well as one of its individual items. - // Then we'll be applying an impl to a thing which doesn't exist. TODO. - let method = Self::sanitize_error_ident(&method).unwrap_or(method); - ( - Some(Box::new(ImplBlockDetails { - item: parse_quote! { - #[doc = #err] - fn #method(_uhoh: autocxx::BindingGenerationFailure) { - } - }, - ty: self_ty, - })), - None, - ) - } - ErrorContext::Method { self_ty, method } => { - // If the type can't be represented (e.g. u8) this would get fiddly. - let id = make_ident(format!("{}_method_{}", self_ty, method)); - ( - None, - Some(Use::Custom(Box::new(parse_quote! { - #[doc = #err] - pub struct #id; - }))), - ) - } + fn #method(_uhoh: autocxx::BindingGenerationFailure) { + } + }, + ty: self_ty, + })), + None, + None, + ), }; RsCodegenResult { - global_items: Vec::new(), impl_entry, - bridge_items: Vec::new(), - extern_c_mod_items: Vec::new(), - bindgen_mod_items: Vec::new(), + bindgen_mod_items: bindgen_mod_item.into_iter().collect(), materializations: materialization.into_iter().collect(), - extern_rust_mod_items: Vec::new(), - } - } - - /// Because errors may be generated for invalid types or identifiers, - /// we may need to scrub the name - fn sanitize_error_ident(id: &Ident) -> Option<Ident> { - let qn = QualifiedName::new(&Namespace::new(), id.clone()); - if known_types().conflicts_with_built_in_type(&qn) { - Some(make_ident(format!("{}_autocxx_error", qn.get_final_item()))) - } else { - None + ..Default::default() } } @@ -1101,29 +1086,19 @@ } } -fn get_unsafe_token(requires_unsafe: bool) -> TokenStream { - if requires_unsafe { - quote! { unsafe } - } else { - quote! {} - } -} - -fn find_trivially_constructed_subclasses(apis: &[Api<FnPhase>]) -> HashSet<QualifiedName> { +fn find_trivially_constructed_subclasses(apis: &ApiVec<FnPhase>) -> HashSet<QualifiedName> { let (simple_constructors, complex_constructors): (Vec<_>, Vec<_>) = apis .iter() .filter_map(|api| match api { - Api::Function { fun, .. } => match &fun.synthesis { - Some(Synthesis::SubclassConstructor { - subclass, - is_trivial, - .. - }) => Some((&subclass.0.name, is_trivial)), + Api::Function { fun, .. } => match &fun.provenance { + Provenance::SynthesizedSubclassConstructor(details) => { + Some((&details.subclass.0.name, details.is_trivial)) + } _ => None, }, _ => None, }) - .partition(|(_, trivial)| **trivial); + .partition(|(_, trivial)| *trivial); let simple_constructors: HashSet<_> = simple_constructors.into_iter().map(|(qn, _)| qn).collect(); let complex_constructors: HashSet<_> = @@ -1134,6 +1109,27 @@ .collect() } +fn find_non_pod_types(apis: &ApiVec<FnPhase>) -> HashSet<QualifiedName> { + apis.iter() + .filter_map(|api| match api { + Api::Struct { + name, + analysis: + PodAndDepAnalysis { + pod: + PodAnalysis { + kind: TypeKind::NonPod, + .. + }, + .. + }, + .. + } => Some(name.name.clone()), + _ => None, + }) + .collect() +} + impl HasNs for (QualifiedName, RsCodegenResult) { fn get_namespace(&self) -> &Namespace { self.0.get_namespace() @@ -1156,5 +1152,6 @@ global_items: Vec<Item>, bindgen_mod_items: Vec<Item>, impl_entry: Option<Box<ImplBlockDetails>>, + trait_impl_entry: Option<Box<TraitImplBlockDetails>>, materializations: Vec<Use>, }
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/namespace_organizer.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/namespace_organizer.rs similarity index 87% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/namespace_organizer.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/namespace_organizer.rs index f88a040f..886cb1c 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/namespace_organizer.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/namespace_organizer.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use crate::types::Namespace; use std::collections::BTreeMap;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/non_pod_struct.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/non_pod_struct.rs similarity index 88% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/non_pod_struct.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/non_pod_struct.rs index d1415ee..e4bec2f 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/non_pod_struct.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/non_pod_struct.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use crate::conversion::api::Layout; use crate::types::make_ident;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/unqualify.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/unqualify.rs similarity index 82% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/unqualify.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/unqualify.rs index b6b91667..6f245c2 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/unqualify.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/unqualify.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use syn::{ parse_quote, punctuated::Punctuated, FnArg, GenericArgument, PathArguments, PathSegment,
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/conversion_tests.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/conversion_tests.rs similarity index 66% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/conversion_tests.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/conversion_tests.rs index 35702f4..b0474d6 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/conversion_tests.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/conversion_tests.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use autocxx_parser::UnsafePolicy; #[allow(unused_imports)]
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/convert_error.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/convert_error.rs similarity index 68% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/convert_error.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/convert_error.rs index 12e99bb..f55303f2 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/convert_error.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/convert_error.rs
@@ -1,22 +1,21 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use std::collections::HashSet; use std::fmt::Display; +use itertools::Itertools; use syn::Ident; -use crate::types::{Namespace, QualifiedName}; +use crate::{ + known_types, + types::{make_ident, Namespace, QualifiedName}, +}; #[derive(Debug, Clone)] pub enum ConvertError { @@ -44,7 +43,7 @@ UnusedTemplateParam, TooManyUnderscores, UnknownDependentType(QualifiedName), - IgnoredDependent, + IgnoredDependent(HashSet<QualifiedName>), ReservedName(String), DuplicateCxxBridgeName, UnsupportedReceiver, @@ -58,6 +57,10 @@ AssignmentOperator, Deleted, RValueReferenceField, + MethodOfNonAllowlistedType, + MethodOfGenericType, + DuplicateItemsFoundInParsing, + ConstructorWithOnlyOneParam, } fn format_maybe_identifier(id: &Option<Ident>) -> String { @@ -94,7 +97,7 @@ ConvertError::UnusedTemplateParam => write!(f, "This function or method uses a type where one of the template parameters was incomprehensible to bindgen/autocxx - probably because it uses template specialization.")?, ConvertError::TooManyUnderscores => write!(f, "Names containing __ are reserved by C++ so not acceptable to cxx")?, ConvertError::UnknownDependentType(qn) => write!(f, "This item relies on a type not known to autocxx ({})", qn.to_cpp_name())?, - ConvertError::IgnoredDependent => write!(f, "This item depends on some other type which autocxx could not generate.")?, + ConvertError::IgnoredDependent(qns) => write!(f, "This item depends on some other type(s) which autocxx could not generate, some of them are: {}", qns.iter().join(", "))?, ConvertError::ReservedName(id) => write!(f, "The item name '{}' is a reserved word in Rust.", id)?, ConvertError::DuplicateCxxBridgeName => write!(f, "This item name is used in multiple namespaces. At present, autocxx and cxx allow only one type of a given name. This limitation will be fixed in future.")?, ConvertError::UnsupportedReceiver => write!(f, "This is a method on a type which can't be used as the receiver in Rust (i.e. self/this). This is probably because some type involves template specialization.")?, @@ -108,32 +111,86 @@ ConvertError::AssignmentOperator => write!(f, "autocxx does not know how to generate bindings to operator=")?, ConvertError::Deleted => write!(f, "This function was marked =delete")?, ConvertError::RValueReferenceField => write!(f, "This structure has an rvalue reference field (&&) which is not yet supported.")?, + ConvertError::MethodOfNonAllowlistedType => write!(f, "This type was not on the allowlist, so we are not generating methods for it.")?, + ConvertError::MethodOfGenericType => write!(f, "This type is templated, so we can't generate bindings. We will instead generate bindings for each instantiation.")?, + ConvertError::DuplicateItemsFoundInParsing => write!(f, "bindgen generated multiple different APIs (functions/types) with this name. autocxx doesn't know how to diambiguate them, so we won't generate bindings for any of them.")?, + ConvertError::ConstructorWithOnlyOneParam => write!(f, "bindgen generated a move or copy constructor with an unexpected number of parameters.")?, } Ok(()) } } +/// Ensures that error contexts are always created using the constructors in this +/// mod, therefore undergoing identifier sanitation. #[derive(Clone)] -pub(crate) enum ErrorContext { +struct PhantomSanitized; + +/// The context of an error, e.g. whether it applies to a function or a method. +/// This is used to generate suitable rustdoc in the output codegen so that +/// the errors can be revealed in rust-analyzer-based IDEs, etc. +#[derive(Clone)] +pub(crate) struct ErrorContext(ErrorContextType, PhantomSanitized); + +/// All idents in this structure are guaranteed to be something we can safely codegen for. +#[derive(Clone)] +pub(crate) enum ErrorContextType { Item(Ident), + SanitizedItem(Ident), Method { self_ty: Ident, method: Ident }, } impl ErrorContext { - /// Return the ID in the output mod with which this should be associated - pub(crate) fn get_id(&self) -> &Ident { - match self { - ErrorContext::Item(id) => id, - ErrorContext::Method { self_ty, method: _ } => self_ty, + pub(crate) fn new_for_item(id: Ident) -> Self { + match Self::sanitize_error_ident(&id) { + None => Self(ErrorContextType::Item(id), PhantomSanitized), + Some(sanitized) => Self(ErrorContextType::SanitizedItem(sanitized), PhantomSanitized), } } + + pub(crate) fn new_for_method(self_ty: Ident, method: Ident) -> Self { + // If this IgnoredItem relates to a method on a self_ty which we can't represent, + // e.g. u8, then forget about trying to attach this error text to something within + // an impl block. + match Self::sanitize_error_ident(&self_ty) { + None => Self( + ErrorContextType::Method { + self_ty, + method: Self::sanitize_error_ident(&method).unwrap_or(method), + }, + PhantomSanitized, + ), + Some(_) => Self( + ErrorContextType::SanitizedItem(make_ident(format!("{}_{}", self_ty, method))), + PhantomSanitized, + ), + } + } + + /// Because errors may be generated for invalid types or identifiers, + /// we may need to scrub the name + fn sanitize_error_ident(id: &Ident) -> Option<Ident> { + let qn = QualifiedName::new(&Namespace::new(), id.clone()); + if known_types().conflicts_with_built_in_type(&qn) { + Some(make_ident(format!("{}_autocxx_error", qn.get_final_item()))) + } else { + None + } + } + + pub(crate) fn get_type(&self) -> &ErrorContextType { + &self.0 + } + + pub(crate) fn into_type(self) -> ErrorContextType { + self.0 + } } impl std::fmt::Display for ErrorContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ErrorContext::Item(id) => write!(f, "{}", id), - ErrorContext::Method { self_ty, method } => write!(f, "{}::{}", self_ty, method), + match &self.0 { + ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => write!(f, "{}", id), + ErrorContextType::Method { self_ty, method } => write!(f, "{}::{}", self_ty, method), } } }
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/doc_attr.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/doc_attr.rs new file mode 100644 index 0000000..685207e7 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/doc_attr.rs
@@ -0,0 +1,17 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syn::Attribute; + +/// Returns the attribute (if any) which contains a doc comment. +pub(super) fn get_doc_attr(attrs: &[Attribute]) -> Option<Attribute> { + attrs + .iter() + .find(|a| a.path.get_ident().iter().any(|p| *p == "doc")) + .cloned() +}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/error_reporter.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/error_reporter.rs similarity index 74% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/error_reporter.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/error_reporter.rs index 2dbd558..c68f13da 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/error_reporter.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/error_reporter.rs
@@ -1,32 +1,30 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use syn::ItemEnum; use super::{ api::{AnalysisPhase, Api, ApiName, FuncToConvert, StructDetails, TypedefKind}, + apivec::ApiVec, convert_error::{ConvertErrorWithContext, ErrorContext}, ConvertError, }; -use crate::types::{Namespace, QualifiedName}; +use crate::{ + conversion::convert_error::ErrorContextType, + types::{Namespace, QualifiedName}, +}; /// Run some code which may generate a ConvertError. /// If it does, try to note the problem in our output APIs /// such that users will see documentation of the error. pub(crate) fn report_any_error<F, T>( ns: &Namespace, - apis: &mut Vec<Api<impl AnalysisPhase>>, + apis: &mut ApiVec<impl AnalysisPhase>, fun: F, ) -> Option<T> where @@ -40,7 +38,16 @@ } Err(ConvertErrorWithContext(err, Some(ctx))) => { eprintln!("Ignored item {}: {}", ctx, err); - apis.push(ignored_item(ns, ctx, err)); + let id = match ctx.get_type() { + ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => id, + ErrorContextType::Method { self_ty, .. } => self_ty, + }; + let name = ApiName::new_from_qualified_name(QualifiedName::new(ns, id.clone())); + apis.push(Api::IgnoredItem { + name, + err, + ctx: Some(ctx), + }); None } } @@ -50,8 +57,8 @@ /// anything goes wrong, instead add a note of the problem in our /// output API such that users will see documentation for the problem. pub(crate) fn convert_apis<FF, SF, EF, TF, A, B: 'static>( - in_apis: Vec<Api<A>>, - out_apis: &mut Vec<Api<B>>, + in_apis: ApiVec<A>, + out_apis: &mut ApiVec<B>, mut func_conversion: FF, mut struct_conversion: SF, mut enum_conversion: EF, @@ -63,7 +70,6 @@ ApiName, Box<FuncToConvert>, A::FunAnalysis, - Option<QualifiedName>, ) -> Result<Box<dyn Iterator<Item = Api<B>>>, ConvertErrorWithContext>, SF: FnMut( ApiName, @@ -81,8 +87,8 @@ A::TypedefAnalysis, ) -> Result<Box<dyn Iterator<Item = Api<B>>>, ConvertErrorWithContext>, { - out_apis.extend(&mut in_apis.into_iter().flat_map(|api| { - let tn = api.name().clone(); + out_apis.extend(in_apis.into_iter().flat_map(|api| { + let fullname = api.name_info().clone(); let result: Result<Box<dyn Iterator<Item = Api<B>>>, ConvertErrorWithContext> = match api { // No changes to any of these... Api::ConcreteType { @@ -132,6 +138,12 @@ ctx, }))) } + Api::SubclassTraitItem { name, details } => { + Ok(Box::new(std::iter::once(Api::SubclassTraitItem { + name, + details, + }))) + } // Apply a mapping to the following Api::Enum { name, item } => enum_conversion(name, item), Api::Typedef { @@ -144,35 +156,26 @@ name, fun, analysis, - name_for_gc, - } => func_conversion(name, fun, analysis, name_for_gc), + } => func_conversion(name, fun, analysis), Api::Struct { name, details, analysis, } => struct_conversion(name, details, analysis), }; - api_or_error(tn, result) + api_or_error(fullname, result) })) } fn api_or_error<T: AnalysisPhase + 'static>( - name: QualifiedName, + name: ApiName, api_or_error: Result<Box<dyn Iterator<Item = Api<T>>>, ConvertErrorWithContext>, ) -> Box<dyn Iterator<Item = Api<T>>> { match api_or_error { Ok(opt) => opt, - Err(ConvertErrorWithContext(err, None)) => { - eprintln!("Ignored {}: {}", name, err); - Box::new(std::iter::empty()) - } - Err(ConvertErrorWithContext(err, Some(ctx))) => { - eprintln!("Ignored {}: {}", name, err); - Box::new(std::iter::once(ignored_item( - name.get_namespace(), - ctx, - err, - ))) + Err(ConvertErrorWithContext(err, ctx)) => { + eprintln!("Ignored {}: {}", name.cpp_name(), err); + Box::new(std::iter::once(Api::IgnoredItem { name, err, ctx })) } } } @@ -182,8 +185,8 @@ /// anything goes wrong, instead add a note of the problem in our /// output API such that users will see documentation for the problem. pub(crate) fn convert_item_apis<F, A, B: 'static>( - in_apis: Vec<Api<A>>, - out_apis: &mut Vec<Api<B>>, + in_apis: ApiVec<A>, + out_apis: &mut ApiVec<B>, mut fun: F, ) where F: FnMut(Api<A>) -> Result<Box<dyn Iterator<Item = Api<B>>>, ConvertError>, @@ -191,18 +194,11 @@ B: AnalysisPhase, { out_apis.extend(in_apis.into_iter().flat_map(|api| { + let fullname = api.name_info().clone(); let tn = api.name().clone(); let result = fun(api).map_err(|e| { - ConvertErrorWithContext(e, Some(ErrorContext::Item(tn.get_final_ident()))) + ConvertErrorWithContext(e, Some(ErrorContext::new_for_item(tn.get_final_ident()))) }); - api_or_error(tn, result) + api_or_error(fullname, result) })) } - -fn ignored_item<A: AnalysisPhase>(ns: &Namespace, ctx: ErrorContext, err: ConvertError) -> Api<A> { - Api::IgnoredItem { - name: ApiName::new(ns, ctx.get_id().clone()), - err, - ctx, - } -}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/mod.rs similarity index 81% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/mod.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/mod.rs index 8745fdf..124b464a 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/mod.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/mod.rs
@@ -1,19 +1,14 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. mod analysis; mod api; +mod apivec; mod codegen_cpp; mod codegen_rs; #[cfg(test)] @@ -31,20 +26,25 @@ use itertools::Itertools; use syn::{Item, ItemMod}; -use crate::{CppCodegenOptions, CppFilePair, UnsafePolicy}; +use crate::{ + conversion::analysis::deps::HasDependencies, CppCodegenOptions, CppFilePair, UnsafePolicy, +}; use self::{ analysis::{ abstract_types::{discard_ignored_functions, mark_types_abstract}, + allocators::create_alloc_and_frees, casts::add_casts, check_names, + constructor_deps::decorate_types_with_constructor_deps, fun::FnPhase, gc::filter_apis_by_following_edges_from_allowlist, pod::analyze_pod_apis, remove_ignored::filter_apis_by_ignored_dependents, tdef::convert_typedef_targets, }, - api::{AnalysisPhase, Api}, + api::AnalysisPhase, + apivec::ApiVec, codegen_rs::RsCodeGenerator, parse::ParseBindgen, }; @@ -82,23 +82,27 @@ } } - fn dump_apis<T: AnalysisPhase>(label: &str, apis: &[Api<T>]) { + fn dump_apis<T: AnalysisPhase>(label: &str, apis: &ApiVec<T>) { if LOG_APIS { log::info!( "APIs after {}:\n{}", label, - apis.iter().map(|api| { format!(" {:?}", api) }).join("\n") + apis.iter() + .map(|api| { format!(" {:?}", api) }) + .sorted() + .join("\n") ) } } - fn dump_apis_with_deps(label: &str, apis: &[Api<FnPhase>]) { + fn dump_apis_with_deps(label: &str, apis: &ApiVec<FnPhase>) { if LOG_APIS { log::info!( "APIs after {}:\n{}", label, apis.iter() .map(|api| { format!(" {:?}, deps={}", api, api.format_deps()) }) + .sorted() .join("\n") ) } @@ -142,6 +146,7 @@ let analyzed_apis = analyze_pod_apis(apis, self.config)?; Self::dump_apis("pod analysis", &analyzed_apis); let analyzed_apis = add_casts(analyzed_apis); + let analyzed_apis = create_alloc_and_frees(analyzed_apis); // Next, figure out how we materialize different functions. // Some will be simple entries in the cxx::bridge module; others will // require C++ wrapper functions. This is probably the most complex @@ -153,9 +158,13 @@ // If any of those functions turned out to be pure virtual, don't attempt // to generate UniquePtr implementations for the type, since it can't // be instantiated. - Self::dump_apis_with_deps("analyze fns", &analyzed_apis); - let analyzed_apis = mark_types_abstract(self.config, analyzed_apis); - Self::dump_apis_with_deps("marking abstract", &analyzed_apis); + Self::dump_apis("analyze fns", &analyzed_apis); + let analyzed_apis = mark_types_abstract(analyzed_apis); + Self::dump_apis("marking abstract", &analyzed_apis); + // Annotate structs with a note of any copy/move constructors which + // we may want to retain to avoid garbage collecting them later. + let analyzed_apis = decorate_types_with_constructor_deps(analyzed_apis); + Self::dump_apis_with_deps("adding constructor deps", &analyzed_apis); let analyzed_apis = discard_ignored_functions(analyzed_apis); Self::dump_apis_with_deps("ignoring ignorable fns", &analyzed_apis); // Remove any APIs whose names are not compatible with cxx. @@ -186,6 +195,7 @@ self.include_list, bindgen_mod, self.config, + cpp.as_ref().map(|file_pair| file_pair.header_name.clone()), ); Ok(CodegenResults { rs, cpp }) }
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/bindgen_semantic_attributes.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/bindgen_semantic_attributes.rs similarity index 90% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/bindgen_semantic_attributes.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/bindgen_semantic_attributes.rs index d2e739f..8b789ae 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/bindgen_semantic_attributes.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/bindgen_semantic_attributes.rs
@@ -1,16 +1,10 @@ // Copyright 2022 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use proc_macro2::{Ident, TokenStream}; use syn::{ @@ -65,7 +59,7 @@ if self.has_attr("unused_template_param") { Err(ConvertErrorWithContext( ConvertError::UnusedTemplateParam, - Some(ErrorContext::Item(id_for_context.clone())), + Some(ErrorContext::new_for_item(id_for_context.clone())), )) } else { Ok(())
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/mod.rs new file mode 100644 index 0000000..3f42ce425 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/mod.rs
@@ -0,0 +1,14 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod bindgen_semantic_attributes; +mod parse_bindgen; +mod parse_foreign_mod; + +pub(crate) use bindgen_semantic_attributes::BindgenSemanticAttributes; +pub(crate) use parse_bindgen::ParseBindgen;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_bindgen.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_bindgen.rs similarity index 92% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_bindgen.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_bindgen.rs index 3692cb05..c56e4ae 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_bindgen.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_bindgen.rs
@@ -1,22 +1,17 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::collections::HashSet; use crate::{ conversion::{ - api::{Api, ApiName, StructDetails, SubclassName, TypedefKind, UnanalyzedApi}, + api::{Api, ApiName, NullPhase, StructDetails, SubclassName, TypedefKind, UnanalyzedApi}, + apivec::ApiVec, ConvertError, }, types::Namespace, @@ -41,7 +36,7 @@ /// Parses a bindgen mod in order to understand the APIs within it. pub(crate) struct ParseBindgen<'a> { config: &'a IncludeCppConfig, - apis: Vec<UnanalyzedApi>, + apis: ApiVec<NullPhase>, } fn api_name(ns: &Namespace, id: Ident, attrs: &BindgenSemanticAttributes) -> ApiName { @@ -55,7 +50,7 @@ ) -> Result<ApiName, ConvertErrorWithContext> { match validate_ident_ok_for_cxx(&id.to_string()) { Err(e) => { - let ctx = ErrorContext::Item(id); + let ctx = ErrorContext::new_for_item(id); Err(ConvertErrorWithContext(e, Some(ctx))) } Ok(..) => Ok(api_name(ns, id, attrs)), @@ -66,7 +61,7 @@ pub(crate) fn new(config: &'a IncludeCppConfig) -> Self { ParseBindgen { config, - apis: Vec::new(), + apis: ApiVec::new(), } } @@ -75,7 +70,7 @@ pub(crate) fn parse_items( mut self, items: Vec<Item>, - ) -> Result<Vec<UnanalyzedApi>, ConvertError> { + ) -> Result<ApiVec<NullPhase>, ConvertError> { let items = Self::find_items_in_root(items)?; if !self.config.exclude_utilities() { generate_utilities(&mut self.apis, self.config); @@ -137,7 +132,7 @@ // This object maintains some state specific to this namespace, i.e. // this particular mod. let mut mod_converter = ParseForeignMod::new(ns.clone()); - let mut more_apis = Vec::new(); + let mut more_apis = ApiVec::new(); for item in items { report_any_error(&ns, &mut more_apis, || { self.parse_item(item, &mut mod_converter, &ns) @@ -253,7 +248,7 @@ if new_tyname == old_tyname { return Err(ConvertErrorWithContext( ConvertError::InfinitelyRecursiveTypedef(new_tyname), - Some(ErrorContext::Item(new_id.clone())), + Some(ErrorContext::new_for_item(new_id.clone())), )); } let annotations = BindgenSemanticAttributes::new(&use_item.attrs); @@ -287,6 +282,8 @@ } Item::Type(ity) => { let annotations = BindgenSemanticAttributes::new(&ity.attrs); + // It's known that sometimes bindgen will give us duplicate typedefs with the + // same name - see test_issue_264. self.apis.push(UnanalyzedApi::Typedef { name: api_name(ns, ity.ident.clone(), &annotations), item: TypedefKind::Type(ity),
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_foreign_mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_foreign_mod.rs similarity index 86% rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_foreign_mod.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_foreign_mod.rs index e2df889f..3f7855f 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_foreign_mod.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_foreign_mod.rs
@@ -1,18 +1,13 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. -use crate::conversion::api::ApiName; +use crate::conversion::api::{ApiName, NullPhase, Provenance}; +use crate::conversion::apivec::ApiVec; use crate::conversion::doc_attr::get_doc_attr; use crate::conversion::error_reporter::report_any_error; use crate::conversion::{ @@ -44,7 +39,7 @@ // may actually be methods (static or otherwise). Mapping from // function name to type name. method_receivers: HashMap<Ident, QualifiedName>, - ignored_apis: Vec<UnanalyzedApi>, + ignored_apis: ApiVec<NullPhase>, } impl ParseForeignMod { @@ -53,14 +48,14 @@ ns, funcs_to_convert: Vec::new(), method_receivers: HashMap::new(), - ignored_apis: Vec::new(), + ignored_apis: ApiVec::new(), } } /// Record information from foreign mod items encountered /// in bindgen output. pub(crate) fn convert_foreign_mod_items(&mut self, foreign_mod_items: Vec<ForeignItem>) { - let mut extra_apis = Vec::new(); + let mut extra_apis = ApiVec::new(); for i in foreign_mod_items { report_any_error(&self.ns.clone(), &mut extra_apis, || { self.parse_foreign_item(i) @@ -75,6 +70,7 @@ let annotations = BindgenSemanticAttributes::new(&item.attrs); let doc_attr = get_doc_attr(&item.attrs); self.funcs_to_convert.push(FuncToConvert { + provenance: Provenance::Bindgen, self_ty: None, ident: item.sig.ident, doc_attr, @@ -89,14 +85,15 @@ references: annotations.get_reference_parameters_and_return(), original_name: annotations.get_original_name(), synthesized_this_type: None, - synthesis: None, + add_to_trait: None, is_deleted: annotations.has_attr("deleted"), + synthetic_cpp: None, }); Ok(()) } ForeignItem::Static(item) => Err(ConvertErrorWithContext( ConvertError::StaticData(item.ident.to_string()), - Some(ErrorContext::Item(item.ident)), + Some(ErrorContext::new_for_item(item.ident)), )), _ => Err(ConvertErrorWithContext( ConvertError::UnexpectedForeignItem, @@ -129,7 +126,7 @@ /// Indicate that all foreign mods and all impl blocks have been /// fed into us, and we should process that information to generate /// the resulting APIs. - pub(crate) fn finished(mut self, apis: &mut Vec<UnanalyzedApi>) { + pub(crate) fn finished(mut self, apis: &mut ApiVec<NullPhase>) { apis.append(&mut self.ignored_apis); while !self.funcs_to_convert.is_empty() { let mut fun = self.funcs_to_convert.remove(0); @@ -142,7 +139,6 @@ ), fun: Box::new(fun), analysis: (), - name_for_gc: None, }) } }
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/utilities.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/utilities.rs new file mode 100644 index 0000000..b90b3659 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/utilities.rs
@@ -0,0 +1,30 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use autocxx_parser::IncludeCppConfig; + +use super::{ + api::{ApiName, NullPhase, UnanalyzedApi}, + apivec::ApiVec, +}; +use crate::types::{make_ident, Namespace}; + +/// Adds items which we always add, cos they're useful. +/// Any APIs or techniques which do not involve actual C++ interop +/// shouldn't go here, but instead should go into the main autocxx +/// src/lib.rs. +pub(crate) fn generate_utilities(apis: &mut ApiVec<NullPhase>, config: &IncludeCppConfig) { + // Unless we've been specifically asked not to do so, we always + // generate a 'make_string' function. That pretty much *always* means + // we run two passes through bindgen. i.e. the next 'if' is always true, + // and we always generate an additional C++ file for our bindings additions, + // unless the include_cpp macro has specified ExcludeUtilities. + apis.push(UnanalyzedApi::StringConstructor { + name: ApiName::new(&Namespace::new(), make_ident(config.get_makestring_name())), + }); +}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/cxxbridge.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/cxxbridge.rs similarity index 65% rename from third_party/rust/autocxx_engine/v0_16/crate/src/cxxbridge.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/cxxbridge.rs index f979f6d..55aba29 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/cxxbridge.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/cxxbridge.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt};
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/known_types.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/known_types.rs similarity index 83% rename from third_party/rust/autocxx_engine/v0_16/crate/src/known_types.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/known_types.rs index b0e866d..f6a3a16 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/known_types.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/known_types.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use crate::{ conversion::ConvertError, @@ -43,10 +37,12 @@ rs_name: String, /// C++ equivalent name for a Rust type. cpp_name: String, - //// The behavior of the type. + /// The behavior of the type. behavior: Behavior, /// Any extra non-canonical names extra_non_canonical_name: Option<String>, + has_const_copy_constructor: bool, + has_move_constructor: bool, } impl TypeDetails { @@ -55,12 +51,16 @@ cpp_name: impl Into<String>, behavior: Behavior, extra_non_canonical_name: Option<String>, + has_const_copy_constructor: bool, + has_move_constructor: bool, ) -> Self { TypeDetails { rs_name: rs_name.into(), cpp_name: cpp_name.into(), behavior, extra_non_canonical_name, + has_const_copy_constructor, + has_move_constructor, } } @@ -153,6 +153,11 @@ Rust, } +pub struct KnownTypeConstructorDetails { + pub has_move_constructor: bool, + pub has_const_copy_constructor: bool, +} + impl TypeDatabase { fn get(&self, ty: &QualifiedName) -> Option<&TypeDetails> { // The following line is important. It says that @@ -180,13 +185,16 @@ ) } + /// Returns all known types. + pub(crate) fn all_names(&self) -> impl Iterator<Item = &QualifiedName> { + self.canonical_names.keys().chain(self.by_rs_name.keys()) + } + /// Types which are known to be safe (or unsafe) to hold and pass by /// value in Rust. pub(crate) fn get_pod_safe_types(&self) -> impl Iterator<Item = (QualifiedName, bool)> { let pod_safety = self - .canonical_names - .keys() - .chain(self.by_rs_name.keys()) + .all_names() .map(|tn| { ( tn.clone(), @@ -208,6 +216,16 @@ pod_safety.into_iter() } + pub(crate) fn get_constructor_details( + &self, + qn: &QualifiedName, + ) -> Option<KnownTypeConstructorDetails> { + self.get(qn).map(|x| KnownTypeConstructorDetails { + has_move_constructor: x.has_move_constructor, + has_const_copy_constructor: x.has_const_copy_constructor, + }) + } + /// Whether this TypePath should be treated as a value in C++ /// but a reference in Rust. This only applies to rust::Str /// (C++ name) which is &str in Rust. @@ -217,6 +235,18 @@ .unwrap_or(false) } + /// Whether this can only be passed around using `std::move` + pub(crate) fn lacks_copy_constructor(&self, tn: &QualifiedName) -> bool { + self.get(tn) + .map(|td| { + matches!( + td.behavior, + Behavior::CxxContainerByValueSafe | Behavior::CxxContainerNotByValueSafe + ) + }) + .unwrap_or(false) + } + /// Here we substitute any names which we know are Special from /// our type database, e.g. std::unique_ptr -> UniquePtr. /// We strip off and ignore @@ -306,60 +336,80 @@ "std::unique_ptr", Behavior::CxxContainerByValueSafe, None, + false, + true, )); db.insert(TypeDetails::new( "cxx::CxxVector", "std::vector", Behavior::CxxContainerNotByValueSafe, None, + false, + true, )); db.insert(TypeDetails::new( "cxx::SharedPtr", "std::shared_ptr", Behavior::CxxContainerByValueSafe, None, + true, + true, )); db.insert(TypeDetails::new( "cxx::WeakPtr", "std::weak_ptr", Behavior::CxxContainerByValueSafe, None, + true, + true, )); db.insert(TypeDetails::new( "cxx::CxxString", "std::string", Behavior::CxxString, None, + true, + true, )); db.insert(TypeDetails::new( "str", "rust::Str", Behavior::RustStr, None, + true, + false, )); db.insert(TypeDetails::new( "String", "rust::String", Behavior::RustString, None, + true, + true, )); db.insert(TypeDetails::new( "std::boxed::Box", "rust::Box", Behavior::RustContainerByValueSafe, None, + false, + true, )); db.insert(TypeDetails::new( "i8", "int8_t", Behavior::CByValue, Some("std::os::raw::c_schar".into()), + true, + true, )); db.insert(TypeDetails::new( "u8", "uint8_t", Behavior::CByValue, Some("std::os::raw::c_uchar".into()), + true, + true, )); for (cpp_type, rust_type) in (4..7).map(|x| 2i32.pow(x)).flat_map(|x| { vec![ @@ -372,15 +422,26 @@ cpp_type, Behavior::CByValue, None, + true, + true, )); } - db.insert(TypeDetails::new("bool", "bool", Behavior::CByValue, None)); + db.insert(TypeDetails::new( + "bool", + "bool", + Behavior::CByValue, + None, + true, + true, + )); db.insert(TypeDetails::new( "std::pin::Pin", "Pin", Behavior::RustByValue, // because this is actually Pin<&something> None, + true, + false, )); let mut insert_ctype = |cname: &str| { @@ -390,12 +451,16 @@ cname, Behavior::CVariableLengthByValue, Some(format!("std::os::raw::c_{}", concatenated_name)), + true, + true, )); db.insert(TypeDetails::new( format!("autocxx::c_u{}", concatenated_name), format!("unsigned {}", cname), Behavior::CVariableLengthByValue, Some(format!("std::os::raw::c_u{}", concatenated_name)), + true, + true, )); }; @@ -404,37 +469,41 @@ insert_ctype("short"); insert_ctype("long long"); - db.insert(TypeDetails::new("f32", "float", Behavior::CByValue, None)); - db.insert(TypeDetails::new("f64", "double", Behavior::CByValue, None)); + db.insert(TypeDetails::new( + "f32", + "float", + Behavior::CByValue, + None, + true, + true, + )); + db.insert(TypeDetails::new( + "f64", + "double", + Behavior::CByValue, + None, + true, + true, + )); db.insert(TypeDetails::new( "::std::os::raw::c_char", "char", Behavior::CByValue, None, + true, + true, )); db.insert(TypeDetails::new( "autocxx::c_void", "void", Behavior::CVoid, Some("std::os::raw::c_void".into()), + false, + false, )); db } -/// If a given type lacks a copy constructor, we should always use -/// std::move in wrapper functions. -pub(crate) fn type_lacks_copy_constructor(ty: &Type) -> bool { - // In future we may wish to look this up in KNOWN_TYPES. - match ty { - Type::Path(typ) => { - let tn = QualifiedName::from_type_path(typ); - tn.to_cpp_name().starts_with("std::unique_ptr") - || tn.to_cpp_name().starts_with("rust::Box") - } - _ => false, - } -} - pub(crate) fn ensure_pointee_is_valid(ptr: &TypePtr) -> Result<(), ConvertError> { match *ptr.elem { Type::Path(..) => Ok(()),
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/lib.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/lib.rs similarity index 93% rename from third_party/rust/autocxx_engine/v0_16/crate/src/lib.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/lib.rs index 669789e1..395ebf76 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/lib.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/lib.rs
@@ -4,21 +4,16 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. // This feature=nightly could be set by build.rs, but since we only care // about it for docs, we ask docs.rs to set it in the Cargo.toml. #![cfg_attr(feature = "nightly", feature(doc_cfg))] +#![forbid(unsafe_code)] mod ast_discoverer; mod conversion; @@ -69,13 +64,6 @@ pub use cxx_gen::HEADER; -/// Re-export cxx such that clients can use the same version as -/// us. This doesn't enable clients to avoid depending on the cxx -/// crate too, unfortunately, since generated cxx::bridge code -/// refers explicitly to ::cxx. See -/// <https://github.com/google/autocxx/issues/36> -pub use cxx; - #[derive(Clone)] /// Some C++ content which should be written to disk and built. pub struct CppFilePair { @@ -570,7 +558,9 @@ State::NotGenerated => panic!("Call generate() first"), State::Generated(gen_results) => { let rs = gen_results.item_mod.to_token_stream(); - files.push(do_cxx_cpp_generation(rs, cpp_codegen_options)?); + if !cpp_codegen_options.skip_cxx_gen { + files.push(do_cxx_cpp_generation(rs, cpp_codegen_options)?); + } if let Some(cpp_file_pair) = &gen_results.cpp { files.push(cpp_file_pair.clone()); } @@ -628,18 +618,43 @@ .unwrap_or_else(|_| "clang++".to_string()) } +/// Newtype wrapper so we can give it a [`Default`]. +pub struct HeaderNamer<'a>(pub Box<dyn 'a + Fn(String) -> String>); + +impl Default for HeaderNamer<'static> { + fn default() -> Self { + Self(Box::new(|mod_name| format!("autocxxgen_{}.h", mod_name))) + } +} + +impl HeaderNamer<'_> { + fn name_header(&self, mod_name: String) -> String { + self.0(mod_name) + } +} + /// Options for C++ codegen #[derive(Default)] -pub struct CppCodegenOptions { +pub struct CppCodegenOptions<'a> { /// Whether to avoid generating `#include <some-system-header>`. /// You may wish to do this to make a hermetic test case with no /// external dependencies. pub suppress_system_headers: bool, - /// Optionally, a prefix to go at `#include "<here>cxx.h" + /// Optionally, a prefix to go at `#include "<here>cxx.h". This is a header file from the `cxx` + /// crate. pub path_to_cxx_h: Option<String>, - /// Optionally, a prefix to go at `#include "<here>cxxgen.h" + /// Optionally, a prefix to go at `#include "<here>cxxgen.h". This is a header file which we + /// generate. pub path_to_cxxgen_h: Option<String>, + /// Optionally, a function called to generate each of the per-section header files. The default + /// names are subject to change. + /// The function is passed the name of the module generated by each `include_cpp`, + /// configured via `name`. These will be unique. + pub header_namer: HeaderNamer<'a>, /// An annotation optionally to include on each C++ function. /// For example to export the symbol from a library. pub cxx_impl_annotations: Option<String>, + /// Whether to skip using [`cxx_gen`] to generate the C++ code, + /// so that some other process can handle that. + pub skip_cxx_gen: bool, }
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/parse_callbacks.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/parse_callbacks.rs new file mode 100644 index 0000000..61098c0 --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/parse_callbacks.rs
@@ -0,0 +1,23 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::panic::UnwindSafe; + +use crate::RebuildDependencyRecorder; +use autocxx_bindgen::callbacks::ParseCallbacks; + +#[derive(Debug)] +pub(crate) struct AutocxxParseCallbacks(pub(crate) Box<dyn RebuildDependencyRecorder>); + +impl UnwindSafe for AutocxxParseCallbacks {} + +impl ParseCallbacks for AutocxxParseCallbacks { + fn include_file(&self, filename: &str) { + self.0.record_header_file_dependency(filename); + } +}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/parse_file.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/parse_file.rs new file mode 100644 index 0000000..3b234ca --- /dev/null +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/parse_file.rs
@@ -0,0 +1,425 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::ast_discoverer::Discoveries; +use crate::CppCodegenOptions; +use crate::{ + cxxbridge::CxxBridge, Error as EngineError, GeneratedCpp, IncludeCppEngine, + RebuildDependencyRecorder, +}; +use autocxx_parser::directives::SUBCLASS; +use autocxx_parser::{AllowlistEntry, RustPath, Subclass, SubclassAttrs}; +use proc_macro2::{Span, TokenStream}; +use quote::ToTokens; +use std::{collections::HashSet, fmt::Display, io::Read, path::PathBuf}; +use std::{panic::UnwindSafe, path::Path, rc::Rc}; +use syn::{token::Brace, Item, ItemMod}; + +/// Errors which may occur when parsing a Rust source file to discover +/// and interpret include_cxx macros. +#[derive(Debug)] +pub enum ParseError { + /// Unable to open the source file + FileOpen(std::io::Error), + /// The .rs file couldn't be read. + FileRead(std::io::Error), + /// The .rs file couldn't be parsed. + Syntax(syn::Error), + /// The include CPP macro could not be expanded into + /// Rust bindings to C++, because of some problem during the conversion + /// process. This could be anything from a C++ parsing error to some + /// C++ feature that autocxx can't yet handle and isn't able to skip + /// over. It could also cover errors in your syntax of the `include_cpp` + /// macro or the directives inside. + AutocxxCodegenError(EngineError), + /// There are two or more `include_cpp` macros with the same + /// mod name. + ConflictingModNames, + ZeroModsForDynamicDiscovery, + MultipleModsForDynamicDiscovery, + DiscoveredRustItemsWhenNotInAutoDiscover, +} + +impl Display for ParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParseError::FileOpen(err) => write!(f, "Unable to open file: {}", err)?, + ParseError::FileRead(err) => write!(f, "Unable to read file: {}", err)?, + ParseError::Syntax(err) => write!(f, "Syntax error parsing Rust file: {}", err)?, + ParseError::AutocxxCodegenError(err) => + write!(f, "Unable to parse include_cpp! macro: {}", err)?, + ParseError::ConflictingModNames => + write!(f, "There are two or more include_cpp! macros with the same output mod name. Use name!")?, + ParseError::ZeroModsForDynamicDiscovery => + write!(f, "This file contains extra information to append to an include_cpp! but no such include_cpp! was found in this file.")?, + ParseError::MultipleModsForDynamicDiscovery => + write!(f, "This file contains extra information to append to an include_cpp! but multiple such include_cpp! declarations were found in this file.")?, + ParseError::DiscoveredRustItemsWhenNotInAutoDiscover => + write!(f, "This file contains extra information to append to an \"extern Rust\" but auto-discover was switched off.")?, + } + Ok(()) + } +} + +/// Parse a Rust file, and spot any include_cpp macros within it. +pub fn parse_file<P1: AsRef<Path>>( + rs_file: P1, + auto_allowlist: bool, +) -> Result<ParsedFile, ParseError> { + let mut source = String::new(); + let mut file = std::fs::File::open(rs_file).map_err(ParseError::FileOpen)?; + file.read_to_string(&mut source) + .map_err(ParseError::FileRead)?; + proc_macro2::fallback::force(); + let source = syn::parse_file(&source).map_err(ParseError::Syntax)?; + parse_file_contents(source, auto_allowlist) +} + +fn parse_file_contents(source: syn::File, auto_allowlist: bool) -> Result<ParsedFile, ParseError> { + #[derive(Default)] + struct State { + auto_allowlist: bool, + results: Vec<Segment>, + extra_superclasses: Vec<Subclass>, + discoveries: Discoveries, + } + impl State { + fn parse_item(&mut self, item: Item, mod_path: Option<RustPath>) -> Result<(), ParseError> { + let result = match item { + Item::Macro(mac) + if mac + .mac + .path + .segments + .last() + .map(|s| s.ident == "include_cpp") + .unwrap_or(false) => + { + Segment::Autocxx( + crate::IncludeCppEngine::new_from_syn(mac.mac) + .map_err(ParseError::AutocxxCodegenError)?, + ) + } + Item::Mod(itm) + if itm + .attrs + .iter() + .any(|attr| attr.path.to_token_stream().to_string() == "cxx :: bridge") => + { + Segment::Cxx(CxxBridge::from(itm)) + } + Item::Mod(itm) => { + if let Some((brace, items)) = itm.content { + let mut mod_state = State { + auto_allowlist: self.auto_allowlist, + ..Default::default() + }; + let mod_path = match &mod_path { + None => RustPath::new_from_ident(itm.ident.clone()), + Some(mod_path) => mod_path.append(itm.ident.clone()), + }; + for item in items { + mod_state.parse_item(item, Some(mod_path.clone()))? + } + self.extra_superclasses.extend(mod_state.extra_superclasses); + self.discoveries.extend(mod_state.discoveries); + Segment::Mod( + mod_state.results, + ( + brace, + ItemMod { + content: None, + ..itm + }, + ), + ) + } else { + Segment::Other(Item::Mod(itm)) + } + } + Item::Struct(ref its) if self.auto_allowlist => { + let attrs = &its.attrs; + let is_superclass_attr = attrs.iter().find(|attr| { + attr.path + .segments + .last() + .map(|seg| seg.ident == "is_subclass" || seg.ident == SUBCLASS) + .unwrap_or(false) + }); + if let Some(is_superclass_attr) = is_superclass_attr { + if !is_superclass_attr.tokens.is_empty() { + let subclass = its.ident.clone(); + let args: SubclassAttrs = is_superclass_attr + .parse_args() + .map_err(ParseError::Syntax)?; + if let Some(superclass) = args.superclass { + self.extra_superclasses.push(Subclass { + superclass, + subclass, + }) + } + } + } + self.discoveries.search_item(&item, mod_path); + Segment::Other(item) + } + _ => { + self.discoveries.search_item(&item, mod_path); + Segment::Other(item) + } + }; + self.results.push(result); + Ok(()) + } + } + let mut state = State { + auto_allowlist, + ..Default::default() + }; + for item in source.items { + state.parse_item(item, None)? + } + let State { + auto_allowlist, + mut results, + mut extra_superclasses, + mut discoveries, + } = state; + if !auto_allowlist + && (!discoveries.extern_rust_types.is_empty() || !discoveries.extern_rust_funs.is_empty()) + { + return Err(ParseError::DiscoveredRustItemsWhenNotInAutoDiscover); + } + if !extra_superclasses.is_empty() || (auto_allowlist && !discoveries.is_empty()) { + let mut autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg { + Segment::Autocxx(engine) => Some(engine), + _ => None, + }); + let our_seg = autocxx_seg_iterator.next(); + match our_seg { + None => return Err(ParseError::ZeroModsForDynamicDiscovery), + Some(engine) => { + engine + .config_mut() + .subclasses + .append(&mut extra_superclasses); + if auto_allowlist { + for cpp in discoveries.cpp_list { + engine + .config_mut() + .allowlist + .push(AllowlistEntry::Item(cpp), Span::call_site()) + .map_err(ParseError::Syntax)?; + } + } + engine + .config_mut() + .extern_rust_funs + .append(&mut discoveries.extern_rust_funs); + engine + .config_mut() + .rust_types + .append(&mut discoveries.extern_rust_types); + } + } + if autocxx_seg_iterator.next().is_some() { + return Err(ParseError::MultipleModsForDynamicDiscovery); + } + } + let autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg { + Segment::Autocxx(engine) => Some(engine), + _ => None, + }); + for seg in autocxx_seg_iterator { + seg.config + .confirm_complete(auto_allowlist) + .map_err(ParseError::Syntax)?; + } + Ok(ParsedFile(results)) +} + +/// A Rust file parsed by autocxx. May contain zero or more autocxx 'engines', +/// i.e. the `IncludeCpp` class, corresponding to zero or more include_cpp +/// macros within this file. Also contains `syn::Item` structures for all +/// the rest of the Rust code, such that it can be reconstituted if necessary. +pub struct ParsedFile(Vec<Segment>); + +#[allow(clippy::large_enum_variant)] +enum Segment { + Autocxx(IncludeCppEngine), + Cxx(CxxBridge), + Mod(Vec<Segment>, (Brace, ItemMod)), + Other(Item), +} + +pub trait CppBuildable { + fn generate_h_and_cxx( + &self, + cpp_codegen_options: &CppCodegenOptions, + ) -> Result<GeneratedCpp, cxx_gen::Error>; +} + +impl ParsedFile { + /// Get all the autocxxes in this parsed file. + pub fn get_rs_buildables(&self) -> impl Iterator<Item = &IncludeCppEngine> { + fn do_get_rs_buildables(segments: &[Segment]) -> impl Iterator<Item = &IncludeCppEngine> { + segments + .iter() + .flat_map(|s| -> Box<dyn Iterator<Item = &IncludeCppEngine>> { + match s { + Segment::Autocxx(includecpp) => Box::new(std::iter::once(includecpp)), + Segment::Mod(segments, _) => Box::new(do_get_rs_buildables(segments)), + _ => Box::new(std::iter::empty()), + } + }) + } + + do_get_rs_buildables(&self.0) + } + + /// Get all items which can result in C++ code + pub fn get_cpp_buildables(&self) -> impl Iterator<Item = &dyn CppBuildable> { + fn do_get_cpp_buildables(segments: &[Segment]) -> impl Iterator<Item = &dyn CppBuildable> { + segments + .iter() + .flat_map(|s| -> Box<dyn Iterator<Item = &dyn CppBuildable>> { + match s { + Segment::Autocxx(includecpp) => { + Box::new(std::iter::once(includecpp as &dyn CppBuildable)) + } + Segment::Cxx(cxxbridge) => { + Box::new(std::iter::once(cxxbridge as &dyn CppBuildable)) + } + Segment::Mod(segments, _) => Box::new(do_get_cpp_buildables(segments)), + _ => Box::new(std::iter::empty()), + } + }) + } + + do_get_cpp_buildables(&self.0) + } + + fn get_autocxxes_mut(&mut self) -> impl Iterator<Item = &mut IncludeCppEngine> { + fn do_get_autocxxes_mut( + segments: &mut [Segment], + ) -> impl Iterator<Item = &mut IncludeCppEngine> { + segments + .iter_mut() + .flat_map(|s| -> Box<dyn Iterator<Item = &mut IncludeCppEngine>> { + match s { + Segment::Autocxx(includecpp) => Box::new(std::iter::once(includecpp)), + Segment::Mod(segments, _) => Box::new(do_get_autocxxes_mut(segments)), + _ => Box::new(std::iter::empty()), + } + }) + } + + do_get_autocxxes_mut(&mut self.0) + } + + pub fn include_dirs(&self) -> impl Iterator<Item = &PathBuf> { + fn do_get_include_dirs(segments: &[Segment]) -> impl Iterator<Item = &PathBuf> { + segments + .iter() + .flat_map(|s| -> Box<dyn Iterator<Item = &PathBuf>> { + match s { + Segment::Autocxx(includecpp) => Box::new(includecpp.include_dirs()), + Segment::Mod(segments, _) => Box::new(do_get_include_dirs(segments)), + _ => Box::new(std::iter::empty()), + } + }) + } + + do_get_include_dirs(&self.0) + } + + pub fn resolve_all( + &mut self, + autocxx_inc: Vec<PathBuf>, + extra_clang_args: &[&str], + dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>, + cpp_codegen_options: &CppCodegenOptions, + ) -> Result<(), ParseError> { + let mut mods_found = HashSet::new(); + let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> = + dep_recorder.map(Rc::from); + for include_cpp in self.get_autocxxes_mut() { + #[allow(clippy::manual_map)] // because of dyn shenanigans + let dep_recorder: Option<Box<dyn RebuildDependencyRecorder>> = match &inner_dep_recorder + { + None => None, + Some(inner_dep_recorder) => Some(Box::new(CompositeDepRecorder::new( + inner_dep_recorder.clone(), + ))), + }; + if !mods_found.insert(include_cpp.get_mod_name()) { + return Err(ParseError::ConflictingModNames); + } + include_cpp + .generate( + autocxx_inc.clone(), + extra_clang_args, + dep_recorder, + cpp_codegen_options, + ) + .map_err(ParseError::AutocxxCodegenError)? + } + Ok(()) + } +} + +impl ToTokens for ParsedFile { + fn to_tokens(&self, tokens: &mut TokenStream) { + for seg in &self.0 { + seg.to_tokens(tokens) + } + } +} + +impl ToTokens for Segment { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Segment::Other(item) => item.to_tokens(tokens), + Segment::Autocxx(autocxx) => { + let these_tokens = autocxx.generate_rs(); + tokens.extend(these_tokens); + } + Segment::Cxx(cxxbridge) => cxxbridge.to_tokens(tokens), + Segment::Mod(segments, (brace, itemmod)) => { + let mod_items = segments + .iter() + .map(|segment| syn::parse2::<Item>(segment.to_token_stream()).unwrap()) + .collect(); + let itemmod = ItemMod { + content: Some((*brace, mod_items)), + ..itemmod.clone() + }; + Item::Mod(itemmod).to_tokens(tokens) + } + } + } +} + +/// Shenanigans required to share the same RebuildDependencyRecorder +/// with all of the include_cpp instances in this one file. +#[derive(Debug, Clone)] +struct CompositeDepRecorder(Rc<dyn RebuildDependencyRecorder>); + +impl CompositeDepRecorder { + fn new(inner: Rc<dyn RebuildDependencyRecorder>) -> Self { + CompositeDepRecorder(inner) + } +} + +impl UnwindSafe for CompositeDepRecorder {} + +impl RebuildDependencyRecorder for CompositeDepRecorder { + fn record_header_file_dependency(&self, filename: &str) { + self.0.record_header_file_dependency(filename); + } +}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/rust_pretty_printer.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/rust_pretty_printer.rs similarity index 62% rename from third_party/rust/autocxx_engine/v0_16/crate/src/rust_pretty_printer.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/rust_pretty_printer.rs index 0c7ccee7..9c23cbc 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/rust_pretty_printer.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/rust_pretty_printer.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use proc_macro2::TokenStream; use std::io::Write;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/types.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/types.rs similarity index 93% rename from third_party/rust/autocxx_engine/v0_16/crate/src/types.rs rename to third_party/rust/autocxx_engine/v0_17/crate/src/types.rs index a0bbf13..241c872 100644 --- a/third_party/rust/autocxx_engine/v0_16/crate/src/types.rs +++ b/third_party/rust/autocxx_engine/v0_17/crate/src/types.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use itertools::Itertools; use proc_macro2::Span;
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_gen/v0_16/crate/.cargo_vcs_info.json deleted file mode 100644 index bf555d2..0000000 --- a/third_party/rust/autocxx_gen/v0_16/crate/.cargo_vcs_info.json +++ /dev/null
@@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "1c57dc961febd206a8046db5a9badac9f5f23b74" - }, - "path_in_vcs": "gen/cmd" -} \ No newline at end of file
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml.orig b/third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml.orig deleted file mode 100644 index ee50bbfd..0000000 --- a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml.orig +++ /dev/null
@@ -1,39 +0,0 @@ -# Copyright 2020 Google LLC -# -# 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 -# -# https://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. - -[package] -name = "autocxx-gen" -version = "0.16.0" -authors = ["Adrian Taylor <adetaylor@chromium.org>"] -edition = "2021" -license = "MIT OR Apache-2.0" -description = "Safe autogenerated interop between Rust and C++" -repository = "https://github.com/google/autocxx" -keywords = ["ffi"] -categories = ["development-tools::ffi", "api-bindings"] - -[features] -runtime = [ "autocxx-engine/runtime" ] -static = [ "autocxx-engine/static" ] - -[dependencies] -autocxx-engine = { version="=0.16.0", path="../../engine" } -clap = "~2.33" -quote = "1.0.7" -proc-macro2 = "1.0" -env_logger = "0.9.0" - -[dev-dependencies] -assert_cmd = "1.0.3" -tempdir = "0.3.7"
diff --git a/third_party/rust/autocxx_gen/v0_16/BUILD.gn b/third_party/rust/autocxx_gen/v0_17/BUILD.gn similarity index 80% rename from third_party/rust/autocxx_gen/v0_16/BUILD.gn rename to third_party/rust/autocxx_gen/v0_17/BUILD.gn index c43f38cc..d5c5b3f5 100644 --- a/third_party/rust/autocxx_gen/v0_16/BUILD.gn +++ b/third_party/rust/autocxx_gen/v0_17/BUILD.gn
@@ -10,14 +10,14 @@ sources = [ "crate/src/main.rs" ] edition = "2018" deps = [ - "//third_party/rust/autocxx_engine/v0_16:lib", + "//third_party/rust/autocxx_engine/v0_17:lib", "//third_party/rust/clap/v2:lib", "//third_party/rust/env_logger/v0_9:lib", "//third_party/rust/proc_macro2/v1:lib", "//third_party/rust/quote/v1:lib", ] rustenv = [ - "CARGO_PKG_VERSION=foo", - "CARGO_PKG_AUTHORS=foo", + "CARGO_PKG_VERSION=0.17", + "CARGO_PKG_AUTHORS=adetaylor@chromium.org", ] }
diff --git a/third_party/rust/autocxx_gen/v0_16/README.chromium b/third_party/rust/autocxx_gen/v0_17/README.chromium similarity index 78% rename from third_party/rust/autocxx_gen/v0_16/README.chromium rename to third_party/rust/autocxx_gen/v0_17/README.chromium index 30cc38f..063d33e9 100644 --- a/third_party/rust/autocxx_gen/v0_16/README.chromium +++ b/third_party/rust/autocxx_gen/v0_17/README.chromium
@@ -1,6 +1,6 @@ Name: autocxx-gen URL: https://crates.io/crates/autocxx-gen Description: Safe autogenerated interop between Rust and C++ -Version: 0.16.0 -Security Critical: no +Version: 0.17.2 +Security Critical: yes License: Apache 2.0
diff --git a/third_party/rust/autocxx_gen/v0_17/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_gen/v0_17/crate/.cargo_vcs_info.json new file mode 100644 index 0000000..3c793de0 --- /dev/null +++ b/third_party/rust/autocxx_gen/v0_17/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "5bb748878f97f854bffa61e5a7cd8ebf970f3dcd" + }, + "path_in_vcs": "gen/cmd" +} \ No newline at end of file
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.lock b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.lock similarity index 84% rename from third_party/rust/autocxx_gen/v0_16/crate/Cargo.lock rename to third_party/rust/autocxx_gen/v0_17/crate/Cargo.lock index e7863d2..e5785f6 100644 --- a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.lock +++ b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.lock
@@ -84,14 +84,13 @@ [[package]] name = "autocxx-engine" -version = "0.16.0" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02323905bec49fde96ff028fcff1c478d0eba14fa34ea5eb5e4d17439748e42a" +checksum = "66906a5d5167cb933a8faf70a9dd27ebb7f8f68d77054766bc9cf05582d3ac51" dependencies = [ "aquamarine", "autocxx-bindgen", "autocxx-parser", - "cxx", "cxx-gen", "indoc", "itertools 0.10.3", @@ -103,13 +102,12 @@ "strum_macros", "syn", "tempfile", - "unzip-n", "version_check", ] [[package]] name = "autocxx-gen" -version = "0.16.0" +version = "0.17.2" dependencies = [ "assert_cmd", "autocxx-engine", @@ -122,9 +120,9 @@ [[package]] name = "autocxx-parser" -version = "0.16.0" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0ad08260adcecc119b08f460b0633b6e306ea2f6fda4f27e4dd28e20b9a2f4" +checksum = "688d5eadba022940ccca017a3aa83edd5d1d7acc8ba23c322d0cec1e4ed4afc0" dependencies = [ "log", "proc-macro2", @@ -152,12 +150,6 @@ ] [[package]] -name = "cc" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" - -[[package]] name = "cexpr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -209,22 +201,10 @@ ] [[package]] -name = "cxx" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcd554072878dec8c16f59839336bf712d6d1f0d8d27cf9b471c10a484a3290f" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] name = "cxx-gen" -version = "0.7.64" +version = "0.7.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2614ca083ee2bb4d362fd63d4d892900b2814566f72e9db38fa9f30182e6fa66" +checksum = "2bba249c8ea90cff9c647cd76928efc82f1e8684da0a6cedb4416cc79478050d" dependencies = [ "codespan-reporting", "proc-macro2", @@ -233,23 +213,6 @@ ] [[package]] -name = "cxxbridge-flags" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cc629121d3f01cd7c85ba046b3bcef66d182429b495eeb1080396ba4bfb8ae4" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a8fa39ee5a91d04a99fc09fc3f1dd780ebfe31817721abcaecd823e00c9f3b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "difflib" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -303,12 +266,9 @@ [[package]] name = "heck" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" @@ -327,9 +287,9 @@ [[package]] name = "indoc" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136" +checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e" dependencies = [ "unindent", ] @@ -381,9 +341,9 @@ [[package]] name = "libc" -version = "0.2.117" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" +checksum = "ad5c14e80759d0939d013e6ca49930e59fc53dd8e5009132f76240c179380c09" [[package]] name = "libloading" @@ -396,15 +356,6 @@ ] [[package]] -name = "link-cplusplus" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cae2cd7ba2f3f63938b9c724475dfb7b9861b545a90324476324ed21dbc8c8" -dependencies = [ - "cc", -] - -[[package]] name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -427,20 +378,19 @@ [[package]] name = "nom" -version = "7.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" dependencies = [ "memchr", "minimal-lexical", - "version_check", ] [[package]] name = "once_cell" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "peeking_take_while" @@ -556,18 +506,18 @@ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", @@ -632,9 +582,9 @@ [[package]] name = "serde_json" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ "itoa", "ryu", @@ -655,9 +605,9 @@ [[package]] name = "strum_macros" -version = "0.23.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" +checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef" dependencies = [ "heck", "proc-macro2", @@ -668,9 +618,9 @@ [[package]] name = "syn" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "ebd69e719f31e88618baa1eaa6ee2de5c9a1c004f1e9ecdb58e8352a13f20a01" dependencies = [ "proc-macro2", "quote", @@ -703,9 +653,9 @@ [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] @@ -726,12 +676,6 @@ ] [[package]] -name = "unicode-segmentation" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" - -[[package]] name = "unicode-width" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -745,20 +689,9 @@ [[package]] name = "unindent" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" - -[[package]] -name = "unzip-n" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7e85a0596447f0f2ac090e16bc4c516c6fe91771fb0c0ccf7fa3dae896b9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8" [[package]] name = "vec_map"
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml similarity index 96% rename from third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml rename to third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml index e4b1d69..bee6b0a 100644 --- a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml +++ b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml
@@ -12,7 +12,7 @@ [package] edition = "2021" name = "autocxx-gen" -version = "0.16.0" +version = "0.17.2" authors = ["Adrian Taylor <adetaylor@chromium.org>"] description = "Safe autogenerated interop between Rust and C++" keywords = ["ffi"] @@ -25,7 +25,7 @@ resolver = "2" [dependencies.autocxx-engine] -version = "=0.16.0" +version = "=0.17.2" [dependencies.clap] version = "~2.33"
diff --git a/third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml.orig b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml.orig new file mode 100644 index 0000000..b299d79 --- /dev/null +++ b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml.orig
@@ -0,0 +1,33 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +[package] +name = "autocxx-gen" +version = "0.17.2" +authors = ["Adrian Taylor <adetaylor@chromium.org>"] +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Safe autogenerated interop between Rust and C++" +repository = "https://github.com/google/autocxx" +keywords = ["ffi"] +categories = ["development-tools::ffi", "api-bindings"] + +[features] +runtime = [ "autocxx-engine/runtime" ] +static = [ "autocxx-engine/static" ] + +[dependencies] +autocxx-engine = { version="=0.17.2", path="../../engine" } +clap = "~2.33" +quote = "1.0.7" +proc-macro2 = "1.0" +env_logger = "0.9.0" + +[dev-dependencies] +assert_cmd = "1.0.3" +tempdir = "0.3.7"
diff --git a/third_party/rust/autocxx_gen/v0_17/crate/README.md b/third_party/rust/autocxx_gen/v0_17/crate/README.md new file mode 100644 index 0000000..9b91d4a --- /dev/null +++ b/third_party/rust/autocxx_gen/v0_17/crate/README.md
@@ -0,0 +1 @@ +This crate is a [component of autocxx](https://google.github.io/autocxx/).
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/src/main.rs b/third_party/rust/autocxx_gen/v0_17/crate/src/main.rs similarity index 76% rename from third_party/rust/autocxx_gen/v0_16/crate/src/main.rs rename to third_party/rust/autocxx_gen/v0_17/crate/src/main.rs index 0af32b4..207ba96 100644 --- a/third_party/rust/autocxx_gen/v0_16/crate/src/main.rs +++ b/third_party/rust/autocxx_gen/v0_17/crate/src/main.rs
@@ -1,27 +1,20 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. -#[cfg(test)] -mod cmd_test; +#![forbid(unsafe_code)] -use autocxx_engine::{parse_file, CppCodegenOptions}; +use autocxx_engine::{parse_file, HeaderNamer}; use clap::{crate_authors, crate_version, App, Arg, ArgGroup}; use proc_macro2::TokenStream; use quote::ToTokens; use std::io::{Read, Write}; use std::path::PathBuf; -use std::{fs::File, path::Path}; +use std::{cell::Cell, fs::File, path::Path}; pub(crate) static BLANK: &str = "// Blank autocxx placeholder"; @@ -62,13 +55,21 @@ b) build all *.cc files produced by this tool. If your build system requires each build rule to make precise filenames -known in advance, then use --generate-exact 2 -and --fix-rs-include-name. If you do this, you'll need to: -a) Have exactly one include_cpp directive in each source .rs file - (or use different numbers with --generate-exact) -b) Set AUTOCXX_RS_FILE when using autocxx_macro. -c) Teach your build system always that the outputs of this tool - are always guaranteed to be gen0.include.rs, gen0.cc and gen1.cc. +known in advance, then you will need to: +a) Use `--generate-exact <N> --gen-rs-complete` +b) Teach your build system that the C++ files to compile are named `gen0.h`, + `gen0.cc`, `gen1.h`, `gen1.cc`, etc (through `N`), corresponding + to each `include_cpp!` section, plus 'cxxgen.h`. `gen.complete.rs` will also + be generated and should be compiled _instead of_ the original Rust file. +c) If `N` is bigger than the number of files needed, extra no-op files will + be emitted. These may still be compiled normally, but won't do anything. If + `N` is smaller than the number of files needed, generation will fail. + +Note that there is currently no way to teach each `include_cpp!` section +which `.include.rs` file to use, so the only way to get fixed output paths is +with `--gen-rs-complete`. There are always multiple `.cc` files (even with just +a single `include_cpp!` section), and we always generate the same number of each +type of file. "; fn main() { @@ -133,9 +134,9 @@ .arg("gen-rs-include") ) .arg( - Arg::with_name("cxx-gen") - .long("cxx-gen") - .help("Perform C++ codegen also for #[cxx::bridge] blocks. Only applies for --gen-cpp") + Arg::with_name("skip-cxx-gen") + .long("skip-cxx-gen") + .help("Skip performing C++ codegen for #[cxx::bridge] blocks. Only applies for --gen-cpp") .requires("gen-cpp") ) .arg( @@ -172,14 +173,14 @@ Arg::with_name("cxx-h-path") .long("cxx-h-path") .value_name("PREFIX") - .help("prefix for path to cxx.h within #include statements. Must end in /") + .help("prefix for path to cxx.h (from the cxx crate) within #include statements. Must end in /") .takes_value(true), ) .arg( Arg::with_name("cxxgen-h-path") .long("cxxgen-h-path") .value_name("PREFIX") - .help("prefix for path to cxxgen.h within #include statements. Must end in /") + .help("prefix for path to cxxgen.h (which we generate into the output directory) within #include statements. Must end in /") .takes_value(true), ) .arg( @@ -206,11 +207,27 @@ .unwrap_or_default() .collect(); let suppress_system_headers = matches.is_present("suppress-system-headers"); - let mut cpp_codegen_options = CppCodegenOptions::default(); - cpp_codegen_options.suppress_system_headers = suppress_system_headers; - cpp_codegen_options.cxx_impl_annotations = get_option_string("cxx-impl-annotations", &matches); - cpp_codegen_options.path_to_cxx_h = get_option_string("cxx-h-path", &matches); - cpp_codegen_options.path_to_cxxgen_h = get_option_string("cxxgen-h-path", &matches); + let desired_number = matches + .value_of("generate-exact") + .map(|s| s.parse::<usize>().unwrap()); + let header_counter = Cell::new(0); + let header_namer = if desired_number.is_some() { + HeaderNamer(Box::new(|_| { + let r = format!("gen{}.h", header_counter.get()); + header_counter.set(header_counter.get() + 1); + r + })) + } else { + Default::default() + }; + let cpp_codegen_options = autocxx_engine::CppCodegenOptions { + suppress_system_headers, + cxx_impl_annotations: get_option_string("cxx-impl-annotations", &matches), + path_to_cxx_h: get_option_string("cxx-h-path", &matches), + path_to_cxxgen_h: get_option_string("cxxgen-h-path", &matches), + skip_cxx_gen: matches.is_present("skip-cxx-gen"), + header_namer, + }; // In future, we should provide an option to write a .d file here // by passing a callback into the dep_recorder parameter here. // https://github.com/google/autocxx/issues/56 @@ -218,9 +235,6 @@ .resolve_all(incs, &extra_clang_args, None, &cpp_codegen_options) .expect("Unable to resolve macro"); let outdir: PathBuf = matches.value_of_os("outdir").unwrap().into(); - let desired_number = matches - .value_of("generate-exact") - .map(|s| s.parse::<usize>().unwrap()); if matches.is_present("gen-cpp") { let cpp = matches.value_of("cpp-extension").unwrap(); let mut counter = 0usize; @@ -237,6 +251,8 @@ } write_placeholders(&outdir, counter, desired_number, cpp); } + drop(cpp_codegen_options); + write_placeholders(&outdir, header_counter.into_inner(), desired_number, "h"); if matches.is_present("gen-rs-complete") { let mut ts = TokenStream::new(); parsed_file.to_tokens(&mut ts); @@ -259,7 +275,9 @@ write_to_file(&outdir, fname, ts.to_string().as_bytes()); counter += 1; } - write_placeholders(&outdir, counter, desired_number, "include.rs"); + if matches.is_present("fix-rs-include-name") { + write_placeholders(&outdir, counter, desired_number, "include.rs"); + } } } @@ -276,7 +294,7 @@ ) { if let Some(desired_number) = desired_number { if counter > desired_number { - panic!("More include_cpp! sections were found than expected"); + panic!("More .{} files were generated than expected. Increase the value passed to --generate-exact or reduce the number of include_cpp! sections.", extension); } while counter < desired_number { let fname = format!("gen{}.{}", counter, extension);
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/src/cmd_test.rs b/third_party/rust/autocxx_gen/v0_17/crate/tests/cmd_test.rs similarity index 61% rename from third_party/rust/autocxx_gen/v0_16/crate/src/cmd_test.rs rename to third_party/rust/autocxx_gen/v0_17/crate/tests/cmd_test.rs index 2adbc3a..8ebd3fb 100644 --- a/third_party/rust/autocxx_gen/v0_16/crate/src/cmd_test.rs +++ b/third_party/rust/autocxx_gen/v0_17/crate/tests/cmd_test.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::{convert::TryInto, fs::File, io::Write, path::Path}; @@ -19,6 +13,7 @@ static MAIN_RS: &str = include_str!("../../../demo/src/main.rs"); static INPUT_H: &str = include_str!("../../../demo/src/input.h"); +static BLANK: &str = "// Blank autocxx placeholder"; #[test] fn test_help() -> Result<(), Box<dyn std::error::Error>> { @@ -31,6 +26,19 @@ where F: FnOnce(&mut Command), { + let result = base_test_ex(tmp_dir, arg_modifier, false); + assert_contentful(tmp_dir, "gen0.cc"); + result +} + +fn base_test_ex<F>( + tmp_dir: &TempDir, + arg_modifier: F, + use_complete_rs: bool, +) -> Result<(), Box<dyn std::error::Error>> +where + F: FnOnce(&mut Command), +{ let demo_code_dir = tmp_dir.path().join("demo"); std::fs::create_dir(&demo_code_dir).unwrap(); write_to_file(&demo_code_dir, "input.h", INPUT_H.as_bytes()); @@ -43,11 +51,13 @@ .arg(demo_rs) .arg("--outdir") .arg(tmp_dir.path().to_str().unwrap()) - .arg("--gen-cpp") - .arg("--gen-rs-include") - .assert() - .success(); - assert_contentful(&tmp_dir, "gen0.cc"); + .arg("--gen-cpp"); + if use_complete_rs { + cmd.arg("--gen-rs-complete"); + } else { + cmd.arg("--gen-rs-include"); + } + cmd.assert().success(); Ok(()) } @@ -68,7 +78,7 @@ .arg("--generate-exact") .arg("3"); })?; - assert_contains(&tmp_dir, "autocxxgen_ffi.h", "foo/cxx.h"); + assert_contains(&tmp_dir, "gen0.h", "foo/cxx.h"); // Currently we don't test cxxgen-h-path because we build the demo code // which doesn't refer to generated cxx header code. Ok(()) @@ -77,17 +87,23 @@ #[test] fn test_gen_fixed_num() -> Result<(), Box<dyn std::error::Error>> { let tmp_dir = TempDir::new("example")?; - base_test(&tmp_dir, |cmd| { - cmd.arg("--generate-exact") - .arg("3") - .arg("--fix-rs-include-name"); - })?; + base_test_ex( + &tmp_dir, + |cmd| { + cmd.arg("--generate-exact").arg("3"); + }, + true, + )?; assert_contentful(&tmp_dir, "gen0.cc"); - assert_exists(&tmp_dir, "gen1.cc"); - assert_exists(&tmp_dir, "gen2.cc"); - assert_contentful(&tmp_dir, "gen0.include.rs"); - assert_exists(&tmp_dir, "gen1.include.rs"); - assert_exists(&tmp_dir, "gen2.include.rs"); + assert_contentful(&tmp_dir, "gen0.h"); + // TODO: This file will actually try #including one of our .h files if there's anything to put + // in it. Figure out how to make that happen to test that it works. + assert_not_contentful(&tmp_dir, "gen1.cc"); + assert_not_contentful(&tmp_dir, "gen1.h"); + assert_not_contentful(&tmp_dir, "gen2.cc"); + assert_not_contentful(&tmp_dir, "gen2.h"); + assert_contentful(&tmp_dir, "cxxgen.h"); + assert_contentful(&tmp_dir, "gen.complete.rs"); Ok(()) } @@ -119,6 +135,31 @@ Ok(()) } +#[test] +fn test_skip_cxx_gen() -> Result<(), Box<dyn std::error::Error>> { + let tmp_dir = TempDir::new("example")?; + base_test_ex( + &tmp_dir, + |cmd| { + cmd.arg("--generate-exact") + .arg("3") + .arg("--fix-rs-include-name") + .arg("--skip-cxx-gen"); + }, + false, + )?; + assert_contentful(&tmp_dir, "gen0.h"); + assert_not_contentful(&tmp_dir, "gen0.cc"); + assert_exists(&tmp_dir, "gen1.cc"); + assert_exists(&tmp_dir, "gen1.h"); + assert_exists(&tmp_dir, "gen2.cc"); + assert_exists(&tmp_dir, "gen2.h"); + assert_contentful(&tmp_dir, "gen0.include.rs"); + assert_exists(&tmp_dir, "gen1.include.rs"); + assert_exists(&tmp_dir, "gen2.include.rs"); + Ok(()) +} + fn write_to_file(dir: &Path, filename: &str, content: &[u8]) { let path = dir.join(filename); let mut f = File::create(&path).expect("Unable to create file"); @@ -130,7 +171,23 @@ if !p.exists() { panic!("File {} didn't exist", p.to_string_lossy()); } - assert!(p.metadata().unwrap().len() > super::BLANK.len().try_into().unwrap()); + assert!( + p.metadata().unwrap().len() > BLANK.len().try_into().unwrap(), + "File {} is empty", + fname + ); +} + +fn assert_not_contentful(outdir: &TempDir, fname: &str) { + let p = outdir.path().join(fname); + if !p.exists() { + panic!("File {} didn't exist", p.to_string_lossy()); + } + assert!( + p.metadata().unwrap().len() <= BLANK.len().try_into().unwrap(), + "File {} is not empty", + fname + ); } fn assert_exists(outdir: &TempDir, fname: &str) { @@ -142,7 +199,7 @@ fn assert_contains(outdir: &TempDir, fname: &str, pattern: &str) { let p = outdir.path().join(fname); - let content = std::fs::read_to_string(&p).unwrap(); + let content = std::fs::read_to_string(&p).expect(fname); eprintln!("content = {}", content); assert!(content.contains(pattern)); }
diff --git a/third_party/rust/autocxx_macro/v0_16/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_macro/v0_16/crate/.cargo_vcs_info.json deleted file mode 100644 index 6852a74..0000000 --- a/third_party/rust/autocxx_macro/v0_16/crate/.cargo_vcs_info.json +++ /dev/null
@@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "1c57dc961febd206a8046db5a9badac9f5f23b74" - }, - "path_in_vcs": "macro" -} \ No newline at end of file
diff --git a/third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml.orig b/third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml.orig deleted file mode 100644 index a680f7f..0000000 --- a/third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml.orig +++ /dev/null
@@ -1,37 +0,0 @@ -# Copyright 2020 Google LLC -# -# 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 -# -# https://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. - -[package] -name = "autocxx-macro" -version = "0.16.0" -authors = ["Adrian Taylor <adetaylor@chromium.org>"] -license = "MIT OR Apache-2.0" -description = "Safe autogenerated interop between Rust and C++" -repository = "https://github.com/google/autocxx" -edition = "2021" -keywords = ["ffi"] -categories = ["development-tools::ffi", "api-bindings"] - -[lib] -proc-macro = true - -[dependencies] -autocxx-parser = { path="../parser", version="=0.16.0" } -proc-macro-error = "1.0" -proc-macro2 = "1.0.11" -quote = "1.0" - -[dependencies.syn] -version = "1.0" -features = [ "full" ]
diff --git a/third_party/rust/autocxx_macro/v0_16/BUILD.gn b/third_party/rust/autocxx_macro/v0_17/BUILD.gn similarity index 92% rename from third_party/rust/autocxx_macro/v0_16/BUILD.gn rename to third_party/rust/autocxx_macro/v0_17/BUILD.gn index b94b7fb..8608bcd4 100644 --- a/third_party/rust/autocxx_macro/v0_16/BUILD.gn +++ b/third_party/rust/autocxx_macro/v0_17/BUILD.gn
@@ -6,7 +6,7 @@ cargo_crate("lib") { crate_name = "autocxx_macro" - epoch = "0.16" + epoch = "0.17" crate_type = "proc-macro" # Only for usage from third-party crates. Add the crate to @@ -19,7 +19,7 @@ sources = [ "crate/src/lib.rs" ] edition = "2021" deps = [ - "//third_party/rust/autocxx_parser/v0_16:lib", + "//third_party/rust/autocxx_parser/v0_17:lib", "//third_party/rust/proc_macro2/v1:lib", "//third_party/rust/proc_macro_error/v1:lib", "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/autocxx_macro/v0_16/README.chromium b/third_party/rust/autocxx_macro/v0_17/README.chromium similarity index 78% rename from third_party/rust/autocxx_macro/v0_16/README.chromium rename to third_party/rust/autocxx_macro/v0_17/README.chromium index 1723585..e4b2e74 100644 --- a/third_party/rust/autocxx_macro/v0_16/README.chromium +++ b/third_party/rust/autocxx_macro/v0_17/README.chromium
@@ -1,6 +1,6 @@ Name: autocxx-macro URL: https://crates.io/crates/autocxx-macro Description: Safe autogenerated interop between Rust and C++ -Version: 0.16.0 -Security Critical: no +Version: 0.17.2 +Security Critical: yes License: Apache 2.0
diff --git a/third_party/rust/autocxx_macro/v0_17/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_macro/v0_17/crate/.cargo_vcs_info.json new file mode 100644 index 0000000..ca758e7 --- /dev/null +++ b/third_party/rust/autocxx_macro/v0_17/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "5bb748878f97f854bffa61e5a7cd8ebf970f3dcd" + }, + "path_in_vcs": "macro" +} \ No newline at end of file
diff --git a/third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml b/third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml similarity index 96% rename from third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml rename to third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml index 61df0bf..ea4aed9 100644 --- a/third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml +++ b/third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml
@@ -12,7 +12,7 @@ [package] edition = "2021" name = "autocxx-macro" -version = "0.16.0" +version = "0.17.2" authors = ["Adrian Taylor <adetaylor@chromium.org>"] description = "Safe autogenerated interop between Rust and C++" keywords = ["ffi"] @@ -28,7 +28,7 @@ proc-macro = true [dependencies.autocxx-parser] -version = "=0.16.0" +version = "=0.17.2" [dependencies.proc-macro-error] version = "1.0"
diff --git a/third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml.orig b/third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml.orig new file mode 100644 index 0000000..b4c21ee --- /dev/null +++ b/third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml.orig
@@ -0,0 +1,31 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +[package] +name = "autocxx-macro" +version = "0.17.2" +authors = ["Adrian Taylor <adetaylor@chromium.org>"] +license = "MIT OR Apache-2.0" +description = "Safe autogenerated interop between Rust and C++" +repository = "https://github.com/google/autocxx" +edition = "2021" +keywords = ["ffi"] +categories = ["development-tools::ffi", "api-bindings"] + +[lib] +proc-macro = true + +[dependencies] +autocxx-parser = { path="../parser", version="=0.17.2" } +proc-macro-error = "1.0" +proc-macro2 = "1.0.11" +quote = "1.0" + +[dependencies.syn] +version = "1.0" +features = [ "full" ]
diff --git a/third_party/rust/autocxx_macro/v0_17/crate/README.md b/third_party/rust/autocxx_macro/v0_17/crate/README.md new file mode 100644 index 0000000..9b91d4a --- /dev/null +++ b/third_party/rust/autocxx_macro/v0_17/crate/README.md
@@ -0,0 +1 @@ +This crate is a [component of autocxx](https://google.github.io/autocxx/).
diff --git a/third_party/rust/autocxx_macro/v0_16/crate/src/lib.rs b/third_party/rust/autocxx_macro/v0_17/crate/src/lib.rs similarity index 89% rename from third_party/rust/autocxx_macro/v0_16/crate/src/lib.rs rename to third_party/rust/autocxx_macro/v0_17/crate/src/lib.rs index 529476c7..01ccd69e 100644 --- a/third_party/rust/autocxx_macro/v0_16/crate/src/lib.rs +++ b/third_party/rust/autocxx_macro/v0_17/crate/src/lib.rs
@@ -1,16 +1,12 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![forbid(unsafe_code)] use autocxx_parser::{IncludeCpp, SubclassAttrs}; use proc_macro::TokenStream;
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_parser/v0_16/crate/.cargo_vcs_info.json deleted file mode 100644 index e8fe9f9d1..0000000 --- a/third_party/rust/autocxx_parser/v0_16/crate/.cargo_vcs_info.json +++ /dev/null
@@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "1c57dc961febd206a8046db5a9badac9f5f23b74" - }, - "path_in_vcs": "parser" -} \ No newline at end of file
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml.orig b/third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml.orig deleted file mode 100644 index 9d23eae..0000000 --- a/third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml.orig +++ /dev/null
@@ -1,38 +0,0 @@ -# Copyright 2020 Google LLC -# -# 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 -# -# https://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. - -[package] -name = "autocxx-parser" -version = "0.16.0" -authors = ["Adrian Taylor <adetaylor@chromium.org>"] -license = "MIT OR Apache-2.0" -description = "Safe autogenerated interop between Rust and C++" -repository = "https://github.com/google/autocxx" -edition = "2021" -keywords = ["ffi"] -categories = ["development-tools::ffi", "api-bindings"] - -[dependencies] -log = "0.4" -proc-macro2 = "1.0" -quote = "1.0" -serde = { version = "1.0", optional = true } -serde_derive = { version = "1.0", optional = true } - -[dependencies.syn] -version = "1.0.39" -features = [ "full" ] - -[features] -reproduction_case = [ "serde_derive", "serde" ]
diff --git a/third_party/rust/autocxx_parser/v0_16/BUILD.gn b/third_party/rust/autocxx_parser/v0_17/BUILD.gn similarity index 98% rename from third_party/rust/autocxx_parser/v0_16/BUILD.gn rename to third_party/rust/autocxx_parser/v0_17/BUILD.gn index 4286401..2b80f884 100644 --- a/third_party/rust/autocxx_parser/v0_16/BUILD.gn +++ b/third_party/rust/autocxx_parser/v0_17/BUILD.gn
@@ -6,7 +6,7 @@ cargo_crate("lib") { crate_name = "autocxx_parser" - epoch = "0.16" + epoch = "0.17" crate_type = "rlib" # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/autocxx_parser/v0_16/README.chromium b/third_party/rust/autocxx_parser/v0_17/README.chromium similarity index 78% rename from third_party/rust/autocxx_parser/v0_16/README.chromium rename to third_party/rust/autocxx_parser/v0_17/README.chromium index e7b607d..7bf312a 100644 --- a/third_party/rust/autocxx_parser/v0_16/README.chromium +++ b/third_party/rust/autocxx_parser/v0_17/README.chromium
@@ -1,6 +1,6 @@ Name: autocxx-parser URL: https://crates.io/crates/autocxx-parser Description: Safe autogenerated interop between Rust and C++ -Version: 0.16.0 -Security Critical: no +Version: 0.17.2 +Security Critical: yes License: Apache 2.0
diff --git a/third_party/rust/autocxx_parser/v0_17/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_parser/v0_17/crate/.cargo_vcs_info.json new file mode 100644 index 0000000..f7c92a32 --- /dev/null +++ b/third_party/rust/autocxx_parser/v0_17/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "5bb748878f97f854bffa61e5a7cd8ebf970f3dcd" + }, + "path_in_vcs": "parser" +} \ No newline at end of file
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml b/third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml similarity index 98% rename from third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml rename to third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml index b2a50884..0932198 100644 --- a/third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml +++ b/third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml
@@ -12,7 +12,7 @@ [package] edition = "2021" name = "autocxx-parser" -version = "0.16.0" +version = "0.17.2" authors = ["Adrian Taylor <adetaylor@chromium.org>"] description = "Safe autogenerated interop between Rust and C++" keywords = ["ffi"]
diff --git a/third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml.orig b/third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml.orig new file mode 100644 index 0000000..15ea808 --- /dev/null +++ b/third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml.orig
@@ -0,0 +1,32 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +[package] +name = "autocxx-parser" +version = "0.17.2" +authors = ["Adrian Taylor <adetaylor@chromium.org>"] +license = "MIT OR Apache-2.0" +description = "Safe autogenerated interop between Rust and C++" +repository = "https://github.com/google/autocxx" +edition = "2021" +keywords = ["ffi"] +categories = ["development-tools::ffi", "api-bindings"] + +[dependencies] +log = "0.4" +proc-macro2 = "1.0" +quote = "1.0" +serde = { version = "1.0", optional = true } +serde_derive = { version = "1.0", optional = true } + +[dependencies.syn] +version = "1.0.39" +features = [ "full" ] + +[features] +reproduction_case = [ "serde_derive", "serde" ]
diff --git a/third_party/rust/autocxx_parser/v0_17/crate/README.md b/third_party/rust/autocxx_parser/v0_17/crate/README.md new file mode 100644 index 0000000..9b91d4a --- /dev/null +++ b/third_party/rust/autocxx_parser/v0_17/crate/README.md
@@ -0,0 +1 @@ +This crate is a [component of autocxx](https://google.github.io/autocxx/).
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/src/config.rs b/third_party/rust/autocxx_parser/v0_17/crate/src/config.rs similarity index 79% rename from third_party/rust/autocxx_parser/v0_16/crate/src/config.rs rename to third_party/rust/autocxx_parser/v0_17/crate/src/config.rs index 3ada407..4a9a181 100644 --- a/third_party/rust/autocxx_parser/v0_16/crate/src/config.rs +++ b/third_party/rust/autocxx_parser/v0_17/crate/src/config.rs
@@ -1,24 +1,18 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. -use std::collections::HashSet; +use std::{borrow::Cow, collections::HashSet}; use proc_macro2::Span; use quote::ToTokens; use syn::{ parse::{Parse, ParseStream}, - LitStr, Signature, Token, + Signature, Token, }; use syn::{Ident, Result as ParseResult}; @@ -70,31 +64,47 @@ } } +/// An entry in the allowlist. +#[derive(Hash, Debug)] +pub enum AllowlistEntry { + Item(String), + Namespace(String), +} + +impl AllowlistEntry { + fn to_bindgen_item(&self) -> String { + match self { + AllowlistEntry::Item(i) => i.clone(), + AllowlistEntry::Namespace(ns) => format!("{}::.*", ns), + } + } +} + /// Allowlist configuration. #[derive(Hash, Debug)] pub enum Allowlist { - Unspecified(Vec<String>), + Unspecified(Vec<AllowlistEntry>), All, - Specific(Vec<String>), + Specific(Vec<AllowlistEntry>), } impl Allowlist { - pub fn push(&mut self, item: LitStr) -> ParseResult<()> { + pub fn push(&mut self, item: AllowlistEntry, span: Span) -> ParseResult<()> { match self { Allowlist::Unspecified(ref mut uncommitted_list) => { let new_list = uncommitted_list .drain(..) - .chain(std::iter::once(item.value())) + .chain(std::iter::once(item)) .collect(); *self = Allowlist::Specific(new_list); } Allowlist::All => { return Err(syn::Error::new( - item.span(), - "use either generate!/generate_pod! or generate_all!, not both.", + span, + "use either generate!/generate_pod!/generate_ns! or generate_all!, not both.", )) } - Allowlist::Specific(list) => list.push(item.value()), + Allowlist::Specific(list) => list.push(item), }; Ok(()) } @@ -103,7 +113,7 @@ if matches!(self, Allowlist::Specific(..)) { return Err(syn::Error::new( ident.span(), - "use either generate!/generate_pod! or generate_all!, not both.", + "use either generate!/generate_pod!/generate_ns! or generate_all!, not both.", )); } *self = Allowlist::All; @@ -191,13 +201,24 @@ let args; syn::parenthesized!(args in input); let generate: syn::LitStr = args.parse()?; - allowlist.push(generate)?; + allowlist.push(AllowlistEntry::Item(generate.value()), generate.span())?; + } else if ident == "generate_ns" { + let args; + syn::parenthesized!(args in input); + let generate_ns: syn::LitStr = args.parse()?; + allowlist.push( + AllowlistEntry::Namespace(generate_ns.value()), + generate_ns.span(), + )?; } else if ident == "generate_pod" { let args; syn::parenthesized!(args in input); let generate_pod: syn::LitStr = args.parse()?; pod_requests.push(generate_pod.value()); - allowlist.push(generate_pod)?; + allowlist.push( + AllowlistEntry::Item(generate_pod.value()), + generate_pod.span(), + )?; } else if ident == "pod" { let args; syn::parenthesized!(args in input); @@ -321,7 +342,16 @@ /// we should raise an error if we weren't able to do so. pub fn must_generate_list(&self) -> Box<dyn Iterator<Item = String> + '_> { if let Allowlist::Specific(items) = &self.allowlist { - Box::new(items.iter().chain(self.pod_requests.iter()).cloned()) + Box::new( + items + .iter() + .filter_map(|i| match i { + AllowlistEntry::Item(i) => Some(i), + AllowlistEntry::Namespace(_) => None, + }) + .chain(self.pod_requests.iter()) + .cloned(), + ) } else { Box::new(self.pod_requests.iter().cloned()) } @@ -334,8 +364,8 @@ Allowlist::Specific(items) => Some(Box::new( items .iter() - .chain(self.pod_requests.iter()) - .cloned() + .map(AllowlistEntry::to_bindgen_item) + .chain(self.pod_requests.iter().cloned()) .chain(self.active_utilities()) .chain(self.subclasses.iter().flat_map(|sc| { [ @@ -353,10 +383,22 @@ if self.exclude_utilities { Vec::new() } else { - vec![self.get_makestring_name()] + vec![self.get_makestring_name().to_string()] } } + fn is_subclass_or_superclass(&self, cpp_name: &str) -> bool { + self.subclasses + .iter() + .flat_map(|sc| { + [ + Cow::Owned(sc.subclass.to_string()), + Cow::Borrowed(&sc.superclass), + ] + }) + .any(|item| cpp_name == item.as_str()) + } + /// Whether this type is on the allowlist specified by the user. /// /// A note on the allowlist handling in general. It's used in two places: @@ -366,16 +408,19 @@ /// This second pass may seem redundant. But sometimes bindgen generates /// unnecessary stuff. pub fn is_on_allowlist(&self, cpp_name: &str) -> bool { - match self.bindgen_allowlist() { - None => true, - Some(mut items) => { - items.any(|item| item == cpp_name) - || self.active_utilities().iter().any(|item| *item == cpp_name) - || self.is_subclass_holder(cpp_name) - || self.is_subclass_cpp(cpp_name) - || self.is_rust_fun(cpp_name) + self.active_utilities().iter().any(|item| *item == cpp_name) + || self.is_subclass_or_superclass(cpp_name) + || self.is_subclass_holder(cpp_name) + || self.is_subclass_cpp(cpp_name) + || self.is_rust_fun(cpp_name) + || match &self.allowlist { + Allowlist::Unspecified(_) => panic!("Eek no allowlist yet"), + Allowlist::All => true, + Allowlist::Specific(items) => items.iter().any(|entry| match entry { + AllowlistEntry::Item(i) => i == cpp_name, + AllowlistEntry::Namespace(ns) => cpp_name.starts_with(ns), + }), } - } } pub fn is_on_blocklist(&self, cpp_name: &str) -> bool { @@ -390,14 +435,18 @@ self.blocklist.iter() } - pub fn get_makestring_name(&self) -> String { - format!( - "autocxx_make_string_{}", - self.mod_name - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| "default".into()) - ) + /// In case there are multiple sets of ffi mods in a single binary, + /// endeavor to return a name which can be used to make symbols + /// unique. + pub fn uniquify_name_per_mod<'a>(&self, name: &'a str) -> Cow<'a, str> { + match self.mod_name.as_ref() { + None => Cow::Borrowed(name), + Some(md) => Cow::Owned(format!("{}_{}", name, md)), + } + } + + pub fn get_makestring_name(&self) -> Cow<str> { + self.uniquify_name_per_mod("autocxx_make_string") } pub fn is_rust_type(&self, id: &Ident) -> bool { @@ -451,7 +500,7 @@ } else { Err(syn::Error::new( Span::call_site(), - "expected either generate! or generate_all!", + "expected either generate!/generate_ns! or generate_all!", )) } } else { @@ -505,7 +554,12 @@ Allowlist::All => tokens.extend(quote! { generate_all!() }), Allowlist::Specific(items) => { for i in items { - tokens.extend(quote! { generate!(#i) }); + match i { + AllowlistEntry::Item(i) => tokens.extend(quote! { generate!(#i) }), + AllowlistEntry::Namespace(ns) => { + tokens.extend(quote! { generate_ns!(#ns) }) + } + } } } Allowlist::Unspecified(_) => panic!("Allowlist mode not yet determined"),
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/src/file_locations.rs b/third_party/rust/autocxx_parser/v0_17/crate/src/file_locations.rs similarity index 89% rename from third_party/rust/autocxx_parser/v0_16/crate/src/file_locations.rs rename to third_party/rust/autocxx_parser/v0_17/crate/src/file_locations.rs index 2bcb65d..b353e06 100644 --- a/third_party/rust/autocxx_parser/v0_16/crate/src/file_locations.rs +++ b/third_party/rust/autocxx_parser/v0_17/crate/src/file_locations.rs
@@ -1,16 +1,10 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use proc_macro2::TokenStream; use quote::quote; @@ -29,7 +23,7 @@ /// (based on a hash of the contents of `include_cpp!`.) But /// some types of build system need to know the precise file _name_ /// produced by the codegen phase and passed into the macro phase, -/// so we have a `AUTOCXX_RS_FILE` option for that. +/// so we have some options for that. See `gen --help` for details. pub enum FileLocationStrategy { Custom(PathBuf), FromAutocxxRsFile(PathBuf),
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/src/lib.rs b/third_party/rust/autocxx_parser/v0_17/crate/src/lib.rs similarity index 72% rename from third_party/rust/autocxx_parser/v0_16/crate/src/lib.rs rename to third_party/rust/autocxx_parser/v0_17/crate/src/lib.rs index b63635c..f0d988df 100644 --- a/third_party/rust/autocxx_parser/v0_16/crate/src/lib.rs +++ b/third_party/rust/autocxx_parser/v0_17/crate/src/lib.rs
@@ -1,23 +1,19 @@ // Copyright 2020 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![forbid(unsafe_code)] mod config; pub mod file_locations; mod path; mod subclass_attrs; -pub use config::{IncludeCppConfig, RustFun, Subclass, UnsafePolicy}; +pub use config::{AllowlistEntry, IncludeCppConfig, RustFun, Subclass, UnsafePolicy}; use file_locations::FileLocationStrategy; pub use path::RustPath; use proc_macro2::TokenStream as TokenStream2;
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/src/path.rs b/third_party/rust/autocxx_parser/v0_17/crate/src/path.rs similarity index 72% rename from third_party/rust/autocxx_parser/v0_16/crate/src/path.rs rename to third_party/rust/autocxx_parser/v0_17/crate/src/path.rs index 222a90f..8958807 100644 --- a/third_party/rust/autocxx_parser/v0_16/crate/src/path.rs +++ b/third_party/rust/autocxx_parser/v0_17/crate/src/path.rs
@@ -1,16 +1,10 @@ // Copyright 2021 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use crate::ParseResult; use proc_macro2::Ident;
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/src/subclass_attrs.rs b/third_party/rust/autocxx_parser/v0_17/crate/src/subclass_attrs.rs similarity index 74% rename from third_party/rust/autocxx_parser/v0_16/crate/src/subclass_attrs.rs rename to third_party/rust/autocxx_parser/v0_17/crate/src/subclass_attrs.rs index 3118ef1..9174bec 100644 --- a/third_party/rust/autocxx_parser/v0_16/crate/src/subclass_attrs.rs +++ b/third_party/rust/autocxx_parser/v0_17/crate/src/subclass_attrs.rs
@@ -1,16 +1,10 @@ // Copyright 2021 Google LLC // -// 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 -// -// https://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. +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use proc_macro2::Ident; use quote::ToTokens;
diff --git a/third_party/rust/heck/v0_3/crate/.cargo_vcs_info.json b/third_party/rust/heck/v0_3/crate/.cargo_vcs_info.json deleted file mode 100644 index 43f6c71..0000000 --- a/third_party/rust/heck/v0_3/crate/.cargo_vcs_info.json +++ /dev/null
@@ -1,5 +0,0 @@ -{ - "git": { - "sha1": "d2f9cda3a00a62ae1e0eeeea8f0a081c7598b8c5" - } -}
diff --git a/third_party/rust/heck/v0_3/crate/src/camel.rs b/third_party/rust/heck/v0_3/crate/src/camel.rs deleted file mode 100644 index 6949435..0000000 --- a/third_party/rust/heck/v0_3/crate/src/camel.rs +++ /dev/null
@@ -1,50 +0,0 @@ -use crate::{capitalize, transform}; - -/// This trait defines a camel case conversion. -/// -/// In CamelCase, word boundaries are indicated by capital letters, including -/// the first word. -/// -/// ## Example: -/// -/// ```rust -/// use heck::CamelCase; -/// -/// let sentence = "We are not in the least afraid of ruins."; -/// assert_eq!(sentence.to_camel_case(), "WeAreNotInTheLeastAfraidOfRuins"); -/// ``` -pub trait CamelCase: ToOwned { - /// Convert this type to camel case. - fn to_camel_case(&self) -> Self::Owned; -} - -impl CamelCase for str { - fn to_camel_case(&self) -> String { - transform(self, capitalize, |_| {}) - } -} - -#[cfg(test)] -mod tests { - use super::CamelCase; - - macro_rules! t { - ($t:ident : $s1:expr => $s2:expr) => { - #[test] - fn $t() { - assert_eq!($s1.to_camel_case(), $s2) - } - }; - } - - t!(test1: "CamelCase" => "CamelCase"); - t!(test2: "This is Human case." => "ThisIsHumanCase"); - t!(test3: "MixedUP_CamelCase, with some Spaces" => "MixedUpCamelCaseWithSomeSpaces"); - t!(test4: "mixed_up_ snake_case, with some _spaces" => "MixedUpSnakeCaseWithSomeSpaces"); - t!(test5: "kebab-case" => "KebabCase"); - t!(test6: "SHOUTY_SNAKE_CASE" => "ShoutySnakeCase"); - t!(test7: "snake_case" => "SnakeCase"); - t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "ThisContainsAllKindsOfWordBoundaries"); - t!(test9: "XΣXΣ baffle" => "XσxςBaffle"); - t!(test10: "XMLHttpRequest" => "XmlHttpRequest"); -}
diff --git a/third_party/rust/heck/v0_3/crate/src/mixed.rs b/third_party/rust/heck/v0_3/crate/src/mixed.rs deleted file mode 100644 index 71089f28..0000000 --- a/third_party/rust/heck/v0_3/crate/src/mixed.rs +++ /dev/null
@@ -1,61 +0,0 @@ -use crate::{capitalize, lowercase, transform}; - -/// This trait defines a mixed case conversion. -/// -/// In mixedCase, word boundaries are indicated by capital letters, excepting -/// the first word. -/// -/// ## Example: -/// -/// ```rust -/// use heck::MixedCase; -/// -/// let sentence = "It is we who built these palaces and cities."; -/// assert_eq!(sentence.to_mixed_case(), "itIsWeWhoBuiltThesePalacesAndCities"); -/// ``` -pub trait MixedCase: ToOwned { - /// Convert this type to mixed case. - fn to_mixed_case(&self) -> Self::Owned; -} - -impl MixedCase for str { - fn to_mixed_case(&self) -> String { - transform( - self, - |s, out| { - if out.is_empty() { - lowercase(s, out); - } else { - capitalize(s, out) - } - }, - |_| {}, - ) - } -} - -#[cfg(test)] -mod tests { - use super::MixedCase; - - macro_rules! t { - ($t:ident : $s1:expr => $s2:expr) => { - #[test] - fn $t() { - assert_eq!($s1.to_mixed_case(), $s2) - } - }; - } - - t!(test1: "CamelCase" => "camelCase"); - t!(test2: "This is Human case." => "thisIsHumanCase"); - t!(test3: "MixedUP CamelCase, with some Spaces" => "mixedUpCamelCaseWithSomeSpaces"); - t!(test4: "mixed_up_ snake_case, with some _spaces" => "mixedUpSnakeCaseWithSomeSpaces"); - t!(test5: "kebab-case" => "kebabCase"); - t!(test6: "SHOUTY_SNAKE_CASE" => "shoutySnakeCase"); - t!(test7: "snake_case" => "snakeCase"); - t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "thisContainsAllKindsOfWordBoundaries"); - t!(test9: "XΣXΣ baffle" => "xσxςBaffle"); - t!(test10: "XMLHttpRequest" => "xmlHttpRequest"); - // TODO unicode tests -}
diff --git a/third_party/rust/heck/v0_3/BUILD.gn b/third_party/rust/heck/v0_4/BUILD.gn similarity index 88% rename from third_party/rust/heck/v0_3/BUILD.gn rename to third_party/rust/heck/v0_4/BUILD.gn index faa3ea9..3514d1d 100644 --- a/third_party/rust/heck/v0_3/BUILD.gn +++ b/third_party/rust/heck/v0_4/BUILD.gn
@@ -6,7 +6,7 @@ cargo_crate("lib") { crate_name = "heck" - epoch = "0.3" + epoch = "0.4" crate_type = "rlib" # Only for usage from third-party crates. Add the crate to @@ -18,5 +18,4 @@ build_native_rust_unit_tests = false sources = [ "crate/src/lib.rs" ] edition = "2018" - deps = [ "//third_party/rust/unicode_segmentation/v1:lib" ] }
diff --git a/third_party/rust/heck/v0_3/README.chromium b/third_party/rust/heck/v0_4/README.chromium similarity index 74% rename from third_party/rust/heck/v0_3/README.chromium rename to third_party/rust/heck/v0_4/README.chromium index 2f956a5..5b66f929 100644 --- a/third_party/rust/heck/v0_3/README.chromium +++ b/third_party/rust/heck/v0_4/README.chromium
@@ -1,6 +1,6 @@ Name: heck URL: https://crates.io/crates/heck Description: heck is a case conversion library. -Version: 0.3.3 -Security Critical: no +Version: 0.4.0 +Security Critical: yes License: Apache 2.0
diff --git a/third_party/rust/heck/v0_4/crate/.cargo_vcs_info.json b/third_party/rust/heck/v0_4/crate/.cargo_vcs_info.json new file mode 100644 index 0000000..192652c --- /dev/null +++ b/third_party/rust/heck/v0_4/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "dbcfc7b8db8e532d1fad44518cf73e88d5212161" + }, + "path_in_vcs": "" +} \ No newline at end of file
diff --git a/third_party/rust/heck/v0_4/crate/CHANGELOG.md b/third_party/rust/heck/v0_4/crate/CHANGELOG.md new file mode 100644 index 0000000..1e02914 --- /dev/null +++ b/third_party/rust/heck/v0_4/crate/CHANGELOG.md
@@ -0,0 +1,11 @@ +# 0.4.0 + +Breaking changes: + +* Make unicode support optional (off by default). Enable the `unicode` crate + feature if you need unicode support. +* Rename all traits from `SomeCase` to `ToSomeCase`, matching `std`s convention + of beginning trait names with a verb (`ToOwned`, `AsRef`, …) +* Rename `ToMixedCase` to `ToLowerCamelCase` +* Rename `ToCamelCase` to `ToUpperCamelCase` +* Add `ToPascalCase` as an alias to `ToUpperCamelCase`
diff --git a/third_party/rust/heck/v0_3/crate/Cargo.toml b/third_party/rust/heck/v0_4/crate/Cargo.toml similarity index 70% rename from third_party/rust/heck/v0_3/crate/Cargo.toml rename to third_party/rust/heck/v0_4/crate/Cargo.toml index fc9c28dc..b72cceb 100644 --- a/third_party/rust/heck/v0_3/crate/Cargo.toml +++ b/third_party/rust/heck/v0_4/crate/Cargo.toml
@@ -3,17 +3,16 @@ # 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 = "heck" -version = "0.3.3" +version = "0.4.0" authors = ["Without Boats <woboats@gmail.com>"] include = ["src/**/*", "LICENSE-*", "README.md", "CHANGELOG.md"] description = "heck is a case conversion library." @@ -25,3 +24,8 @@ repository = "https://github.com/withoutboats/heck" [dependencies.unicode-segmentation] version = "1.2.0" +optional = true + +[features] +default = [] +unicode = ["unicode-segmentation"]
diff --git a/third_party/rust/heck/v0_3/crate/Cargo.toml.orig b/third_party/rust/heck/v0_4/crate/Cargo.toml.orig similarity index 77% rename from third_party/rust/heck/v0_3/crate/Cargo.toml.orig rename to third_party/rust/heck/v0_4/crate/Cargo.toml.orig index 80acd39..1de9796 100644 --- a/third_party/rust/heck/v0_3/crate/Cargo.toml.orig +++ b/third_party/rust/heck/v0_4/crate/Cargo.toml.orig
@@ -1,7 +1,7 @@ [package] authors = ["Without Boats <woboats@gmail.com>"] name = "heck" -version = "0.3.3" +version = "0.4.0" edition = "2018" license = "MIT OR Apache-2.0" description = "heck is a case conversion library." @@ -12,5 +12,9 @@ readme = "README.md" include = ["src/**/*", "LICENSE-*", "README.md", "CHANGELOG.md"] +[features] +default = [] +unicode = ["unicode-segmentation"] + [dependencies] -unicode-segmentation = "1.2.0" +unicode-segmentation = { version = "1.2.0", optional = true }
diff --git a/third_party/rust/heck/v0_3/crate/LICENSE-APACHE b/third_party/rust/heck/v0_4/crate/LICENSE-APACHE similarity index 100% rename from third_party/rust/heck/v0_3/crate/LICENSE-APACHE rename to third_party/rust/heck/v0_4/crate/LICENSE-APACHE
diff --git a/third_party/rust/heck/v0_3/crate/LICENSE-MIT b/third_party/rust/heck/v0_4/crate/LICENSE-MIT similarity index 100% rename from third_party/rust/heck/v0_3/crate/LICENSE-MIT rename to third_party/rust/heck/v0_4/crate/LICENSE-MIT
diff --git a/third_party/rust/heck/v0_3/crate/README.md b/third_party/rust/heck/v0_4/crate/README.md similarity index 96% rename from third_party/rust/heck/v0_3/crate/README.md rename to third_party/rust/heck/v0_4/crate/README.md index 33f2a5d..5099339 100644 --- a/third_party/rust/heck/v0_3/crate/README.md +++ b/third_party/rust/heck/v0_4/crate/README.md
@@ -30,11 +30,11 @@ ## Cases contained in this library: -1. CamelCase -2. snake_case -3. kebab-case -4. SHOUTY_SNAKE_CASE -5. mixedCase +1. UpperCamelCase +2. lowerCamelCase +3. snake_case +4. kebab-case +5. SHOUTY_SNAKE_CASE 6. Title Case 7. SHOUTY-KEBAB-CASE
diff --git a/third_party/rust/heck/v0_3/crate/src/kebab.rs b/third_party/rust/heck/v0_4/crate/src/kebab.rs similarity index 65% rename from third_party/rust/heck/v0_3/crate/src/kebab.rs rename to third_party/rust/heck/v0_4/crate/src/kebab.rs index 75e5978..6cce5a5 100644 --- a/third_party/rust/heck/v0_3/crate/src/kebab.rs +++ b/third_party/rust/heck/v0_4/crate/src/kebab.rs
@@ -1,3 +1,5 @@ +use std::fmt; + use crate::{lowercase, transform}; /// This trait defines a kebab case conversion. @@ -7,25 +9,43 @@ /// ## Example: /// /// ```rust -/// use heck::KebabCase; +/// use heck::ToKebabCase; /// /// let sentence = "We are going to inherit the earth."; /// assert_eq!(sentence.to_kebab_case(), "we-are-going-to-inherit-the-earth"); /// ``` -pub trait KebabCase: ToOwned { +pub trait ToKebabCase: ToOwned { /// Convert this type to kebab case. fn to_kebab_case(&self) -> Self::Owned; } -impl KebabCase for str { +impl ToKebabCase for str { fn to_kebab_case(&self) -> Self::Owned { - transform(self, lowercase, |s| s.push('-')) + AsKebabCase(self).to_string() + } +} + +/// This wrapper performs a kebab case conversion in [`fmt::Display`]. +/// +/// ## Example: +/// +/// ``` +/// use heck::AsKebabCase; +/// +/// let sentence = "We are going to inherit the earth."; +/// assert_eq!(format!("{}", AsKebabCase(sentence)), "we-are-going-to-inherit-the-earth"); +/// ``` +pub struct AsKebabCase<T: AsRef<str>>(pub T); + +impl<T: AsRef<str>> fmt::Display for AsKebabCase<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + transform(self.0.as_ref(), lowercase, |f| write!(f, "-"), f) } } #[cfg(test)] mod tests { - use super::KebabCase; + use super::ToKebabCase; macro_rules! t { ($t:ident : $s1:expr => $s2:expr) => { @@ -44,6 +64,7 @@ t!(test6: "SHOUTY_SNAKE_CASE" => "shouty-snake-case"); t!(test7: "snake_case" => "snake-case"); t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "this-contains-all-kinds-of-word-boundaries"); + #[cfg(feature = "unicode")] t!(test9: "XΣXΣ baffle" => "xσxς-baffle"); t!(test10: "XMLHttpRequest" => "xml-http-request"); }
diff --git a/third_party/rust/heck/v0_3/crate/src/lib.rs b/third_party/rust/heck/v0_4/crate/src/lib.rs similarity index 71% rename from third_party/rust/heck/v0_3/crate/src/lib.rs rename to third_party/rust/heck/v0_4/crate/src/lib.rs index 00a9fbea..21a45f0 100644 --- a/third_party/rust/heck/v0_3/crate/src/lib.rs +++ b/third_party/rust/heck/v0_4/crate/src/lib.rs
@@ -30,37 +30,57 @@ //! //! ### Cases contained in this library: //! -//! 1. CamelCase -//! 2. snake_case -//! 3. kebab-case -//! 4. SHOUTY_SNAKE_CASE -//! 5. mixedCase +//! 1. UpperCamelCase +//! 2. lowerCamelCase +//! 3. snake_case +//! 4. kebab-case +//! 5. SHOUTY_SNAKE_CASE //! 6. Title Case //! 7. SHOUTY-KEBAB-CASE #![deny(missing_docs)] +#![forbid(unsafe_code)] -mod camel; mod kebab; -mod mixed; +mod lower_camel; mod shouty_kebab; mod shouty_snake; mod snake; mod title; +mod upper_camel; -pub use camel::CamelCase; -pub use kebab::KebabCase; -pub use mixed::MixedCase; -pub use shouty_kebab::ShoutyKebabCase; -pub use shouty_snake::{ShoutySnakeCase, ShoutySnekCase}; -pub use snake::{SnakeCase, SnekCase}; -pub use title::TitleCase; +pub use kebab::{AsKebabCase, ToKebabCase}; +pub use lower_camel::{AsLowerCamelCase, ToLowerCamelCase}; +pub use shouty_kebab::{AsShoutyKebabCase, ToShoutyKebabCase}; +pub use shouty_snake::{ + AsShoutySnakeCase, AsShoutySnakeCase as AsShoutySnekCase, ToShoutySnakeCase, ToShoutySnekCase, +}; +pub use snake::{AsSnakeCase, AsSnakeCase as AsSnekCase, ToSnakeCase, ToSnekCase}; +pub use title::{AsTitleCase, ToTitleCase}; +pub use upper_camel::{ + AsUpperCamelCase, AsUpperCamelCase as AsPascalCase, ToPascalCase, ToUpperCamelCase, +}; -use unicode_segmentation::UnicodeSegmentation; +use std::fmt; -fn transform<F, G>(s: &str, with_word: F, boundary: G) -> String +#[cfg(feature = "unicode")] +fn get_iterator(s: &str) -> unicode_segmentation::UnicodeWords { + use unicode_segmentation::UnicodeSegmentation; + s.unicode_words() +} +#[cfg(not(feature = "unicode"))] +fn get_iterator(s: &str) -> impl Iterator<Item = &str> { + s.split(|letter: char| !letter.is_ascii_alphanumeric()) +} + +fn transform<F, G>( + s: &str, + mut with_word: F, + mut boundary: G, + f: &mut fmt::Formatter, +) -> fmt::Result where - F: Fn(&str, &mut String), - G: Fn(&mut String), + F: FnMut(&str, &mut fmt::Formatter) -> fmt::Result, + G: FnMut(&mut fmt::Formatter) -> fmt::Result, { /// Tracks the current 'mode' of the transformation algorithm as it scans /// the input string. @@ -82,10 +102,9 @@ Uppercase, } - let mut out = String::new(); let mut first_word = true; - for word in s.unicode_words() { + for word in get_iterator(s) { let mut char_indices = word.char_indices().peekable(); let mut init = 0; let mut mode = WordMode::Boundary; @@ -114,9 +133,9 @@ // not uppercase and next is uppercase if next == '_' || (next_mode == WordMode::Lowercase && next.is_uppercase()) { if !first_word { - boundary(&mut out); + boundary(f)?; } - with_word(&word[init..next_i], &mut out); + with_word(&word[init..next_i], f)?; first_word = false; init = next_i; mode = WordMode::Boundary; @@ -125,11 +144,11 @@ // is lowercase, word boundary before } else if mode == WordMode::Uppercase && c.is_uppercase() && next.is_lowercase() { if !first_word { - boundary(&mut out); + boundary(f)?; } else { first_word = false; } - with_word(&word[init..i], &mut out); + with_word(&word[init..i], f)?; init = i; mode = WordMode::Boundary; @@ -140,42 +159,48 @@ } else { // Collect trailing characters as a word if !first_word { - boundary(&mut out); + boundary(f)?; } else { first_word = false; } - with_word(&word[init..], &mut out); + with_word(&word[init..], f)?; break; } } } - out + Ok(()) } -fn lowercase(s: &str, out: &mut String) { +fn lowercase(s: &str, f: &mut fmt::Formatter) -> fmt::Result { let mut chars = s.chars().peekable(); while let Some(c) = chars.next() { if c == 'Σ' && chars.peek().is_none() { - out.push('ς'); + write!(f, "ς")?; } else { - out.extend(c.to_lowercase()); + write!(f, "{}", c.to_lowercase())?; } } + + Ok(()) } -fn uppercase(s: &str, out: &mut String) { +fn uppercase(s: &str, f: &mut fmt::Formatter) -> fmt::Result { for c in s.chars() { - out.extend(c.to_uppercase()) + write!(f, "{}", c.to_uppercase())?; } + + Ok(()) } -fn capitalize(s: &str, out: &mut String) { +fn capitalize(s: &str, f: &mut fmt::Formatter) -> fmt::Result { let mut char_indices = s.char_indices(); if let Some((_, c)) = char_indices.next() { - out.extend(c.to_uppercase()); + write!(f, "{}", c.to_uppercase())?; if let Some((i, _)) = char_indices.next() { - lowercase(&s[i..], out); + lowercase(&s[i..], f)?; } } + + Ok(()) }
diff --git a/third_party/rust/heck/v0_4/crate/src/lower_camel.rs b/third_party/rust/heck/v0_4/crate/src/lower_camel.rs new file mode 100644 index 0000000..f1d6c94c --- /dev/null +++ b/third_party/rust/heck/v0_4/crate/src/lower_camel.rs
@@ -0,0 +1,85 @@ +use std::fmt; + +use crate::{capitalize, lowercase, transform}; + +/// This trait defines a lower camel case conversion. +/// +/// In lowerCamelCase, word boundaries are indicated by capital letters, +/// excepting the first word. +/// +/// ## Example: +/// +/// ```rust +/// use heck::ToLowerCamelCase; +/// +/// let sentence = "It is we who built these palaces and cities."; +/// assert_eq!(sentence.to_lower_camel_case(), "itIsWeWhoBuiltThesePalacesAndCities"); +/// ``` +pub trait ToLowerCamelCase: ToOwned { + /// Convert this type to lower camel case. + fn to_lower_camel_case(&self) -> Self::Owned; +} + +impl ToLowerCamelCase for str { + fn to_lower_camel_case(&self) -> String { + AsLowerCamelCase(self).to_string() + } +} + +/// This wrapper performs a lower camel case conversion in [`fmt::Display`]. +/// +/// ## Example: +/// +/// ``` +/// use heck::AsLowerCamelCase; +/// +/// let sentence = "It is we who built these palaces and cities."; +/// assert_eq!(format!("{}", AsLowerCamelCase(sentence)), "itIsWeWhoBuiltThesePalacesAndCities"); +/// ``` +pub struct AsLowerCamelCase<T: AsRef<str>>(pub T); + +impl<T: AsRef<str>> fmt::Display for AsLowerCamelCase<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut first = true; + transform( + self.0.as_ref(), + |s, f| { + if first { + first = false; + lowercase(s, f) + } else { + capitalize(s, f) + } + }, + |_| Ok(()), + f, + ) + } +} + +#[cfg(test)] +mod tests { + use super::ToLowerCamelCase; + + macro_rules! t { + ($t:ident : $s1:expr => $s2:expr) => { + #[test] + fn $t() { + assert_eq!($s1.to_lower_camel_case(), $s2) + } + }; + } + + t!(test1: "CamelCase" => "camelCase"); + t!(test2: "This is Human case." => "thisIsHumanCase"); + t!(test3: "MixedUP CamelCase, with some Spaces" => "mixedUpCamelCaseWithSomeSpaces"); + t!(test4: "mixed_up_ snake_case, with some _spaces" => "mixedUpSnakeCaseWithSomeSpaces"); + t!(test5: "kebab-case" => "kebabCase"); + t!(test6: "SHOUTY_SNAKE_CASE" => "shoutySnakeCase"); + t!(test7: "snake_case" => "snakeCase"); + t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "thisContainsAllKindsOfWordBoundaries"); + #[cfg(feature = "unicode")] + t!(test9: "XΣXΣ baffle" => "xσxςBaffle"); + t!(test10: "XMLHttpRequest" => "xmlHttpRequest"); + // TODO unicode tests +}
diff --git a/third_party/rust/heck/v0_3/crate/src/shouty_kebab.rs b/third_party/rust/heck/v0_4/crate/src/shouty_kebab.rs similarity index 65% rename from third_party/rust/heck/v0_3/crate/src/shouty_kebab.rs rename to third_party/rust/heck/v0_4/crate/src/shouty_kebab.rs index 0225471..e679978 100644 --- a/third_party/rust/heck/v0_3/crate/src/shouty_kebab.rs +++ b/third_party/rust/heck/v0_4/crate/src/shouty_kebab.rs
@@ -1,3 +1,5 @@ +use std::fmt; + use crate::{transform, uppercase}; /// This trait defines a shouty kebab case conversion. @@ -8,25 +10,43 @@ /// ## Example: /// /// ```rust -/// use heck::ShoutyKebabCase; +/// use heck::ToShoutyKebabCase; /// /// let sentence = "We are going to inherit the earth."; /// assert_eq!(sentence.to_shouty_kebab_case(), "WE-ARE-GOING-TO-INHERIT-THE-EARTH"); /// ``` -pub trait ShoutyKebabCase: ToOwned { +pub trait ToShoutyKebabCase: ToOwned { /// Convert this type to shouty kebab case. fn to_shouty_kebab_case(&self) -> Self::Owned; } -impl ShoutyKebabCase for str { +impl ToShoutyKebabCase for str { fn to_shouty_kebab_case(&self) -> Self::Owned { - transform(self, uppercase, |s| s.push('-')) + AsShoutyKebabCase(self).to_string() + } +} + +/// This wrapper performs a kebab case conversion in [`fmt::Display`]. +/// +/// ## Example: +/// +/// ``` +/// use heck::AsShoutyKebabCase; +/// +/// let sentence = "We are going to inherit the earth."; +/// assert_eq!(format!("{}", AsShoutyKebabCase(sentence)), "WE-ARE-GOING-TO-INHERIT-THE-EARTH"); +/// ``` +pub struct AsShoutyKebabCase<T: AsRef<str>>(pub T); + +impl<T: AsRef<str>> fmt::Display for AsShoutyKebabCase<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + transform(self.0.as_ref(), uppercase, |f| write!(f, "-"), f) } } #[cfg(test)] mod tests { - use super::ShoutyKebabCase; + use super::ToShoutyKebabCase; macro_rules! t { ($t:ident : $s1:expr => $s2:expr) => { @@ -45,6 +65,7 @@ t!(test6: "SHOUTY_SNAKE_CASE" => "SHOUTY-SNAKE-CASE"); t!(test7: "snake_case" => "SNAKE-CASE"); t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "THIS-CONTAINS-ALL-KINDS-OF-WORD-BOUNDARIES"); + #[cfg(feature = "unicode")] t!(test9: "XΣXΣ baffle" => "XΣXΣ-BAFFLE"); t!(test10: "XMLHttpRequest" => "XML-HTTP-REQUEST"); t!(test11: "SHOUTY-KEBAB-CASE" => "SHOUTY-KEBAB-CASE");
diff --git a/third_party/rust/heck/v0_3/crate/src/shouty_snake.rs b/third_party/rust/heck/v0_4/crate/src/shouty_snake.rs similarity index 62% rename from third_party/rust/heck/v0_3/crate/src/shouty_snake.rs rename to third_party/rust/heck/v0_4/crate/src/shouty_snake.rs index 8f4289a0..d5043755 100644 --- a/third_party/rust/heck/v0_3/crate/src/shouty_snake.rs +++ b/third_party/rust/heck/v0_4/crate/src/shouty_snake.rs
@@ -1,3 +1,5 @@ +use std::fmt; + use crate::{transform, uppercase}; /// This trait defines a shouty snake case conversion. @@ -8,39 +10,57 @@ /// ## Example: /// /// ```rust -/// use heck::ShoutySnakeCase; -/// +/// use heck::ToShoutySnakeCase; +/// /// let sentence = "That world is growing in this minute."; /// assert_eq!(sentence.to_shouty_snake_case(), "THAT_WORLD_IS_GROWING_IN_THIS_MINUTE"); /// ``` -pub trait ShoutySnakeCase: ToOwned { +pub trait ToShoutySnakeCase: ToOwned { /// Convert this type to shouty snake case. fn to_shouty_snake_case(&self) -> Self::Owned; } -/// Oh heck, ShoutySnekCase is an alias for ShoutySnakeCase. See ShoutySnakeCase -/// for more documentation. -pub trait ShoutySnekCase: ToOwned { +/// Oh heck, ToShoutySnekCase is an alias for ToShoutySnakeCase. See +/// ToShoutySnakeCase for more documentation. +pub trait ToShoutySnekCase: ToOwned { /// CONVERT THIS TYPE TO SNEK CASE. #[allow(non_snake_case)] fn TO_SHOUTY_SNEK_CASE(&self) -> Self::Owned; } -impl<T: ?Sized + ShoutySnakeCase> ShoutySnekCase for T { +impl<T: ?Sized + ToShoutySnakeCase> ToShoutySnekCase for T { fn TO_SHOUTY_SNEK_CASE(&self) -> Self::Owned { self.to_shouty_snake_case() } } -impl ShoutySnakeCase for str { +impl ToShoutySnakeCase for str { fn to_shouty_snake_case(&self) -> Self::Owned { - transform(self, uppercase, |s| s.push('_')) + AsShoutySnakeCase(self).to_string() + } +} + +/// This wrapper performs a shouty snake case conversion in [`fmt::Display`]. +/// +/// ## Example: +/// +/// ``` +/// use heck::AsShoutySnakeCase; +/// +/// let sentence = "That world is growing in this minute."; +/// assert_eq!(format!("{}", AsShoutySnakeCase(sentence)), "THAT_WORLD_IS_GROWING_IN_THIS_MINUTE"); +/// ``` +pub struct AsShoutySnakeCase<T: AsRef<str>>(pub T); + +impl<T: AsRef<str>> fmt::Display for AsShoutySnakeCase<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + transform(self.0.as_ref(), uppercase, |f| write!(f, "_"), f) } } #[cfg(test)] mod tests { - use super::ShoutySnakeCase; + use super::ToShoutySnakeCase; macro_rules! t { ($t:ident : $s1:expr => $s2:expr) => { @@ -59,6 +79,7 @@ t!(test6: "SHOUTY_SNAKE_CASE" => "SHOUTY_SNAKE_CASE"); t!(test7: "snake_case" => "SNAKE_CASE"); t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "THIS_CONTAINS_ALL_KINDS_OF_WORD_BOUNDARIES"); + #[cfg(feature = "unicode")] t!(test9: "XΣXΣ baffle" => "XΣXΣ_BAFFLE"); t!(test10: "XMLHttpRequest" => "XML_HTTP_REQUEST"); }
diff --git a/third_party/rust/heck/v0_3/crate/src/snake.rs b/third_party/rust/heck/v0_4/crate/src/snake.rs similarity index 71% rename from third_party/rust/heck/v0_3/crate/src/snake.rs rename to third_party/rust/heck/v0_4/crate/src/snake.rs index c1ad376..127a8642 100644 --- a/third_party/rust/heck/v0_3/crate/src/snake.rs +++ b/third_party/rust/heck/v0_4/crate/src/snake.rs
@@ -1,3 +1,5 @@ +use std::fmt; + use crate::{lowercase, transform}; /// This trait defines a snake case conversion. @@ -7,38 +9,56 @@ /// ## Example: /// /// ```rust -/// use heck::SnakeCase; +/// use heck::ToSnakeCase; /// /// let sentence = "We carry a new world here, in our hearts."; /// assert_eq!(sentence.to_snake_case(), "we_carry_a_new_world_here_in_our_hearts"); /// ``` -pub trait SnakeCase: ToOwned { +pub trait ToSnakeCase: ToOwned { /// Convert this type to snake case. fn to_snake_case(&self) -> Self::Owned; } -/// Oh heck, SnekCase is an alias for SnakeCase. See SnakeCase for +/// Oh heck, SnekCase is an alias for ToSnakeCase. See ToSnakeCase for /// more documentation. -pub trait SnekCase: ToOwned { +pub trait ToSnekCase: ToOwned { /// Convert this type to snek case. fn to_snek_case(&self) -> Self::Owned; } -impl<T: ?Sized + SnakeCase> SnekCase for T { +impl<T: ?Sized + ToSnakeCase> ToSnekCase for T { fn to_snek_case(&self) -> Self::Owned { self.to_snake_case() } } -impl SnakeCase for str { +impl ToSnakeCase for str { fn to_snake_case(&self) -> String { - transform(self, lowercase, |s| s.push('_')) + AsSnakeCase(self).to_string() + } +} + +/// This wrapper performs a snake case conversion in [`fmt::Display`]. +/// +/// ## Example: +/// +/// ``` +/// use heck::AsSnakeCase; +/// +/// let sentence = "We carry a new world here, in our hearts."; +/// assert_eq!(format!("{}", AsSnakeCase(sentence)), "we_carry_a_new_world_here_in_our_hearts"); +/// ``` +pub struct AsSnakeCase<T: AsRef<str>>(pub T); + +impl<T: AsRef<str>> fmt::Display for AsSnakeCase<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + transform(self.0.as_ref(), lowercase, |f| write!(f, "_"), f) } } #[cfg(test)] mod tests { - use super::SnakeCase; + use super::ToSnakeCase; macro_rules! t { ($t:ident : $s1:expr => $s2:expr) => { @@ -57,6 +77,7 @@ t!(test6: "SHOUTY_SNAKE_CASE" => "shouty_snake_case"); t!(test7: "snake_case" => "snake_case"); t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "this_contains_all_kinds_of_word_boundaries"); + #[cfg(feature = "unicode")] t!(test9: "XΣXΣ baffle" => "xσxς_baffle"); t!(test10: "XMLHttpRequest" => "xml_http_request"); t!(test11: "FIELD_NAME11" => "field_name11");
diff --git a/third_party/rust/heck/v0_3/crate/src/title.rs b/third_party/rust/heck/v0_4/crate/src/title.rs similarity index 65% rename from third_party/rust/heck/v0_3/crate/src/title.rs rename to third_party/rust/heck/v0_4/crate/src/title.rs index 015a9fa..fdf175b 100644 --- a/third_party/rust/heck/v0_3/crate/src/title.rs +++ b/third_party/rust/heck/v0_4/crate/src/title.rs
@@ -1,3 +1,5 @@ +use std::fmt; + use crate::{capitalize, transform}; /// This trait defines a title case conversion. @@ -8,25 +10,43 @@ /// ## Example: /// /// ```rust -/// use heck::TitleCase; +/// use heck::ToTitleCase; /// /// let sentence = "We have always lived in slums and holes in the wall."; /// assert_eq!(sentence.to_title_case(), "We Have Always Lived In Slums And Holes In The Wall"); /// ``` -pub trait TitleCase: ToOwned { +pub trait ToTitleCase: ToOwned { /// Convert this type to title case. fn to_title_case(&self) -> Self::Owned; } -impl TitleCase for str { +impl ToTitleCase for str { fn to_title_case(&self) -> String { - transform(self, capitalize, |s| s.push(' ')) + AsTitleCase(self).to_string() + } +} + +/// This wrapper performs a title case conversion in [`fmt::Display`]. +/// +/// ## Example: +/// +/// ``` +/// use heck::AsTitleCase; +/// +/// let sentence = "We have always lived in slums and holes in the wall."; +/// assert_eq!(format!("{}", AsTitleCase(sentence)), "We Have Always Lived In Slums And Holes In The Wall"); +/// ``` +pub struct AsTitleCase<T: AsRef<str>>(pub T); + +impl<T: AsRef<str>> fmt::Display for AsTitleCase<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + transform(self.0.as_ref(), capitalize, |f| write!(f, " "), f) } } #[cfg(test)] mod tests { - use super::TitleCase; + use super::ToTitleCase; macro_rules! t { ($t:ident : $s1:expr => $s2:expr) => { @@ -45,6 +65,7 @@ t!(test6: "SHOUTY_SNAKE_CASE" => "Shouty Snake Case"); t!(test7: "snake_case" => "Snake Case"); t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "This Contains All Kinds Of Word Boundaries"); + #[cfg(feature = "unicode")] t!(test9: "XΣXΣ baffle" => "Xσxς Baffle"); t!(test10: "XMLHttpRequest" => "Xml Http Request"); }
diff --git a/third_party/rust/heck/v0_4/crate/src/upper_camel.rs b/third_party/rust/heck/v0_4/crate/src/upper_camel.rs new file mode 100644 index 0000000..70bf4ac --- /dev/null +++ b/third_party/rust/heck/v0_4/crate/src/upper_camel.rs
@@ -0,0 +1,84 @@ +use std::fmt; + +use crate::{capitalize, transform}; + +/// This trait defines an upper camel case conversion. +/// +/// In UpperCamelCase, word boundaries are indicated by capital letters, +/// including the first word. +/// +/// ## Example: +/// +/// ```rust +/// use heck::ToUpperCamelCase; +/// +/// let sentence = "We are not in the least afraid of ruins."; +/// assert_eq!(sentence.to_upper_camel_case(), "WeAreNotInTheLeastAfraidOfRuins"); +/// ``` +pub trait ToUpperCamelCase: ToOwned { + /// Convert this type to upper camel case. + fn to_upper_camel_case(&self) -> Self::Owned; +} + +impl ToUpperCamelCase for str { + fn to_upper_camel_case(&self) -> String { + AsUpperCamelCase(self).to_string() + } +} + +/// ToPascalCase is an alias for ToUpperCamelCase. See ToUpperCamelCase for more +/// documentation. +pub trait ToPascalCase: ToOwned { + /// Convert this type to upper camel case. + fn to_pascal_case(&self) -> Self::Owned; +} + +impl<T: ?Sized + ToUpperCamelCase> ToPascalCase for T { + fn to_pascal_case(&self) -> Self::Owned { + self.to_upper_camel_case() + } +} + +/// This wrapper performs a upper camel case conversion in [`fmt::Display`]. +/// +/// ## Example: +/// +/// ``` +/// use heck::AsUpperCamelCase; +/// +/// let sentence = "We are not in the least afraid of ruins."; +/// assert_eq!(format!("{}", AsUpperCamelCase(sentence)), "WeAreNotInTheLeastAfraidOfRuins"); +/// ``` +pub struct AsUpperCamelCase<T: AsRef<str>>(pub T); + +impl<T: AsRef<str>> fmt::Display for AsUpperCamelCase<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + transform(self.0.as_ref(), capitalize, |_| Ok(()), f) + } +} + +#[cfg(test)] +mod tests { + use super::ToUpperCamelCase; + + macro_rules! t { + ($t:ident : $s1:expr => $s2:expr) => { + #[test] + fn $t() { + assert_eq!($s1.to_upper_camel_case(), $s2) + } + }; + } + + t!(test1: "CamelCase" => "CamelCase"); + t!(test2: "This is Human case." => "ThisIsHumanCase"); + t!(test3: "MixedUP_CamelCase, with some Spaces" => "MixedUpCamelCaseWithSomeSpaces"); + t!(test4: "mixed_up_ snake_case, with some _spaces" => "MixedUpSnakeCaseWithSomeSpaces"); + t!(test5: "kebab-case" => "KebabCase"); + t!(test6: "SHOUTY_SNAKE_CASE" => "ShoutySnakeCase"); + t!(test7: "snake_case" => "SnakeCase"); + t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "ThisContainsAllKindsOfWordBoundaries"); + #[cfg(feature = "unicode")] + t!(test9: "XΣXΣ baffle" => "XσxςBaffle"); + t!(test10: "XMLHttpRequest" => "XmlHttpRequest"); +}
diff --git a/third_party/rust/strum_macros/v0_23/crate/.cargo_vcs_info.json b/third_party/rust/strum_macros/v0_23/crate/.cargo_vcs_info.json deleted file mode 100644 index 72cacbd..0000000 --- a/third_party/rust/strum_macros/v0_23/crate/.cargo_vcs_info.json +++ /dev/null
@@ -1,5 +0,0 @@ -{ - "git": { - "sha1": "5d733fd1f35690c51b43d20296548253c77665ac" - } -}
diff --git a/third_party/rust/strum_macros/v0_23/BUILD.gn b/third_party/rust/strum_macros/v0_24/BUILD.gn similarity index 93% rename from third_party/rust/strum_macros/v0_23/BUILD.gn rename to third_party/rust/strum_macros/v0_24/BUILD.gn index f570764..16141e04 100644 --- a/third_party/rust/strum_macros/v0_23/BUILD.gn +++ b/third_party/rust/strum_macros/v0_24/BUILD.gn
@@ -6,7 +6,7 @@ cargo_crate("lib") { crate_name = "strum_macros" - epoch = "0.23" + epoch = "0.24" crate_type = "proc-macro" # Only for usage from third-party crates. Add the crate to @@ -19,7 +19,7 @@ sources = [ "crate/src/lib.rs" ] edition = "2018" deps = [ - "//third_party/rust/heck/v0_3:lib", + "//third_party/rust/heck/v0_4:lib", "//third_party/rust/proc_macro2/v1:lib", "//third_party/rust/quote/v1:lib", "//third_party/rust/rustversion/v1:lib",
diff --git a/third_party/rust/strum_macros/v0_23/README.chromium b/third_party/rust/strum_macros/v0_24/README.chromium similarity index 77% rename from third_party/rust/strum_macros/v0_23/README.chromium rename to third_party/rust/strum_macros/v0_24/README.chromium index e156d8df..2181401e 100644 --- a/third_party/rust/strum_macros/v0_23/README.chromium +++ b/third_party/rust/strum_macros/v0_24/README.chromium
@@ -1,6 +1,6 @@ Name: strum_macros URL: https://crates.io/crates/strum_macros Description: Helpful macros for working with enums and strings -Version: 0.23.1 -Security Critical: no +Version: 0.24.0 +Security Critical: yes License: MIT
diff --git a/third_party/rust/strum_macros/v0_24/crate/.cargo_vcs_info.json b/third_party/rust/strum_macros/v0_24/crate/.cargo_vcs_info.json new file mode 100644 index 0000000..d913ec6 --- /dev/null +++ b/third_party/rust/strum_macros/v0_24/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "94460f006cc6ecce6fe77262a073372d283ad0fe" + }, + "path_in_vcs": "strum_macros" +} \ No newline at end of file
diff --git a/third_party/rust/strum_macros/v0_23/crate/Cargo.toml b/third_party/rust/strum_macros/v0_24/crate/Cargo.toml similarity index 95% rename from third_party/rust/strum_macros/v0_23/crate/Cargo.toml rename to third_party/rust/strum_macros/v0_24/crate/Cargo.toml index 61ecf76..be175e68 100644 --- a/third_party/rust/strum_macros/v0_23/crate/Cargo.toml +++ b/third_party/rust/strum_macros/v0_24/crate/Cargo.toml
@@ -12,7 +12,7 @@ [package] edition = "2018" name = "strum_macros" -version = "0.23.1" +version = "0.24.0" authors = ["Peter Glotfelty <peter.glotfelty@microsoft.com>"] description = "Helpful macros for working with enums and strings" homepage = "https://github.com/Peternator7/strum" @@ -27,7 +27,7 @@ name = "strum_macros" proc-macro = true [dependencies.heck] -version = "0.3" +version = "0.4" [dependencies.proc-macro2] version = "1.0" @@ -42,4 +42,4 @@ version = "1.0" features = ["parsing", "extra-traits"] [dev-dependencies.strum] -version = "0.20" +version = "0.23"
diff --git a/third_party/rust/strum_macros/v0_23/crate/Cargo.toml.orig b/third_party/rust/strum_macros/v0_24/crate/Cargo.toml.orig similarity index 93% rename from third_party/rust/strum_macros/v0_23/crate/Cargo.toml.orig rename to third_party/rust/strum_macros/v0_24/crate/Cargo.toml.orig index 140f2e9..8dff380 100644 --- a/third_party/rust/strum_macros/v0_23/crate/Cargo.toml.orig +++ b/third_party/rust/strum_macros/v0_24/crate/Cargo.toml.orig
@@ -1,6 +1,6 @@ [package] name = "strum_macros" -version = "0.23.1" +version = "0.24.0" edition = "2018" authors = ["Peter Glotfelty <peter.glotfelty@microsoft.com>"] license = "MIT" @@ -19,11 +19,11 @@ name = "strum_macros" [dependencies] -heck = "0.3" +heck = "0.4" proc-macro2 = "1.0" quote = "1.0" rustversion = "1.0" syn = { version = "1.0", features = ["parsing", "extra-traits"] } [dev-dependencies] -strum = "0.20" +strum = "0.23"
diff --git a/third_party/rust/strum_macros/v0_23/crate/LICENSE b/third_party/rust/strum_macros/v0_24/crate/LICENSE similarity index 100% rename from third_party/rust/strum_macros/v0_23/crate/LICENSE rename to third_party/rust/strum_macros/v0_24/crate/LICENSE
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/helpers/case_style.rs b/third_party/rust/strum_macros/v0_24/crate/src/helpers/case_style.rs similarity index 88% rename from third_party/rust/strum_macros/v0_23/crate/src/helpers/case_style.rs rename to third_party/rust/strum_macros/v0_24/crate/src/helpers/case_style.rs index 352a277..42538260 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/helpers/case_style.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/helpers/case_style.rs
@@ -1,10 +1,13 @@ -use heck::{CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase, TitleCase}; +use heck::{ + ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase, ToUpperCamelCase, +}; use std::str::FromStr; use syn::{ parse::{Parse, ParseStream}, Ident, LitStr, }; +#[allow(clippy::enum_variant_names)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum CaseStyle { CamelCase, @@ -52,7 +55,7 @@ impl FromStr for CaseStyle { type Err = (); - fn from_str(text: &str) -> Result<CaseStyle, ()> { + fn from_str(text: &str) -> Result<Self, ()> { Ok(match text { "camel_case" | "PascalCase" => CaseStyle::PascalCase, "camelCase" => CaseStyle::CamelCase, @@ -80,9 +83,9 @@ let ident_string = self.to_string(); if let Some(case_style) = case_style { match case_style { - CaseStyle::PascalCase => ident_string.to_camel_case(), + CaseStyle::PascalCase => ident_string.to_upper_camel_case(), CaseStyle::KebabCase => ident_string.to_kebab_case(), - CaseStyle::MixedCase => ident_string.to_mixed_case(), + CaseStyle::MixedCase => ident_string.to_lower_camel_case(), CaseStyle::ShoutySnakeCase => ident_string.to_shouty_snake_case(), CaseStyle::SnakeCase => ident_string.to_snake_case(), CaseStyle::TitleCase => ident_string.to_title_case(), @@ -90,7 +93,7 @@ CaseStyle::LowerCase => ident_string.to_lowercase(), CaseStyle::ScreamingKebabCase => ident_string.to_kebab_case().to_uppercase(), CaseStyle::CamelCase => { - let camel_case = ident_string.to_camel_case(); + let camel_case = ident_string.to_upper_camel_case(); let mut pascal = String::with_capacity(camel_case.len()); let mut it = camel_case.chars(); if let Some(ch) = it.next() {
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/helpers/metadata.rs b/third_party/rust/strum_macros/v0_24/crate/src/helpers/metadata.rs similarity index 91% rename from third_party/rust/strum_macros/v0_23/crate/src/helpers/metadata.rs rename to third_party/rust/strum_macros/v0_24/crate/src/helpers/metadata.rs index 6279e6a..08ca384 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/helpers/metadata.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/helpers/metadata.rs
@@ -5,7 +5,7 @@ parse2, parse_str, punctuated::Punctuated, spanned::Spanned, - Attribute, DeriveInput, Ident, LitBool, LitStr, Path, Token, Variant, Visibility, + Attribute, DeriveInput, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue, Path, Token, Variant, Visibility, }; use super::case_style::CaseStyle; @@ -137,7 +137,7 @@ /// Get all the strum metadata associated with an enum. fn get_metadata(&self) -> syn::Result<Vec<EnumMeta>>; - /// Get all the strum_discriminants metadata associated with an enum. + /// Get all the `strum_discriminants` metadata associated with an enum. fn get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>>; } @@ -164,6 +164,9 @@ kw: kw::serialize, value: LitStr, }, + Documentation { + value: LitStr, + }, ToString { kw: kw::to_string, value: LitStr, @@ -240,7 +243,7 @@ fn parse(input: ParseStream) -> syn::Result<Self> { use syn::ext::IdentExt; - let k = Ident::parse_any(&input)?; + let k = Ident::parse_any(input)?; let _: Token![=] = input.parse()?; let v = input.parse()?; @@ -253,6 +256,7 @@ match self { VariantMeta::Message { kw, .. } => kw.span, VariantMeta::DetailedMessage { kw, .. } => kw.span, + VariantMeta::Documentation { value } => value.span(), VariantMeta::Serialize { kw, .. } => kw.span, VariantMeta::ToString { kw, .. } => kw.span, VariantMeta::Disabled(kw) => kw.span, @@ -270,7 +274,15 @@ impl VariantExt for Variant { fn get_metadata(&self) -> syn::Result<Vec<VariantMeta>> { - get_metadata_inner("strum", &self.attrs) + let result = get_metadata_inner("strum", &self.attrs)?; + self.attrs.iter() + .filter(|attr| attr.path.is_ident("doc")) + .try_fold(result, |mut vec, attr| { + if let Meta::NameValue(MetaNameValue { lit: Lit::Str(value), .. }) = attr.parse_meta()? { + vec.push(VariantMeta::Documentation { value }) + } + Ok(vec) + }) } }
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/helpers/mod.rs b/third_party/rust/strum_macros/v0_24/crate/src/helpers/mod.rs similarity index 90% rename from third_party/rust/strum_macros/v0_23/crate/src/helpers/mod.rs rename to third_party/rust/strum_macros/v0_24/crate/src/helpers/mod.rs index 30787b28..11aebc8 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/helpers/mod.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/helpers/mod.rs
@@ -15,7 +15,7 @@ syn::Error::new(Span::call_site(), "This macro only supports enums.") } -pub fn strum_discriminants_passthrough_error(span: impl Spanned) -> syn::Error { +pub fn strum_discriminants_passthrough_error(span: &impl Spanned) -> syn::Error { syn::Error::new( span.span(), "expected a pass-through attribute, e.g. #[strum_discriminants(serde(rename = \"var0\"))]",
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/helpers/type_props.rs b/third_party/rust/strum_macros/v0_24/crate/src/helpers/type_props.rs similarity index 95% rename from third_party/rust/strum_macros/v0_23/crate/src/helpers/type_props.rs rename to third_party/rust/strum_macros/v0_24/crate/src/helpers/type_props.rs index 39b0a2bd..cdca79f3 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/helpers/type_props.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/helpers/type_props.rs
@@ -99,10 +99,8 @@ impl StrumTypeProperties { pub fn crate_module_path(&self) -> Path { - if let Some(path) = &self.crate_module_path { - parse_quote!(#path) - } else { - parse_quote!(::strum) - } + self.crate_module_path + .as_ref() + .map_or_else(|| parse_quote!(::strum), |path| parse_quote!(#path)) } }
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/helpers/variant_props.rs b/third_party/rust/strum_macros/v0_24/crate/src/helpers/variant_props.rs similarity index 87% rename from third_party/rust/strum_macros/v0_23/crate/src/helpers/variant_props.rs rename to third_party/rust/strum_macros/v0_24/crate/src/helpers/variant_props.rs index a7e948622..a39d3ea1 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/helpers/variant_props.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/helpers/variant_props.rs
@@ -16,6 +16,7 @@ pub ascii_case_insensitive: Option<bool>, pub message: Option<LitStr>, pub detailed_message: Option<LitStr>, + pub documentation: Vec<LitStr>, pub string_props: Vec<(LitStr, LitStr)>, serialize: Vec<LitStr>, to_string: Option<LitStr>, @@ -29,17 +30,13 @@ } pub fn get_preferred_name(&self, case_style: Option<CaseStyle>) -> LitStr { - if let Some(to_string) = &self.to_string { - to_string.clone() - } else { - let mut serialized = self.serialize.clone(); - serialized.sort_by_key(|s| s.value().len()); - if let Some(n) = serialized.pop() { - n - } else { - self.ident_as_str(case_style) - } - } + self.to_string.as_ref().cloned().unwrap_or_else(|| { + self.serialize + .iter() + .max_by_key(|s| s.value().len()) + .cloned() + .unwrap_or_else(|| self.ident_as_str(case_style)) + }) } pub fn get_serializations(&self, case_style: Option<CaseStyle>) -> Vec<LitStr> { @@ -58,8 +55,10 @@ impl HasStrumVariantProperties for Variant { fn get_variant_properties(&self) -> syn::Result<StrumVariantProperties> { - let mut output = StrumVariantProperties::default(); - output.ident = Some(self.ident.clone()); + let mut output = StrumVariantProperties { + ident: Some(self.ident.clone()), + ..Default::default() + }; let mut message_kw = None; let mut detailed_message_kw = None; @@ -85,6 +84,9 @@ detailed_message_kw = Some(kw); output.detailed_message = Some(value); } + VariantMeta::Documentation { value } => { + output.documentation.push(value); + } VariantMeta::Serialize { value, .. } => { output.serialize.push(value); }
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/lib.rs b/third_party/rust/strum_macros/v0_24/crate/src/lib.rs similarity index 92% rename from third_party/rust/strum_macros/v0_23/crate/src/lib.rs rename to third_party/rust/strum_macros/v0_24/crate/src/lib.rs index 73185fe9..30d3edd 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/lib.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/lib.rs
@@ -30,7 +30,7 @@ /// Converts strings to enum variants based on their name. /// -/// auto-derives `std::str::FromStr` on the enum (for Rust 1.34 and above, std::convert::TryFrom<&str> +/// auto-derives `std::str::FromStr` on the enum (for Rust 1.34 and above, `std::convert::TryFrom<&str>` /// will be derived as well). Each variant of the enum will match on it's own name. /// This can be overridden using `serialize="DifferentName"` or `to_string="DifferentName"` /// on the attribute as shown below. @@ -47,7 +47,7 @@ /// See the [Additional Attributes](https://docs.rs/strum/0.22/strum/additional_attributes/index.html) /// Section for more information on using this feature. /// -/// # Example howto use EnumString +/// # Example howto use `EnumString` /// ``` /// use std::str::FromStr; /// use strum_macros::EnumString; @@ -78,14 +78,14 @@ /// impl std::str::FromStr for Color { /// type Err = ::strum::ParseError; /// -/// fn from_str(s: &str) -> ::std::result::Result<Color, Self::Err> { +/// fn from_str(s: &str) -> ::core::result::Result<Color, Self::Err> { /// match s { -/// "Red" => ::std::result::Result::Ok(Color::Red), -/// "Green" => ::std::result::Result::Ok(Color::Green { range:Default::default() }), -/// "blue" => ::std::result::Result::Ok(Color::Blue(Default::default())), -/// "b" => ::std::result::Result::Ok(Color::Blue(Default::default())), -/// s if s.eq_ignore_ascii_case("Black") => ::std::result::Result::Ok(Color::Black), -/// _ => ::std::result::Result::Err(::strum::ParseError::VariantNotFound), +/// "Red" => ::core::result::Result::Ok(Color::Red), +/// "Green" => ::core::result::Result::Ok(Color::Green { range:Default::default() }), +/// "blue" => ::core::result::Result::Ok(Color::Blue(Default::default())), +/// "b" => ::core::result::Result::Ok(Color::Blue(Default::default())), +/// s if s.eq_ignore_ascii_case("Black") => ::core::result::Result::Ok(Color::Black), +/// _ => ::core::result::Result::Err(::strum::ParseError::VariantNotFound), /// } /// } /// } @@ -160,7 +160,7 @@ toks.into() } -/// Implements Strum::VariantNames which adds an associated constant `VARIANTS` which is an array of discriminant names. +/// Implements `Strum::VariantNames` which adds an associated constant `VARIANTS` which is an array of discriminant names. /// /// Adds an `impl` block for the `enum` that adds a static `VARIANTS` array of `&'static str` that are the discriminant names. /// This will respect the `serialize_all` attribute on the `enum` (like `#[strum(serialize_all = "snake_case")]`. @@ -201,7 +201,7 @@ let toks = macros::as_ref_str::as_static_str_inner( &ast, - macros::as_ref_str::GenerateTraitVariant::AsStaticStr, + ¯os::as_ref_str::GenerateTraitVariant::AsStaticStr, ) .unwrap_or_else(|err| err.to_compile_error()); debug_print_generated(&ast, &toks); @@ -243,7 +243,7 @@ let toks = macros::as_ref_str::as_static_str_inner( &ast, - macros::as_ref_str::GenerateTraitVariant::From, + ¯os::as_ref_str::GenerateTraitVariant::From, ) .unwrap_or_else(|err| err.to_compile_error()); debug_print_generated(&ast, &toks); @@ -471,6 +471,11 @@ /// Encode strings into the enum itself. The `strum_macros::EmumMessage` macro implements the `strum::EnumMessage` trait. /// `EnumMessage` looks for `#[strum(message="...")]` attributes on your variants. /// You can also provided a `detailed_message="..."` attribute to create a seperate more detailed message than the first. +/// +/// `EnumMessage` also exposes the variants doc comments through `get_documentation()`. This is useful in some scenarios, +/// but `get_message` should generally be preferred. Rust doc comments are intended for developer facing documentation, +/// not end user messaging. +/// /// ``` /// // You need to bring the trait into scope to use it /// use strum::EnumMessage; @@ -479,6 +484,7 @@ /// #[derive(strum_macros::EnumMessage, Debug)] /// #[allow(dead_code)] /// enum Color { +/// /// Danger color. /// #[strum(message = "Red", detailed_message = "This is very red")] /// Red, /// #[strum(message = "Simply Green")] @@ -490,18 +496,25 @@ /// // Generated code looks like more or less like this: /// /* /// impl ::strum::EnumMessage for Color { -/// fn get_message(&self) -> ::std::option::Option<&'static str> { +/// fn get_message(&self) -> ::core::option::Option<&'static str> { /// match self { -/// &Color::Red => ::std::option::Option::Some("Red"), -/// &Color::Green {..} => ::std::option::Option::Some("Simply Green"), +/// &Color::Red => ::core::option::Option::Some("Red"), +/// &Color::Green {..} => ::core::option::Option::Some("Simply Green"), /// _ => None /// } /// } /// -/// fn get_detailed_message(&self) -> ::std::option::Option<&'static str> { +/// fn get_detailed_message(&self) -> ::core::option::Option<&'static str> { /// match self { -/// &Color::Red => ::std::option::Option::Some("This is very red"), -/// &Color::Green {..}=> ::std::option::Option::Some("Simply Green"), +/// &Color::Red => ::core::option::Option::Some("This is very red"), +/// &Color::Green {..}=> ::core::option::Option::Some("Simply Green"), +/// _ => None +/// } +/// } +/// +/// fn get_documentation(&self) -> ::std::option::Option<&'static str> { +/// match self { +/// &Color::Red => ::std::option::Option::Some("Danger color."), /// _ => None /// } /// } @@ -528,6 +541,7 @@ /// let c = Color::Red; /// assert_eq!("Red", c.get_message().unwrap()); /// assert_eq!("This is very red", c.get_detailed_message().unwrap()); +/// assert_eq!("Danger color.", c.get_documentation().unwrap()); /// assert_eq!(["Red"], c.get_serializations()); /// ``` #[proc_macro_derive(EnumMessage, attributes(strum))] @@ -613,7 +627,7 @@ /// // Bring trait into scope /// use std::str::FromStr; /// use strum::{IntoEnumIterator, EnumMessage}; -/// use strum_macros::{EnumDiscriminants, EnumIter, EnumString, EnumMessage}; +/// use strum_macros::{EnumDiscriminants, EnumIter, EnumString}; /// /// #[derive(Debug)] /// struct NonDefault;
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_count.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_count.rs similarity index 100% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_count.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_count.rs
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_discriminants.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_discriminants.rs similarity index 93% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_discriminants.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_discriminants.rs index e4b9af20..66eee46b4 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_discriminants.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_discriminants.rs
@@ -1,7 +1,7 @@ use proc_macro2::{Span, TokenStream, TokenTree}; use quote::{quote, ToTokens}; use syn::parse_quote; -use syn::{Data, DeriveInput}; +use syn::{Data, DeriveInput, Fields}; use crate::helpers::{non_enum_error, strum_discriminants_passthrough_error, HasTypeProperties}; @@ -30,10 +30,7 @@ }; // Work out the name - let default_name = syn::Ident::new( - &format!("{}Discriminants", name.to_string()), - Span::call_site(), - ); + let default_name = syn::Ident::new(&format!("{}Discriminants", name), Span::call_site()); let discriminants_name = type_properties.discriminant_name.unwrap_or(default_name); let discriminants_vis = type_properties @@ -69,11 +66,11 @@ let passthrough_attribute = match passthrough_group { TokenTree::Group(ref group) => group.stream(), _ => { - return Err(strum_discriminants_passthrough_error(passthrough_group)); + return Err(strum_discriminants_passthrough_error(&passthrough_group)); } }; if passthrough_attribute.is_empty() { - return Err(strum_discriminants_passthrough_error(passthrough_group)); + return Err(strum_discriminants_passthrough_error(&passthrough_group)); } Ok(quote! { #[#passthrough_attribute] }) } else { @@ -108,14 +105,12 @@ .iter() .map(|variant| { let ident = &variant.ident; - - use syn::Fields::*; let params = match &variant.fields { - Unit => quote! {}, - Unnamed(_fields) => { + Fields::Unit => quote! {}, + Fields::Unnamed(_fields) => { quote! { (..) } } - Named(_fields) => { + Fields::Named(_fields) => { quote! { { .. } } } };
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_iter.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_iter.rs similarity index 93% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_iter.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_iter.rs index 515160c..3dc2abd 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_iter.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_iter.rs
@@ -1,6 +1,6 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; -use syn::{Data, DeriveInput, Ident}; +use syn::{Data, DeriveInput, Fields, Ident}; use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; @@ -35,21 +35,19 @@ let mut arms = Vec::new(); let mut idx = 0usize; for variant in variants { - use syn::Fields::*; - if variant.get_variant_properties()?.disabled.is_some() { continue; } let ident = &variant.ident; let params = match &variant.fields { - Unit => quote! {}, - Unnamed(fields) => { - let defaults = ::std::iter::repeat(quote!(::core::default::Default::default())) + Fields::Unit => quote! {}, + Fields::Unnamed(fields) => { + let defaults = ::core::iter::repeat(quote!(::core::default::Default::default())) .take(fields.unnamed.len()); quote! { (#(#defaults),*) } } - Named(fields) => { + Fields::Named(fields) => { let fields = fields .named .iter() @@ -67,7 +65,7 @@ let iter_name = syn::parse_str::<Ident>(&format!("{}Iter", name)).unwrap(); Ok(quote! { - #[allow(missing_docs)] + #[doc = "An iterator over the variants of [Self]"] #vis struct #iter_name #ty_generics { idx: usize, back_idx: usize,
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_messages.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_messages.rs similarity index 64% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_messages.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_messages.rs index 6e599d0..c056108 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_messages.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_messages.rs
@@ -1,6 +1,6 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{Data, DeriveInput}; +use syn::{Data, DeriveInput, Fields, LitStr}; use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; @@ -17,19 +17,20 @@ let mut arms = Vec::new(); let mut detailed_arms = Vec::new(); + let mut documentation_arms = Vec::new(); let mut serializations = Vec::new(); for variant in variants { let variant_properties = variant.get_variant_properties()?; let messages = variant_properties.message.as_ref(); let detailed_messages = variant_properties.detailed_message.as_ref(); + let documentation = &variant_properties.documentation; let ident = &variant.ident; - use syn::Fields::*; let params = match variant.fields { - Unit => quote! {}, - Unnamed(..) => quote! { (..) }, - Named(..) => quote! { {..} }, + Fields::Unit => quote! {}, + Fields::Unnamed(..) => quote! { (..) }, + Fields::Named(..) => quote! { {..} }, }; // You can't disable getting the serializations. @@ -65,10 +66,34 @@ if let Some(msg) = detailed_messages { let params = params.clone(); - // Push the simple message. + // Push the detailed message. detailed_arms .push(quote! { &#name::#ident #params => ::core::option::Option::Some(#msg) }); } + + if !documentation.is_empty() { + let params = params.clone(); + // Strip a single leading space from each documentation line. + let documentation: Vec<LitStr> = documentation.iter().map(|lit_str| { + let line = lit_str.value(); + if line.starts_with(' ') { + LitStr::new(&line.as_str()[1..], lit_str.span()) + } else { + lit_str.clone() + } + }).collect(); + if documentation.len() == 1 { + let text = &documentation[0]; + documentation_arms + .push(quote! { &#name::#ident #params => ::core::option::Option::Some(#text) }); + } else { + // Push the documentation. + documentation_arms + .push(quote! { + &#name::#ident #params => ::core::option::Option::Some(concat!(#(concat!(#documentation, "\n")),*)) + }); + } + } } if arms.len() < variants.len() { @@ -79,6 +104,10 @@ detailed_arms.push(quote! { _ => ::core::option::Option::None }); } + if documentation_arms.len() < variants.len() { + documentation_arms.push(quote! { _ => ::core::option::Option::None }); + } + Ok(quote! { impl #impl_generics #strum_module_path::EnumMessage for #name #ty_generics #where_clause { fn get_message(&self) -> ::core::option::Option<&'static str> { @@ -93,6 +122,12 @@ } } + fn get_documentation(&self) -> ::core::option::Option<&'static str> { + match self { + #(#documentation_arms),* + } + } + fn get_serializations(&self) -> &'static [&'static str] { match self { #(#serializations),*
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_properties.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_properties.rs similarity index 90% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_properties.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_properties.rs index 58265a74..0fe389e 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_properties.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_properties.rs
@@ -1,6 +1,6 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{Data, DeriveInput}; +use syn::{Data, DeriveInput, Fields}; use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; @@ -26,15 +26,14 @@ continue; } - use syn::Fields::*; let params = match variant.fields { - Unit => quote! {}, - Unnamed(..) => quote! { (..) }, - Named(..) => quote! { {..} }, + Fields::Unit => quote! {}, + Fields::Unnamed(..) => quote! { (..) }, + Fields::Named(..) => quote! { {..} }, }; for (key, value) in variant_properties.string_props { - string_arms.push(quote! { #key => ::core::option::Option::Some( #value )}) + string_arms.push(quote! { #key => ::core::option::Option::Some( #value )}); } string_arms.push(quote! { _ => ::core::option::Option::None });
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_variant_names.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_variant_names.rs similarity index 100% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_variant_names.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_variant_names.rs
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/from_repr.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/from_repr.rs similarity index 89% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/from_repr.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/from_repr.rs index 7fdb1b5d..ec9f5577 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/macros/from_repr.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/from_repr.rs
@@ -1,6 +1,7 @@ +use heck::ToShoutySnakeCase; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; -use syn::{Data, DeriveInput, PathArguments, Type, TypeParen}; +use syn::{Data, DeriveInput, Fields, PathArguments, Type, TypeParen}; use crate::helpers::{non_enum_error, HasStrumVariantProperties}; @@ -66,22 +67,20 @@ let mut has_additional_data = false; let mut prev_const_var_ident = None; for variant in variants { - use syn::Fields::*; - if variant.get_variant_properties()?.disabled.is_some() { continue; } let ident = &variant.ident; let params = match &variant.fields { - Unit => quote! {}, - Unnamed(fields) => { + Fields::Unit => quote! {}, + Fields::Unnamed(fields) => { has_additional_data = true; - let defaults = ::std::iter::repeat(quote!(::core::default::Default::default())) + let defaults = ::core::iter::repeat(quote!(::core::default::Default::default())) .take(fields.unnamed.len()); quote! { (#(#defaults),*) } } - Named(fields) => { + Fields::Named(fields) => { has_additional_data = true; let fields = fields .named @@ -91,7 +90,6 @@ } }; - use heck::ShoutySnakeCase; let const_var_str = format!("{}_DISCRIMINANT", variant.ident).to_shouty_snake_case(); let const_var_ident = format_ident!("{}", const_var_str); @@ -115,7 +113,7 @@ quote! {} } else { #[rustversion::before(1.46)] - fn filter_by_rust_version(s: TokenStream) -> TokenStream { + fn filter_by_rust_version(_: TokenStream) -> TokenStream { quote! {} } @@ -127,7 +125,9 @@ }; Ok(quote! { + #[allow(clippy::use_self)] impl #impl_generics #name #ty_generics #where_clause { + #[doc = "Try to create [Self] from the raw representation"] #vis #const_if_possible fn from_repr(discriminant: #discriminant_type) -> Option<#name #ty_generics> { #(#constant_defs)* match discriminant {
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/mod.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/mod.rs similarity index 100% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/mod.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/mod.rs
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/as_ref_str.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/as_ref_str.rs similarity index 93% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/strings/as_ref_str.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/strings/as_ref_str.rs index b487b36..4dfdc4b8 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/as_ref_str.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/as_ref_str.rs
@@ -1,6 +1,6 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{parse_quote, Data, DeriveInput}; +use syn::{parse_quote, Data, DeriveInput, Fields}; use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; @@ -15,7 +15,6 @@ let type_properties = ast.get_type_properties()?; for variant in variants { - use syn::Fields::*; let ident = &variant.ident; let variant_properties = variant.get_variant_properties()?; @@ -28,9 +27,9 @@ // (i.e. always `enum.as_ref().to_string() == enum.to_string()`). let output = variant_properties.get_preferred_name(type_properties.case_style); let params = match variant.fields { - Unit => quote! {}, - Unnamed(..) => quote! { (..) }, - Named(..) => quote! { {..} }, + Fields::Unit => quote! {}, + Fields::Unnamed(..) => quote! { (..) }, + Fields::Named(..) => quote! { {..} }, }; arms.push(quote! { #name::#ident #params => #output }); @@ -70,7 +69,7 @@ pub fn as_static_str_inner( ast: &DeriveInput, - trait_variant: GenerateTraitVariant, + trait_variant: &GenerateTraitVariant, ) -> syn::Result<TokenStream> { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/display.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/display.rs similarity index 100% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/strings/display.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/strings/display.rs
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/from_string.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/from_string.rs similarity index 91% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/strings/from_string.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/strings/from_string.rs index 526472e..d0754d2 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/from_string.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/from_string.rs
@@ -69,7 +69,7 @@ Fields::Unit => quote! {}, Fields::Unnamed(fields) => { let defaults = - ::std::iter::repeat(quote!(Default::default())).take(fields.unnamed.len()); + ::core::iter::repeat(quote!(Default::default())).take(fields.unnamed.len()); quote! { (#(#defaults),*) } } Fields::Named(fields) => { @@ -100,10 +100,10 @@ let try_from_str = try_from_str( name, - impl_generics, - ty_generics, + &impl_generics, + &ty_generics, where_clause, - strum_module_path, + &strum_module_path, ); Ok(quote! { @@ -115,10 +115,10 @@ #[rustversion::before(1.34)] fn try_from_str( _name: &proc_macro2::Ident, - _impl_generics: syn::ImplGenerics, - _ty_generics: syn::TypeGenerics, + _impl_generics: &syn::ImplGenerics, + _ty_generics: &syn::TypeGenerics, _where_clause: Option<&syn::WhereClause>, - _strum_module_path: syn::Path, + _strum_module_path: &syn::Path, ) -> TokenStream { Default::default() } @@ -126,10 +126,10 @@ #[rustversion::since(1.34)] fn try_from_str( name: &proc_macro2::Ident, - impl_generics: syn::ImplGenerics, - ty_generics: syn::TypeGenerics, + impl_generics: &syn::ImplGenerics, + ty_generics: &syn::TypeGenerics, where_clause: Option<&syn::WhereClause>, - strum_module_path: syn::Path, + strum_module_path: &syn::Path, ) -> TokenStream { quote! { #[allow(clippy::use_self)]
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/mod.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/mod.rs similarity index 100% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/strings/mod.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/strings/mod.rs
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/to_string.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/to_string.rs similarity index 89% rename from third_party/rust/strum_macros/v0_23/crate/src/macros/strings/to_string.rs rename to third_party/rust/strum_macros/v0_24/crate/src/macros/strings/to_string.rs index a22b674..7e4c483 100644 --- a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/to_string.rs +++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/to_string.rs
@@ -1,6 +1,6 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{Data, DeriveInput}; +use syn::{Data, DeriveInput, Fields}; use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; @@ -15,7 +15,6 @@ let type_properties = ast.get_type_properties()?; let mut arms = Vec::new(); for variant in variants { - use syn::Fields::*; let ident = &variant.ident; let variant_properties = variant.get_variant_properties()?; @@ -27,9 +26,9 @@ let output = variant_properties.get_preferred_name(type_properties.case_style); let params = match variant.fields { - Unit => quote! {}, - Unnamed(..) => quote! { (..) }, - Named(..) => quote! { {..} }, + Fields::Unit => quote! {}, + Fields::Unnamed(..) => quote! { (..) }, + Fields::Named(..) => quote! { {..} }, }; arms.push(quote! { #name::#ident #params => ::std::string::String::from(#output) });
diff --git a/third_party/rust/third_party.toml b/third_party/rust/third_party.toml index 069cd62..d0cee63 100644 --- a/third_party/rust/third_party.toml +++ b/third_party/rust/third_party.toml
@@ -37,8 +37,11 @@ cxxbridge-cmd = "1" cxx = "1" serde = "1" -autocxx = "0.16" -autocxx-gen = "0.16" +autocxx = "0.17" + +[dependencies.autocxx-gen] +version = "0.17" +features = [ "static" ] [dependencies.serde_json_lenient] version = "0.1"
diff --git a/third_party/wpt_tools/README.chromium b/third_party/wpt_tools/README.chromium index 636339fd..858cdf1 100644 --- a/third_party/wpt_tools/README.chromium +++ b/third_party/wpt_tools/README.chromium
@@ -1,7 +1,7 @@ Name: web-platform-tests - Test Suites for Web Platform specifications Short Name: wpt URL: https://github.com/web-platform-tests/wpt/ -Version: e0a72ad1cebe5f0cb794627777754decb3597e40 +Version: 87c66f4e4498996d2b1d9cd2e0cf9c0f0a3f6a3d License: LICENSES FOR W3C TEST SUITES (https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html) License File: NOT_SHIPPED Security Critical: no
diff --git a/third_party/wpt_tools/wpt/resources/testdriver-actions.js b/third_party/wpt_tools/wpt/resources/testdriver-actions.js index ef097961..3e5ba74 100644 --- a/third_party/wpt_tools/wpt/resources/testdriver-actions.js +++ b/third_party/wpt_tools/wpt/resources/testdriver-actions.js
@@ -29,7 +29,7 @@ * .keyDown("p") * .keyUp("p"); * - * actions.send(); + * await actions.send(); * * @param {number} [defaultTickDuration] - The default duration of a * tick. Be default this is set ot 16ms, which is one frame time
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_edge.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_edge.txt index 6276db1..01e467a 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_edge.txt +++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_edge.txt
@@ -1 +1 @@ -selenium==4.1.2 +selenium==4.1.3
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_ie.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_ie.txt index b385a35..a829279 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_ie.txt +++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_ie.txt
@@ -1,2 +1,2 @@ mozprocess==1.3.0 -selenium==4.1.2 +selenium==4.1.3
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_opera.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_opera.txt index b385a35..a829279 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_opera.txt +++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_opera.txt
@@ -1,2 +1,2 @@ mozprocess==1.3.0 -selenium==4.1.2 +selenium==4.1.3
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_sauce.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_sauce.txt index cc5bdf9..7a28278 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_sauce.txt +++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_sauce.txt
@@ -1,2 +1,2 @@ -selenium==4.1.2 +selenium==4.1.3 requests==2.27.1
diff --git a/third_party/zlib/google/test/data/Mixed Paths.zip b/third_party/zlib/google/test/data/Mixed Paths.zip new file mode 100644 index 0000000..2af418b0 --- /dev/null +++ b/third_party/zlib/google/test/data/Mixed Paths.zip Binary files differ
diff --git a/third_party/zlib/google/zip.cc b/third_party/zlib/google/zip.cc index 7f9af5b..1a43196e 100644 --- a/third_party/zlib/google/zip.cc +++ b/third_party/zlib/google/zip.cc
@@ -206,7 +206,9 @@ while (const ZipReader::Entry* const entry = reader.Next()) { if (entry->is_unsafe) { LOG(ERROR) << "Found unsafe entry " << Redact(entry->path) << " in ZIP"; - return false; + if (!options.continue_on_error) + return false; + continue; } if (options.filter && !options.filter.Run(entry->path)) { @@ -218,7 +220,8 @@ // It's a directory. if (!directory_creator.Run(entry->path)) { LOG(ERROR) << "Cannot create directory " << Redact(entry->path); - return false; + if (!options.continue_on_error) + return false; } continue; @@ -229,7 +232,8 @@ if (!writer || !reader.ExtractCurrentEntry(writer.get())) { LOG(ERROR) << "Cannot extract file " << Redact(entry->path) << " from ZIP"; - return false; + if (!options.continue_on_error) + return false; } }
diff --git a/third_party/zlib/google/zip.h b/third_party/zlib/google/zip.h index 621f0d9..25ec655 100644 --- a/third_party/zlib/google/zip.h +++ b/third_party/zlib/google/zip.h
@@ -182,6 +182,9 @@ // Password to decrypt the encrypted files. std::string password; + + // Should ignore errors when extracting files? + bool continue_on_error = false; }; typedef base::RepeatingCallback<std::unique_ptr<WriterDelegate>(
diff --git a/third_party/zlib/google/zip_unittest.cc b/third_party/zlib/google/zip_unittest.cc index cf54991..8fbec32 100644 --- a/third_party/zlib/google/zip_unittest.cc +++ b/third_party/zlib/google/zip_unittest.cc
@@ -471,8 +471,9 @@ EXPECT_EQ("This is not encrypted.\n", contents); // No rubbish file should be left behind. - EXPECT_FALSE( - base::PathExists(test_dir_.AppendASCII("Encrypted ZipCrypto.txt"))); + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("ClearText.txt")); } TEST_F(ZipTest, UnzipEncryptedWithNoPassword) { @@ -491,8 +492,25 @@ EXPECT_EQ("This is not encrypted.\n", contents); // No rubbish file should be left behind. - EXPECT_FALSE( - base::PathExists(test_dir_.AppendASCII("Encrypted ZipCrypto.txt"))); + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("ClearText.txt")); +} + +TEST_F(ZipTest, UnzipEncryptedContinueOnError) { + EXPECT_TRUE( + zip::Unzip(GetDataDirectory().AppendASCII("Different Encryptions.zip"), + test_dir_, {.continue_on_error = true})); + + std::string contents; + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"), + &contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + + // No rubbish file should be left behind. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("ClearText.txt")); } TEST_F(ZipTest, UnzipWrongCrc) { @@ -500,7 +518,9 @@ zip::Unzip(GetDataDirectory().AppendASCII("Wrong CRC.zip"), test_dir_)); // No rubbish file should be left behind. - EXPECT_FALSE(base::PathExists(test_dir_.AppendASCII("Corrupted.txt"))); + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre()); } TEST_F(ZipTest, UnzipRepeatedDirName) { @@ -637,6 +657,148 @@ #endif } +TEST_F(ZipTest, UnzipDifferentCasesContinueOnError) { + EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII( + "Repeated File Name With Different Cases.zip"), + test_dir_, {.continue_on_error = true})); + + std::string contents; + +#if defined(OS_WIN) || defined(OS_MAC) + // Only the first file (with mixed case) has been extracted. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("Case")); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents)); + EXPECT_EQ("Mixed case 111", contents); +#else + // All the files have been extracted. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("Case", "case", "CASE")); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents)); + EXPECT_EQ("Mixed case 111", contents); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("case"), &contents)); + EXPECT_EQ("Lower case 22", contents); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("CASE"), &contents)); + EXPECT_EQ("Upper case 3", contents); +#endif +} + +TEST_F(ZipTest, UnzipMixedPaths) { + EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("Mixed Paths.zip"), + test_dir_, {.continue_on_error = true})); + + std::unordered_set<std::string> want_paths = { +#ifdef OS_WIN + "Dot", // + "Space→", // + "a\\b", // + "u\\v\\w\\x\\y\\z", // + "←Backslash2", // +#else + " ", // Invalid on Windows + "Angle <>", // Invalid on Windows + "Backslash1→\\", // + "Backspace \x08", // Invalid on Windows + "Bell \a", // Invalid on Windows + "C:", // Invalid on Windows + "C:\\", // Absolute path on Windows + "C:\\Temp", // Absolute path on Windows + "C:\\Temp\\", // Absolute path on Windows + "C:\\Temp\\File", // Absolute path on Windows + "Carriage Return \r", // Invalid on Windows + "Colon :", // Invalid on Windows + "Dot .", // Becomes "Dot" on Windows + "Double quote \"", // Invalid on Windows + "Escape \x1B", // Invalid on Windows + "Line Feed \n", // Invalid on Windows + "NUL .txt", // Disappears on Windows + "NUL", // Disappears on Windows + "NUL..txt", // Disappears on Windows + "NUL.tar.gz", // Disappears on Windows + "NUL.txt", // Disappears on Windows + "Pipe |", // Invalid on Windows + "Question ?", // Invalid on Windows + "Space→ ", // Becomes "Space→" on Windows + "Star *", // Invalid on Windows + "Tab \t", // Invalid on Windows + "\\\\server\\share\\file", // Absolute path on Windows + "\\←Backslash2", // Becomes "←Backslash2" on Windows + "a/b", // + "u/v/w/x/y/z", // +#ifndef OS_MAC + "CASE", // + "Case", // +#endif +#endif + " NUL.txt", // + " ←Space", // + "$HOME", // + "%TMP", // + "-", // + "...Tree", // + "..Two", // + ".One", // + "Ampersand &", // + "At @", // + "Backslash3→\\←Backslash4", // + "Backtick `", // + "Caret ^", // + "Comma ,", // + "Curly {}", // + "Dash -", // + "Delete \x7F", // + "Dollar $", // + "Equal =", // + "Euro €", // + "Exclamation !", // + "FileOrDir", // + "First", // + "Hash #", // + "Last", // + "Percent %", // + "Plus +", // + "Quote '", // + "Round ()", // + "Semicolon ;", // + "Smile \U0001F642", // + "Square []", // + "String Terminator \u009C", // + "Tilde ~", // + "Underscore _", // + "case", // + "~", // + }; + + const std::vector<std::string> got_paths = + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES); + + for (const std::string& path : got_paths) { + EXPECT_TRUE(want_paths.erase(path)) + << "Found unexpected file: " << std::quoted(path); + } + + for (const std::string& path : want_paths) { + EXPECT_TRUE(false) << "Cannot find expected file: " << std::quoted(path); + } + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre( +#ifdef OS_WIN + "Backslash3→", "Empty", "a", "u", "u\\v", "u\\v\\w", "u\\v\\w\\x", + "u\\v\\w\\x\\y" +#else + "Empty", "a", "u", "u/v", "u/v/w", "u/v/w/x", "u/v/w/x/y" +#endif + )); +} + TEST_F(ZipTest, UnzipWithDelegates) { auto dir_creator = base::BindLambdaForTesting([this](const base::FilePath& entry_path) { @@ -664,6 +826,34 @@ EXPECT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt"))); } +TEST_F(ZipTest, UnzipOnlyDirectories) { + auto dir_creator = + base::BindLambdaForTesting([this](const base::FilePath& entry_path) { + return base::CreateDirectory(test_dir_.Append(entry_path)); + }); + + // Always return a null WriterDelegate. + auto writer = + base::BindLambdaForTesting([](const base::FilePath& entry_path) { + return std::unique_ptr<zip::WriterDelegate>(); + }); + + base::File file(GetDataDirectory().AppendASCII("test.zip"), + base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ); + EXPECT_TRUE(zip::Unzip(file.GetPlatformFile(), writer, dir_creator, + {.continue_on_error = true})); + base::FilePath dir = test_dir_; + base::FilePath dir_foo = dir.AppendASCII("foo"); + base::FilePath dir_foo_bar = dir_foo.AppendASCII("bar"); + EXPECT_FALSE(base::PathExists(dir.AppendASCII("foo.txt"))); + EXPECT_TRUE(base::DirectoryExists(dir_foo)); + EXPECT_FALSE(base::PathExists(dir_foo.AppendASCII("bar.txt"))); + EXPECT_TRUE(base::DirectoryExists(dir_foo_bar)); + EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII(".hidden"))); + EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII("baz.txt"))); + EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt"))); +} + // Tests that a ZIP archive containing SJIS-encoded file names can be correctly // extracted if the encoding is specified. TEST_F(ZipTest, UnzipSjis) {
diff --git a/tools/android/avd/proto/creation/generic_android24.textpb b/tools/android/avd/proto/creation/generic_android24.textpb new file mode 100644 index 0000000..9e84d794 --- /dev/null +++ b/tools/android/avd/proto/creation/generic_android24.textpb
@@ -0,0 +1,39 @@ +# 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. + +# Configuration for a generic x86 android-24 AVD. + +emulator_package { + package_name: "chromium/third_party/android_sdk/public/emulator" + version: "KUbHEU8j3yFnPWB_61mplm5-Mpm1bJ-cRDeDzTHK3hkC" # 31.2.8 + dest_path: "generic_android24" +} + +system_image_package { + package_name: "chromium/third_party/android_sdk/public/system-images/android-24/google_apis/x86" + version: "GWn2zCnRX5fGPkQMCRucE4tNEjCJeS__h0WFMm9_QPgC" # 27 + dest_path: "generic_android24" +} +system_image_name: "system-images;android-24;google_apis;x86" + +avd_package { + package_name: "chromium/third_party/android_sdk/public/avds/android-24/google_apis/x86" + dest_path: "generic_android24" +} +avd_name: "android_24_google_apis_x86" + +avd_settings { + screen { + density: 480 + height: 1920 + width: 1080 + } + advanced_features { + key: "GLESDynamicVersion" + value: "on" + } + # Tests can run into low memory issue with the default ram size 1024MB + # Incease to 2048MB, which is the same as that on Nexus 5X + ram_size: 2048 +}
diff --git a/tools/android/avd/proto/creation/generic_playstore_android24.textpb b/tools/android/avd/proto/creation/generic_playstore_android24.textpb new file mode 100644 index 0000000..b47d525 --- /dev/null +++ b/tools/android/avd/proto/creation/generic_playstore_android24.textpb
@@ -0,0 +1,39 @@ +# Copyright 2021 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Configuration for a generic x86 android-24 AVD with the Playstore. + +emulator_package { + package_name: "chromium/third_party/android_sdk/public/emulator" + version: "KUbHEU8j3yFnPWB_61mplm5-Mpm1bJ-cRDeDzTHK3hkC" # 31.2.8 + dest_path: "generic_playstore_android24" +} + +system_image_package { + package_name: "chromium/third_party/android_sdk/public/system-images/android-24/google_apis_playstore/x86" + version: "Ojw5nKhSYXGt87HR42bLyeDhuHUByNUnuItEn7t6n3IC" # 19 + dest_path: "generic_playstore_android24" +} +system_image_name: "system-images;android-24;google_apis_playstore;x86" + +avd_package { + package_name: "chromium/third_party/android_sdk/public/avds/android-24/google_apis_playstore/x86" + dest_path: "generic_playstore_android24" +} +avd_name: "android_24_google_apis_playstore_x86" + +avd_settings { + screen { + density: 480 + height: 1920 + width: 1080 + } + advanced_features { + key: "GLESDynamicVersion" + value: "on" + } + # Tests can run into low memory issue with the default ram size 1024MB + # Incease to 2048MB, which is the same as that on Nexus 5X + ram_size: 2048 +}
diff --git a/tools/android/avd/proto/generic_android24.textpb b/tools/android/avd/proto/generic_android24.textpb new file mode 100644 index 0000000..62d5283 --- /dev/null +++ b/tools/android/avd/proto/generic_android24.textpb
@@ -0,0 +1,26 @@ +# Copyright 2022 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Configuration for a generic x86 android-24 AVD (userdebug build). + +emulator_package { + package_name: "chromium/third_party/android_sdk/public/emulator" + version: "KUbHEU8j3yFnPWB_61mplm5-Mpm1bJ-cRDeDzTHK3hkC" # 31.2.8 + dest_path: "generic_android24" +} + +system_image_package { + package_name: "chromium/third_party/android_sdk/public/system-images/android-24/google_apis/x86" + version: "GWn2zCnRX5fGPkQMCRucE4tNEjCJeS__h0WFMm9_QPgC" # 27 + dest_path: "generic_android24" +} +system_image_name: "system-images;android-24;google_apis;x86" + +avd_package { + package_name: "chromium/third_party/android_sdk/public/avds/android-24/google_apis/x86" + # Created in https://ci.chromium.org/b/8819381886161538497 + version: "64fH2yjR-kDMol3lCazqC1funlcJZnmbptglSdfjTlEC" + dest_path: "generic_android24" +} +avd_name: "android_24_google_apis_x86"
diff --git a/tools/android/avd/proto/generic_playstore_android24.textpb b/tools/android/avd/proto/generic_playstore_android24.textpb new file mode 100644 index 0000000..bc5c2b7 --- /dev/null +++ b/tools/android/avd/proto/generic_playstore_android24.textpb
@@ -0,0 +1,26 @@ +# Copyright 2022 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Configuration for a generic x86 android-24 AVD with playstore (user build) + +emulator_package { + package_name: "chromium/third_party/android_sdk/public/emulator" + version: "KUbHEU8j3yFnPWB_61mplm5-Mpm1bJ-cRDeDzTHK3hkC" # 31.2.8 + dest_path: "generic_playstore_android24" +} + +system_image_package { + package_name: "chromium/third_party/android_sdk/public/system-images/android-24/google_apis_playstore/x86" + version: "Ojw5nKhSYXGt87HR42bLyeDhuHUByNUnuItEn7t6n3IC" # 19 + dest_path: "generic_playstore_android24" +} +system_image_name: "system-images;android-24;google_apis_playstore;x86" + +avd_package { + package_name: "chromium/third_party/android_sdk/public/avds/android-24/google_apis_playstore/x86" + # Created in https://ci.chromium.org/b/8819381886161538497 + version: "3pqakRkidFSzF6rs0z2BOg19ojnQsqYdJssqyNT-4dcC" + dest_path: "generic_playstore_android24" +} +avd_name: "android_24_google_apis_playstore_x86"
diff --git a/tools/auto-nav.py b/tools/auto-nav.py index 17b61b68..698b718 100644 --- a/tools/auto-nav.py +++ b/tools/auto-nav.py
@@ -52,7 +52,8 @@ import psutil from selenium import webdriver except ImportError: - print('Error importing required modules. Run with vpython3 instead of python.') + print('Error importing required modules. Run with vpython3 instead of ' + 'python.') sys.exit(1) DEFAULT_INTERVAL = 1 @@ -74,8 +75,8 @@ # line. def ParseArgs(): # Customize usage and help to include options to be passed to chrome.exe. - usage_text = '''%(prog)s [-h] [--interval INTERVAL] [--wait] - [--idlewakeups_dir IDLEWAKEUPS_DIR] + usage_text = '''%(prog)s [-h] [--interval INTERVAL] [--start_prompt] + [--exit_prompt] [--idlewakeups_dir IDLEWAKEUPS_DIR] chrome_dir num_navigations url [url ...] [-- --chrome_option ...]''' additional_help_text = '''optional arguments to chrome.exe, example:
diff --git a/tools/bisect-builds.py b/tools/bisect-builds.py index 3727afd..96e7750 100755 --- a/tools/bisect-builds.py +++ b/tools/bisect-builds.py
@@ -1116,6 +1116,10 @@ revision_details = json.loads(response.read()) revision_text = revision_details['chromium_base_position'] + if not revision_text: + raise Exception( + "No 'chromium_base_position' matching %s found." % chromium_base_position) + # Translate from text commit position to integer commit position. return int(revision_text)
diff --git a/tools/grit/grit/node/base_unittest.py b/tools/grit/grit/node/base_unittest.py index 285ba4c..6cf9b11 100755 --- a/tools/grit/grit/node/base_unittest.py +++ b/tools/grit/grit/node/base_unittest.py
@@ -210,6 +210,14 @@ AssertExpr(False, "'foo' in defs", {'baz': 'bar'}, 'ios', {}) AssertExpr(False, "'foo' in defs", {}, 'ios', {}) AssertExpr(True, "is_linux", {}, 'linux2', {}) + AssertExpr(True, "is_linux", {'is_chromeos': True}, 'linux2', {}) + AssertExpr(True, "is_linux", {'chromeos': True}, 'linux2', {}) + AssertExpr(True, "is_linux", {'lacros': True}, 'linux2', {}) + # TODO(crbug.com/1307455): These two should be False once fixed. + AssertExpr(True, "is_linux", {'chromeos_ash': True}, 'linux2', {}) + AssertExpr(True, "is_linux", {'chromeos_lacros': True}, 'linux2', {}) + AssertExpr(False, "is_chromeos", {}, 'linux2', {}) + AssertExpr(False, "is_fuchsia", {}, 'linux2', {}) AssertExpr(False, "is_linux", {}, 'win32', {}) AssertExpr(True, "is_macosx", {}, 'darwin', {}) AssertExpr(False, "is_macosx", {}, 'ios', {}) @@ -220,11 +228,15 @@ AssertExpr(True, "is_ios", {}, 'ios', {}) AssertExpr(False, "is_ios", {}, 'darwin', {}) AssertExpr(True, "is_posix", {}, 'linux2', {}) + AssertExpr(True, "is_posix", {'chromeos_ash': True}, 'linux2', {}) + AssertExpr(True, "is_posix", {'chromeos_lacros': True}, '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", {}, 'fuchsia', {}) AssertExpr(False, "is_posix", {}, 'win32', {}) + AssertExpr(True, "is_fuchsia", {}, 'fuchsia', {}) AssertExpr(True, "pp_ifdef('foo')", {'foo': True}, 'win32', {}) AssertExpr(True, "pp_ifdef('foo')", {'foo': False}, 'win32', {}) AssertExpr(False, "pp_ifdef('foo')", {'bar': True}, 'win32', {})
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 303fc78..9124835 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -296,12 +296,6 @@ 'goma': 'chromeos_amd64-generic_use_fake_dbus_clients_goma', 'reclient': 'chromeos_amd64-generic_use_fake_dbus_clients_reclient', }, - 'GPU FYI Win x64 Builder (reclient shadow)': 'gpu_fyi_tests_release_trybot_reclient', - 'GPU FYI Win x64 Builder (dbg) (reclient shadow)': 'gpu_fyi_tests_debug_trybot_reclient', - 'GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)': 'gpu_fyi_tests_dx12vk_release_trybot_reclient', - 'GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)': 'gpu_fyi_tests_dx12vk_debug_trybot_reclient', - 'GPU FYI XR Win x64 Builder (reclient shadow)': 'gpu_fyi_tests_release_trybot_reclient', - 'GPU Win x64 Builder (dbg) (reclient shadow)': 'gpu_tests_debug_bot_reclient', 'Libfuzzer Upload Chrome OS ASan': 'libfuzzer_chromeos_asan_release_bot', 'Libfuzzer Upload Linux ASan': 'libfuzzer_asan_release_bot', 'Libfuzzer Upload Linux ASan (reclient)': 'libfuzzer_asan_release_bot_reclient', @@ -510,10 +504,16 @@ 'GPU FYI Mac arm64 Builder': 'gpu_fyi_tests_release_trybot_arm64', 'GPU FYI Win Builder': 'gpu_fyi_tests_release_trybot_x86', 'GPU FYI Win x64 Builder': 'gpu_fyi_tests_release_trybot', + 'GPU FYI Win x64 Builder (reclient shadow)': 'gpu_fyi_tests_release_trybot_reclient', 'GPU FYI Win x64 Builder (dbg)': 'gpu_fyi_tests_debug_trybot', + 'GPU FYI Win x64 Builder (dbg) (reclient shadow)': 'gpu_fyi_tests_debug_trybot_reclient', 'GPU FYI Win x64 DX12 Vulkan Builder': 'gpu_fyi_tests_dx12vk_release_trybot', + 'GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)': 'gpu_fyi_tests_dx12vk_release_trybot_reclient', 'GPU FYI Win x64 DX12 Vulkan Builder (dbg)': 'gpu_fyi_tests_dx12vk_debug_trybot', + 'GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)': 'gpu_fyi_tests_dx12vk_debug_trybot_reclient', 'GPU FYI XR Win x64 Builder': 'gpu_fyi_tests_release_trybot', + 'GPU FYI XR Win x64 Builder (reclient shadow)': 'gpu_fyi_tests_release_trybot_reclient', + 'GPU Win x64 Builder (dbg) (reclient shadow)': 'gpu_tests_debug_bot_reclient', 'Linux FYI GPU TSAN Release': 'gpu_fyi_tests_release_trybot_tsan_reclient', 'Optional Android Release (Nexus 5X)': 'gpu_tests_android_release_trybot_arm64', 'Optional Android Release (Pixel 4)': 'gpu_tests_android_release_trybot',
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json index c5f8ed20..9d2316d8 100644 --- a/tools/mb/mb_config_expectations/chromium.fyi.json +++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -155,95 +155,6 @@ } } }, - "GPU FYI Win x64 Builder (dbg) (reclient shadow)": { - "gn_args": { - "enable_nacl": false, - "ffmpeg_branding": "Chrome", - "internal_gles2_conform_tests": true, - "is_component_build": true, - "is_debug": true, - "is_gpu_fyi_bot": true, - "proprietary_codecs": true, - "symbol_level": 1, - "use_rbe": true, - "use_remoteexec": true - } - }, - "GPU FYI Win x64 Builder (reclient shadow)": { - "gn_args": { - "blink_enable_generated_code_formatting": false, - "dcheck_always_on": true, - "enable_nacl": false, - "ffmpeg_branding": "Chrome", - "internal_gles2_conform_tests": true, - "is_component_build": false, - "is_debug": false, - "is_gpu_fyi_bot": true, - "proprietary_codecs": true, - "symbol_level": 1, - "use_rbe": true, - "use_remoteexec": true - } - }, - "GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)": { - "gn_args": { - "enable_nacl": false, - "enable_vulkan": true, - "ffmpeg_branding": "Chrome", - "internal_gles2_conform_tests": true, - "is_component_build": true, - "is_debug": true, - "is_gpu_fyi_bot": true, - "proprietary_codecs": true, - "symbol_level": 1, - "use_rbe": true, - "use_remoteexec": true - } - }, - "GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)": { - "gn_args": { - "blink_enable_generated_code_formatting": false, - "dcheck_always_on": true, - "enable_nacl": false, - "enable_vulkan": true, - "ffmpeg_branding": "Chrome", - "internal_gles2_conform_tests": true, - "is_component_build": false, - "is_debug": false, - "is_gpu_fyi_bot": true, - "proprietary_codecs": true, - "symbol_level": 1, - "use_rbe": true, - "use_remoteexec": true - } - }, - "GPU FYI XR Win x64 Builder (reclient shadow)": { - "gn_args": { - "blink_enable_generated_code_formatting": false, - "dcheck_always_on": true, - "enable_nacl": false, - "ffmpeg_branding": "Chrome", - "internal_gles2_conform_tests": true, - "is_component_build": false, - "is_debug": false, - "is_gpu_fyi_bot": true, - "proprietary_codecs": true, - "symbol_level": 1, - "use_rbe": true, - "use_remoteexec": true - } - }, - "GPU Win x64 Builder (dbg) (reclient shadow)": { - "gn_args": { - "ffmpeg_branding": "Chrome", - "is_component_build": true, - "is_debug": true, - "proprietary_codecs": true, - "symbol_level": 1, - "use_rbe": true, - "use_remoteexec": true - } - }, "Libfuzzer Upload Chrome OS ASan": { "gn_args": { "archive_seed_corpus": false,
diff --git a/tools/mb/mb_config_expectations/chromium.gpu.fyi.json b/tools/mb/mb_config_expectations/chromium.gpu.fyi.json index bce886a..6467fa81 100644 --- a/tools/mb/mb_config_expectations/chromium.gpu.fyi.json +++ b/tools/mb/mb_config_expectations/chromium.gpu.fyi.json
@@ -225,6 +225,36 @@ "use_goma": true } }, + "GPU FYI Win x64 Builder (dbg) (reclient shadow)": { + "gn_args": { + "enable_nacl": false, + "ffmpeg_branding": "Chrome", + "internal_gles2_conform_tests": true, + "is_component_build": true, + "is_debug": true, + "is_gpu_fyi_bot": true, + "proprietary_codecs": true, + "symbol_level": 1, + "use_rbe": true, + "use_remoteexec": true + } + }, + "GPU FYI Win x64 Builder (reclient shadow)": { + "gn_args": { + "blink_enable_generated_code_formatting": false, + "dcheck_always_on": true, + "enable_nacl": false, + "ffmpeg_branding": "Chrome", + "internal_gles2_conform_tests": true, + "is_component_build": false, + "is_debug": false, + "is_gpu_fyi_bot": true, + "proprietary_codecs": true, + "symbol_level": 1, + "use_rbe": true, + "use_remoteexec": true + } + }, "GPU FYI Win x64 DX12 Vulkan Builder": { "gn_args": { "blink_enable_generated_code_formatting": false, @@ -255,6 +285,38 @@ "use_goma": true } }, + "GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)": { + "gn_args": { + "enable_nacl": false, + "enable_vulkan": true, + "ffmpeg_branding": "Chrome", + "internal_gles2_conform_tests": true, + "is_component_build": true, + "is_debug": true, + "is_gpu_fyi_bot": true, + "proprietary_codecs": true, + "symbol_level": 1, + "use_rbe": true, + "use_remoteexec": true + } + }, + "GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)": { + "gn_args": { + "blink_enable_generated_code_formatting": false, + "dcheck_always_on": true, + "enable_nacl": false, + "enable_vulkan": true, + "ffmpeg_branding": "Chrome", + "internal_gles2_conform_tests": true, + "is_component_build": false, + "is_debug": false, + "is_gpu_fyi_bot": true, + "proprietary_codecs": true, + "symbol_level": 1, + "use_rbe": true, + "use_remoteexec": true + } + }, "GPU FYI XR Win x64 Builder": { "gn_args": { "blink_enable_generated_code_formatting": false, @@ -270,6 +332,33 @@ "use_goma": true } }, + "GPU FYI XR Win x64 Builder (reclient shadow)": { + "gn_args": { + "blink_enable_generated_code_formatting": false, + "dcheck_always_on": true, + "enable_nacl": false, + "ffmpeg_branding": "Chrome", + "internal_gles2_conform_tests": true, + "is_component_build": false, + "is_debug": false, + "is_gpu_fyi_bot": true, + "proprietary_codecs": true, + "symbol_level": 1, + "use_rbe": true, + "use_remoteexec": true + } + }, + "GPU Win x64 Builder (dbg) (reclient shadow)": { + "gn_args": { + "ffmpeg_branding": "Chrome", + "is_component_build": true, + "is_debug": true, + "proprietary_codecs": true, + "symbol_level": 1, + "use_rbe": true, + "use_remoteexec": true + } + }, "Linux FYI GPU TSAN Release": { "gn_args": { "blink_enable_generated_code_formatting": false,
diff --git a/tools/memory/partition_allocator/BUILD.gn b/tools/memory/partition_allocator/BUILD.gn index 6cbe9e02..fae79c6 100644 --- a/tools/memory/partition_allocator/BUILD.gn +++ b/tools/memory/partition_allocator/BUILD.gn
@@ -26,6 +26,13 @@ "//base", ] } + executable("pa_dump_heap") { + sources = [ "pa_dump_heap.cc" ] + deps = [ + ":pa_tool_utils", + "//base", + ] + } } group("all") {
diff --git a/tools/memory/partition_allocator/inspect_utils.cc b/tools/memory/partition_allocator/inspect_utils.cc index 013ed78c..5f6a53d 100644 --- a/tools/memory/partition_allocator/inspect_utils.cc +++ b/tools/memory/partition_allocator/inspect_utils.cc
@@ -26,7 +26,6 @@ static_cast<ssize_t>(size)) { return true; } - return false; }
diff --git a/tools/memory/partition_allocator/inspect_utils.h b/tools/memory/partition_allocator/inspect_utils.h index ca5e13cc..a909f7e 100644 --- a/tools/memory/partition_allocator/inspect_utils.h +++ b/tools/memory/partition_allocator/inspect_utils.h
@@ -17,6 +17,7 @@ #include "base/files/file.h" #include "base/posix/eintr_wrapper.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace partition_alloc::internal::tools { @@ -36,6 +37,31 @@ uintptr_t IndexThreadCacheNeedleArray(pid_t pid, int mem_fd, size_t index); +// Allows to access an object copied from remote memory "as if" it were +// local. Of course, dereferencing any pointer from within it will at best +// fault. +template <typename T> +class RawBuffer { + public: + RawBuffer() = default; + const T* get() const { return reinterpret_cast<const T*>(buffer_); } + char* get_buffer() { return buffer_; } + + static absl::optional<RawBuffer<T>> ReadFromMemFd(int mem_fd, + uintptr_t address) { + RawBuffer<T> buf; + bool ok = ReadMemory(mem_fd, reinterpret_cast<unsigned long>(address), + sizeof(T), buf.get_buffer()); + if (!ok) + return absl::nullopt; + + return {buf}; + } + + private: + alignas(T) char buffer_[sizeof(T)]; +}; + } // namespace partition_alloc::internal::tools #endif // TOOLS_MEMORY_PARTITION_ALLOCATOR_INSPECT_UTILS_H_
diff --git a/tools/memory/partition_allocator/pa_dump_heap.cc b/tools/memory/partition_allocator/pa_dump_heap.cc new file mode 100644 index 0000000..42fa8bcf --- /dev/null +++ b/tools/memory/partition_allocator/pa_dump_heap.cc
@@ -0,0 +1,198 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Dumps PartitionAlloc's heap into a file. + +#include <cstdlib> +#include <string> + +#include "base/allocator/partition_allocator/partition_root.h" +#include "base/allocator/partition_allocator/thread_cache.h" +#include "base/check.h" +#include "base/command_line.h" +#include "base/files/file.h" +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "base/thread_annotations.h" +#include "base/values.h" +#include "tools/memory/partition_allocator/inspect_utils.h" + +namespace partition_alloc::internal::tools { + +class HeapDumper { + public: + HeapDumper(pid_t pid, int mem_fd) : pid_(pid), mem_fd_(mem_fd) {} + + void FindRoot() { + root_address_ = FindRootAddress(pid_, mem_fd_); + CHECK(root_address_); + auto root = RawBuffer<PartitionRoot<ThreadSafe>>::ReadFromMemFd( + mem_fd_, root_address_); + CHECK(root); + root_ = *root; + } + + void DumpSuperPages() { + std::vector<uintptr_t> super_pages; + // There is no list of super page, only a list of extents. Walk the extent + // list to get all superpages. + uintptr_t extent_address = + reinterpret_cast<uintptr_t>(root_.get()->first_extent); + while (extent_address) { + auto extent = + RawBuffer<PartitionSuperPageExtentEntry<ThreadSafe>>::ReadFromMemFd( + mem_fd_, extent_address); + uintptr_t first_super_page_address = SuperPagesBeginFromExtent( + reinterpret_cast<PartitionSuperPageExtentEntry<ThreadSafe>*>( + extent_address)); + for (uintptr_t super_page = first_super_page_address; + super_page < first_super_page_address + + extent->get()->number_of_consecutive_super_pages * + kSuperPageSize; + super_page += kSuperPageSize) { + super_pages.push_back(super_page); + } + extent_address = reinterpret_cast<uintptr_t>(extent->get()->next); + } + + LOG(WARNING) << "Found " << super_pages.size() << std::hex + << " super pages."; + for (uintptr_t super_page : super_pages) { + auto super_page_data = + std::make_unique<std::array<char, kSuperPageSize>>(); + bool ok = ReadMemory(mem_fd_, super_page, kSuperPageSize, + super_page_data->data()); + if (!ok) { + LOG(WARNING) << base::StringPrintf("Cannot read from super page 0x%lx", + super_page); + continue; + } + super_pages_.emplace(super_page, std::move(super_page_data)); + } + LOG(WARNING) << "Read all super pages"; + } + + base::Value Dump() const { + auto partition_page_to_value = + [](uintptr_t offset, + const std::array<char, kSuperPageSize>& data) -> base::Value { + auto ret = base::Value(base::Value::Type::DICTIONARY); + if (offset == 0) { + ret.SetKey("type", base::Value{"metadata"}); + } else if (offset == kSuperPageSize - PartitionPageSize()) { + ret.SetKey("type", base::Value{"guard"}); + } else { + ret.SetKey("type", base::Value{"payload"}); + } + + bool all_zeros = true; + for (size_t i = 0; i < PartitionPageSize(); i++) { + if (data[offset + i]) { + all_zeros = false; + break; + } + } + ret.SetKey("all_zeros", base::Value{all_zeros}); + return ret; + }; + auto super_page_to_value = + [&](uintptr_t address, + const std::array<char, kSuperPageSize>& data) -> base::Value { + auto ret = base::Value(base::Value::Type::DICTIONARY); + ret.SetKey("address", base::Value{base::StringPrintf("0x%lx", address)}); + + auto partition_pages = base::Value(base::Value::Type::LIST); + for (uintptr_t offset = 0; offset < kSuperPageSize; + offset += PartitionPageSize()) { + partition_pages.Append(partition_page_to_value(offset, data)); + } + ret.SetKey("partition_pages", std::move(partition_pages)); + + return ret; + }; + + auto super_pages_value = base::Value(base::Value::Type::LIST); + for (const auto& address_data : super_pages_) { + super_pages_value.Append( + super_page_to_value(address_data.first, *address_data.second)); + } + + return super_pages_value; + } + + private: + static uintptr_t FindRootAddress(pid_t pid, + int mem_fd) NO_THREAD_SAFETY_ANALYSIS { + uintptr_t tcache_registry_address = + IndexThreadCacheNeedleArray(pid, mem_fd, 1); + auto registry = + RawBuffer<base::internal::ThreadCacheRegistry>::ReadFromMemFd( + mem_fd, tcache_registry_address); + if (!registry) + return 0; + + auto tcache_address = + reinterpret_cast<uintptr_t>(registry->get()->list_head_); + if (!tcache_address) + return 0; + + auto tcache = RawBuffer<base::internal::ThreadCache>::ReadFromMemFd( + mem_fd, tcache_address); + if (!tcache) + return 0; + + auto root_address = reinterpret_cast<uintptr_t>(tcache->get()->root_); + return root_address; + } + + const pid_t pid_; + const int mem_fd_; + uintptr_t root_address_; + RawBuffer<PartitionRoot<ThreadSafe>> root_; + std::map<uintptr_t, std::unique_ptr<std::array<char, kSuperPageSize>>> + super_pages_; +}; + +} // namespace partition_alloc::internal::tools + +int main(int argc, char** argv) { + base::CommandLine::Init(argc, argv); + + auto* command_line = base::CommandLine::ForCurrentProcess(); + if (!command_line->HasSwitch("pid") || !command_line->HasSwitch("json")) { + LOG(ERROR) << "Usage:" << argv[0] << " --pid=<PID> --json=<FILENAME>"; + return 1; + } + + int pid = atoi(command_line->GetSwitchValueASCII("pid").c_str()); + LOG(WARNING) << "PID = " << pid; + + auto mem_fd = partition_alloc::internal::tools::OpenProcMem(pid); + partition_alloc::internal::tools::HeapDumper dumper{pid, mem_fd.get()}; + + { + partition_alloc::internal::tools::ScopedSigStopper stopper{pid}; + dumper.FindRoot(); + dumper.DumpSuperPages(); + } + + auto dump = dumper.Dump(); + std::string json_string; + bool ok = base::JSONWriter::WriteWithOptions( + dump, base::JSONWriter::Options::OPTIONS_PRETTY_PRINT, &json_string); + + if (ok) { + base::FilePath json_filename = command_line->GetSwitchValuePath("json"); + auto f = base::File(json_filename, base::File::Flags::FLAG_OPEN_ALWAYS | + base::File::Flags::FLAG_WRITE); + if (f.IsValid()) { + f.WriteAtCurrentPos(json_string.c_str(), json_string.size()); + LOG(WARNING) << "\n\nDumped JSON to " << json_filename; + return 0; + } + } + + return 1; +}
diff --git a/tools/memory/partition_allocator/pa_tcache_inspect.cc b/tools/memory/partition_allocator/pa_tcache_inspect.cc index ad8914d..961407d 100644 --- a/tools/memory/partition_allocator/pa_tcache_inspect.cc +++ b/tools/memory/partition_allocator/pa_tcache_inspect.cc
@@ -50,31 +50,6 @@ return IndexThreadCacheNeedleArray(pid, mem_fd, 1); } -// Allows to access an object copied from remote memory "as if" it were -// local. Of course, dereferencing any pointer from within it will at best -// fault. -template <typename T> -class RawBuffer { - public: - RawBuffer() = default; - const T* get() const { return reinterpret_cast<const T*>(buffer_); } - char* get_buffer() { return buffer_; } - - static absl::optional<RawBuffer<T>> ReadFromMemFd(int mem_fd, - uintptr_t address) { - RawBuffer<T> buf; - bool ok = ReadMemory(mem_fd, reinterpret_cast<unsigned long>(address), - sizeof(T), buf.get_buffer()); - if (!ok) - return absl::nullopt; - - return {buf}; - } - - private: - alignas(T) char buffer_[sizeof(T)]; -}; - // List all thread names for a given PID. std::map<base::PlatformThreadId, std::string> ThreadNames(pid_t pid) { std::map<base::PlatformThreadId, std::string> result;
diff --git a/tools/memory/partition_allocator/plot_superpages.py b/tools/memory/partition_allocator/plot_superpages.py new file mode 100755 index 0000000..f1c40d5b --- /dev/null +++ b/tools/memory/partition_allocator/plot_superpages.py
@@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# Copyright 2022 The Chromium 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 the superpage occupancy. + +To run this: + +1. Build pa_dump_heap at a close-enough revision to the Chrome instance you're + interested in. +2. Run pa_dump_heap --pid=PID_OF_RUNNING_CHROME_PROCESS --json=output.json +3. Run plot_superpages.py --json=output.json --output=output.png +""" + +import argparse +import json + +import matplotlib +from matplotlib import pylab as plt +import numpy as np + + +def ParseJson(filename): + with open(filename, 'r') as f: + data = json.load(f) + return data + + +def PlotData(data, output_filename): + num_superpages = len(data) + num_partition_pages = len(data[0]['partition_pages']) + data_np = np.zeros((num_superpages, num_partition_pages, 3)) + + address_to_superpage = {superpage['address']: superpage for superpage in data} + addresses = sorted(address_to_superpage.keys()) + + for superpage_index in range(len(addresses)): + superpage = address_to_superpage[addresses[superpage_index]] + for index, partition_page in enumerate(superpage['partition_pages']): + value = 0 + if partition_page['type'] == 'metadata': + value = (0., 0., 0.) + elif partition_page['type'] == 'guard': + value = (.5, .5, .5) + elif partition_page['all_zeros']: + value = (1., 0., 0.) + else: + value = (0., 1., 0.) + data_np[superpage_index, index, :] = value + + plt.figure(figsize=(20, 14)) + plt.imshow(data_np) + plt.title('Super page map') + plt.yticks(ticks=range(len(addresses)), labels=addresses) + plt.xlabel('PartitionPage index') + + handles = [ + matplotlib.patches.Patch(facecolor=(0., 0., 0.), label='Metadata'), + matplotlib.patches.Patch(facecolor=(.5, .5, .5), label='Guard'), + matplotlib.patches.Patch(facecolor=(1., 0., 0.), label='Free'), + matplotlib.patches.Patch(facecolor=(0., 1., 0.), label='Allocated'), + ] + plt.legend(handles=handles, loc='lower right', fontsize=16) + + plt.savefig(output_filename, bbox_inches='tight') + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--json', + help='JSON dump from pa_tcache_inspect', + required=True) + parser.add_argument('--output', help='Output file', required=True) + + args = parser.parse_args() + data = ParseJson(args.json) + PlotData(data, args.output) + + +if __name__ == '__main__': + main()
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index cbb64fe9..503e4bf 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -7621,6 +7621,7 @@ <int value="2" label="User closed bubble via corner X or ESC"/> <int value="3" label="User did not interact with the bubble"/> <int value="4" label="Bubble lost focus and was deactivated"/> + <int value="5" label="User cancelled the bubble"/> </enum> <enum name="AutofillVirtualCardManualFallbackBubbleFieldClicked"> @@ -11454,6 +11455,15 @@ <int value="3" label="Multiple pages, all of which are non-visible"/> </enum> +<enum name="BrowsingTopicsCalculatorResultStatus"> + <int value="0" label="Success"/> + <int value="1" label="Failure: permission denied"/> + <int value="2" label="Failure: api usage context query error"/> + <int value="3" label="Failure: annotation execution error"/> + <int value="4" + label="Failure: taxonomy version not supported in the Chrome binary"/> +</enum> + <enum name="BubbleDismissalReason"> <obsolete> Deprecated 12/2020 as it is no longer used for analysis. @@ -16722,6 +16732,12 @@ <int value="3" label="Failed to assemble report"/> </enum> +<enum name="ConversionAttributionSrcRequestStatus"> + <int value="0" label="Requested"/> + <int value="1" label="Received"/> + <int value="2" label="Failed"/> +</enum> + <enum name="ConversionPostSendReportDeleteEvent"> <obsolete> The error rate is not high enough to merit monitoring. @@ -37150,7 +37166,7 @@ <int value="3810" label="CanvasUseColorSpace"/> <int value="3811" label="SelectMenuElement"/> <int value="3812" - label="RTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial"/> + label="OBSOLETE_RTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial"/> <int value="3813" label="WebAppManifestCaptureLinks"/> <int value="3814" label="SanitizerAPICreated"/> <int value="3815" label="SanitizerAPIDefaultConfiguration"/> @@ -37535,6 +37551,7 @@ <int value="4179" label="V8FunctionPrototypeArguments"/> <int value="4180" label="V8FunctionPrototypeCaller"/> <int value="4181" label="BluetoothDeviceForget"/> + <int value="4182" label="TopicsAPI_BrowsingTopics_Method"/> </enum> <enum name="FeaturePolicyAllowlistType"> @@ -37651,6 +37668,8 @@ <int value="94" label="ClientHintUAFull"/> <int value="95" label="ClientHintUAWoW64"/> <int value="96" label="ClientHintPartitionedCookies"/> + <int value="97" label="BrowsingTopics"/> + <int value="98" label="BrowsingTopicsBackwardCompatible"/> </enum> <enum name="FeaturePolicyImageCompressionFormat"> @@ -52148,6 +52167,7 @@ <int value="-1779753607" label="VizDisplayCompositor:disabled"/> <int value="-1778993296" label="ContextualSearchMlTapSuppression:disabled"/> <int value="-1778202807" label="DelayAsyncScriptExecution:enabled"/> + <int value="-1777540083" label="LocationBarModelOptimizations:disabled"/> <int value="-1776351704" label="DesktopPWAsOmniboxInstall:disabled"/> <int value="-1775842908" label="EnableOAuthIpp:disabled"/> <int value="-1774818943" label="VrWebInputEditing:enabled"/> @@ -52367,6 +52387,7 @@ <int value="-1617805422" label="WebViewUseMetricsUploadService:enabled"/> <int value="-1617183455" label="OfflineRecentPages:disabled"/> <int value="-1616855537" label="enable-manual-password-generation:disabled"/> + <int value="-1616638720" label="AutofillShadowDOM:enabled"/> <int value="-1615901413" label="NewMacNotificationAPI:enabled"/> <int value="-1615727417" label="OmniboxSearchReadyIncognito:enabled"/> <int value="-1615704396" @@ -52869,6 +52890,7 @@ <int value="-1294050129" label="ContentFullscreen:disabled"/> <int value="-1293987566" label="OmniboxZeroSuggestionsOnNTPRealbox:disabled"/> <int value="-1293566107" label="DocumentTransition:disabled"/> + <int value="-1292766410" label="IntentChipSkipsPicker:disabled"/> <int value="-1292615467" label="OmniboxSuggestionTransparencyOptions:disabled"/> <int value="-1291963295" label="ComputePressure:disabled"/> @@ -52893,6 +52915,7 @@ <int value="-1283164264" label="CroshSWA:disabled"/> <int value="-1282992935" label="AutofillLocalCardMigrationShowFeedback:disabled"/> + <int value="-1282488703" label="LocationBarModelOptimizations:enabled"/> <int value="-1281465357" label="MacV2GPUSandbox:disabled"/> <int value="-1280673966" label="PluginVmFullscreen:disabled"/> <int value="-1280274244" label="OmniboxClosePopupWithEscape:disabled"/> @@ -53610,6 +53633,7 @@ <int value="-790544721" label="OverrideUnsupportedPageLanguageForHrefTranslate:disabled"/> <int value="-790036192" label="overscroll-start-threshold"/> + <int value="-788070719" label="PartitionedCookiesBypassOriginTrial:disabled"/> <int value="-787969387" label="HTTPSServerPreviewsUsingURLLoader:enabled"/> <int value="-787885873" label="BorealisForceBetaClient:disabled"/> <int value="-787876637" label="HomeLauncherGestures:enabled"/> @@ -54052,6 +54076,7 @@ label="OmniboxHistoryQuickProviderAllowMidwordContinuations:disabled"/> <int value="-482259889" label="MacMDDownloadShelf:disabled"/> <int value="-482188490" label="ImpulseScrollAnimations:disabled"/> + <int value="-480263549" label="AutofillShadowDOM:disabled"/> <int value="-478462945" label="enable-ephemeral-apps"/> <int value="-477101783" label="HandwritingGesture:disabled"/> <int value="-475049740" label="disable-vr-shell"/> @@ -54180,6 +54205,7 @@ label="OmniboxUIExperimentHideSteadyStateUrlPathQueryAndRefOnInteraction:enabled"/> <int value="-389283574" label="IPH_PasswordsAccountStorage:disabled"/> <int value="-387606010" label="ArcBootCompletedBroadcast:enabled"/> + <int value="-387217874" label="LinkCapturingInfoBar:enabled"/> <int value="-385461103" label="XsurfaceMetricsReporting:enabled"/> <int value="-385337473" label="enable-fast-unload"/> <int value="-384589459" label="disable-supervised-user-safesites"/> @@ -54866,6 +54892,7 @@ <int value="92327255" label="DisplayMoveWindowAccels:disabled"/> <int value="93832899" label="NtpCustomizationMenuV2:enabled"/> <int value="97091906" label="AutofillSaveCardShowNoThanks:enabled"/> + <int value="97976428" label="CastUseBlocklistForRemotingQuery:enabled"/> <int value="98029688" label="MediaAppDisplayExif:disabled"/> <int value="98134240" label="material-design-ink-drop-animation-speed"/> <int value="98218116" label="ContextualSearchLongpressResolve:disabled"/> @@ -54904,6 +54931,7 @@ <int value="121911155" label="DesktopPWAsElidedExtensionsMenu:enabled"/> <int value="122959683" label="WebViewJavaJsBridgeMojo:enabled"/> <int value="123097915" label="FaviconsFromWebManifest:enabled"/> + <int value="124590378" label="LensStandalone:disabled"/> <int value="125581289" label="WebRtcHWVP8Encoding:disabled"/> <int value="125934378" label="enable-password-link"/> <int value="127338155" label="NtpSafeBrowsingModule:disabled"/> @@ -55051,6 +55079,7 @@ <int value="239330938" label="LauncherAppSort:enabled"/> <int value="240856309" label="MimeHandlerViewInCrossProcessFrame:enabled"/> <int value="241187301" label="BrowserTouchBar:disabled"/> + <int value="241846183" label="PartitionedCookiesBypassOriginTrial:enabled"/> <int value="244697230" label="enable-theme-color-in-tabbed-mode"/> <int value="245100553" label="PcieBillboardNotification:enabled"/> <int value="245896533" label="SearchSuggestionsOnLocalNtp:enabled"/> @@ -55168,6 +55197,7 @@ <int value="317432596" label="DisplayLocking:disabled"/> <int value="317889969" label="AutofillAddressProfileSavePrompt:disabled"/> <int value="318277328" label="WebViewAppsPackageNamesAllowlist:enabled"/> + <int value="318704417" label="LensStandalone:enabled"/> <int value="319683583" label="ContentSuggestionsDebugLog:enabled"/> <int value="320121752" label="DelegateOverscrollSwipes:disabled"/> <int value="321366101" label="WebXRARDOMOverlay:enabled"/> @@ -56013,6 +56043,7 @@ <int value="884106779" label="supervised-user-safesites"/> <int value="884892326" label="CSSCascadeLayers:enabled"/> <int value="885186849" label="finch-seed-expiration-age"/> + <int value="885943836" label="IntentChipSkipsPicker:enabled"/> <int value="885971656" label="EnablePlayStoreAppSearch:enabled"/> <int value="886907524" label="autoplay-policy"/> <int value="886990840" label="ImeMojoDecoder:enabled"/> @@ -56401,6 +56432,7 @@ <int value="1162622865" label="EarlyCodeCache:disabled"/> <int value="1162693272" label="MacCoreLocationImplementation:disabled"/> <int value="1163255347" label="ash-enable-touch-view-touch-feedback"/> + <int value="1163588337" label="CastForceEnableRemotingQuery:enabled"/> <int value="1164377197" label="SwipingFromLeftEdgeToGoBack:enabled"/> <int value="1164460660" label="AutofillEnableVirtualCard:enabled"/> <int value="1165682330" label="AutofillSaveAndFillVPA:disabled"/> @@ -56943,6 +56975,7 @@ <int value="1540487070" label="ContextualSearchDebug:enabled"/> <int value="1541723759" label="ServiceWorkerNavigationPreload:disabled"/> <int value="1543027970" label="EnableDisplayZoomSetting:disabled"/> + <int value="1543349770" label="CastUseBlocklistForRemotingQuery:disabled"/> <int value="1546652609" label="SharingHubDesktopOmnibox:disabled"/> <int value="1548776701" label="AllBookmarks:disabled"/> <int value="1548942246" label="PassiveDocumentEventListeners:disabled"/> @@ -57182,9 +57215,11 @@ label="AutofillUseUnassociatedListedElements:enabled"/> <int value="1710630380" label="AutofillEnableSupportForMoreStructureInNames:enabled"/> + <int value="1710663404" label="CastForceEnableRemotingQuery:disabled"/> <int value="1711037950" label="AutofillVisualImprovementsForSuggestionUi:disabled"/> <int value="1711286384" label="ContextMenuCopyImage:disabled"/> + <int value="1711998812" label="LinkCapturingInfoBar:disabled"/> <int value="1712622545" label="Memories:disabled"/> <int value="1712697097" label="QuickAnswersV2:disabled"/> <int value="1712880335" label="FiltersInRecents:enabled"/> @@ -59857,12 +59892,15 @@ <int value="0" label="Unknown"/> <int value="1" label="CdmProxyReceivedInInvalidState"/> <int value="2" label="FailedToSetSourceOnMediaEngine"/> - <int value="3" label="kFailedToSetCurrentTime"/> - <int value="4" label="kFailedToPlay"/> - <int value="5" label="kOnPlaybackError"/> - <int value="6" label="kOnDCompSurfaceReceivedError"/> - <int value="7" label="kOnDCompSurfaceHandleSetError"/> - <int value="8" label="kOnConnectionError"/> + <int value="3" label="SetCurrentTimeError"/> + <int value="4" label="FailedToPlay"/> + <int value="5" label="OnPlaybackError"/> + <int value="6" label="OnDCompSurfaceReceivedError (deprecated)"/> + <int value="7" label="OnDCompSurfaceHandleSetError"/> + <int value="8" label="OnConnectionError"/> + <int value="9" label="FailedToSetDCompMode"/> + <int value="10" label="FailedToGetDCompSurface"/> + <int value="11" label="FailedToDuplicateHandle"/> </enum> <enum name="MediaGalleriesUsageType"> @@ -80851,6 +80889,7 @@ <int value="8" label="Failed to write model result to DB"/> <int value="9" label="Invalid selection result found in prefs"/> <int value="10" label="Database initialization failed"/> + <int value="11" label="At least one segment is not available"/> </enum> <enum name="SelectedNewTabCreationOption">
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml index 49ba440..13e9bb1 100644 --- a/tools/metrics/histograms/metadata/arc/histograms.xml +++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -93,7 +93,7 @@ </histogram> <histogram name="Arc.Accessibility.ActiveTime.{ArcAccessibilityFeatures}" - units="ms" expires_after="2022-04-10"> + units="ms" expires_after="2023-04-10"> <owner>sahok@chromium.org</owner> <owner>arc-framework@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/browsing_topics/histograms.xml b/tools/metrics/histograms/metadata/browsing_topics/histograms.xml index 1e7dfbd..4b1da00 100644 --- a/tools/metrics/histograms/metadata/browsing_topics/histograms.xml +++ b/tools/metrics/histograms/metadata/browsing_topics/histograms.xml
@@ -32,8 +32,60 @@ </summary> </histogram> +<histogram name="BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus" + enum="BrowsingTopicsCalculatorResultStatus" expires_after="2023-03-14"> + <owner>yaoxia@chromium.org</owner> + <owner>jkarlin@chromium.org</owner> + <summary> + Records the browsing topics calculation result status (i.e. success, or the + failure reason). Recored at the end of each (weekly) topics calculation. + </summary> +</histogram> + +<histogram + name="BrowsingTopics.EpochTopicsCalculation.EligibleDistinctHistoryHostsCount" + units="hosts" expires_after="2023-03-14"> + <owner>yaoxia@chromium.org</owner> + <owner>jkarlin@chromium.org</owner> + <summary> + Records the count of distinct history hosts that are eligible for topics + calculation. Recorded during each (weekly) topics calculation after the + observation domains are derived. In case of a calculation failure (e.g. + permission denied, etc.), this metric won't be recorded. + </summary> +</histogram> + +<histogram + name="BrowsingTopics.EpochTopicsCalculation.ObservationContextDomainsCountPerTopTopic" + units="context domains" expires_after="2023-03-14"> + <owner>yaoxia@chromium.org</owner> + <owner>jkarlin@chromium.org</owner> + <summary> + Records the count of context domains for each of the calculated top topics. + This won't exceed the cap number + `kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToKeepPerTopic`. Recorded + once for each calculated top topics, during each (weekly) topics calculation + after the observation domains are derived. In case of a calculation failure + (e.g. permission denied; candidate topic was blocked; etc.), this metric + won't be recorded. + </summary> +</histogram> + +<histogram + name="BrowsingTopics.EpochTopicsCalculation.TopTopicsCountBeforePadding" + units="topics" expires_after="2023-03-14"> + <owner>yaoxia@chromium.org</owner> + <owner>jkarlin@chromium.org</owner> + <summary> + Records the count of derived top topics before random ones are padded. + Recorded during each (weekly) topics calculation after the top topics are + derived. In case of a calculation failure (e.g. permission denied, etc.), + this metric won't be recorded. + </summary> +</histogram> + <histogram name="BrowsingTopics.SiteDataStorage.InitStatus" - enum="BooleanSuccess" expires_after="2023-02-10"> + enum="BooleanSuccess" expires_after="2023-03-14"> <owner>yaoxia@chromium.org</owner> <owner>jkarlin@chromium.org</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml index c22226c..99fead3c 100644 --- a/tools/metrics/histograms/metadata/enterprise/histograms.xml +++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -772,6 +772,16 @@ </summary> </histogram> +<histogram name="Enterprise.Dlp.CaptureModeInitWarned" enum="BooleanWarned" + expires_after="2022-12-01"> + <owner>aidazolic@chromium.org</owner> + <owner>chromeos-dlp@google.com</owner> + <summary> + Recorded when the user is warned by Data Leak Prevention before capture mode + initialization. + </summary> +</histogram> + <histogram name="Enterprise.Dlp.ClipboardReadBlocked" enum="BooleanBlocked" expires_after="2022-12-01"> <owner>poromov@chromium.org</owner> @@ -1012,6 +1022,40 @@ </summary> </histogram> +<histogram name="Enterprise.Dlp.ScreenshotWarned" enum="BooleanWarned" + expires_after="2022-12-01"> + <owner>aidazolic@chromium.org</owner> + <owner>chromeos-dlp@google.com</owner> + <summary> + Recorded when there is a "warn" level Data Leak Prevention + evaluation for taking a screenshot or a video capture. + </summary> +</histogram> + +<histogram name="Enterprise.Dlp.ScreenshotWarnProceeded" enum="Boolean" + expires_after="2022-12-01"> + <owner>aidazolic@chromium.org</owner> + <owner>chromeos-dlp@google.com</owner> + <summary> + Recorded when the user proceeded with (True) or canceled (False) the action + after a Data Leak Prevention warning shown at the capture mode + initialization or before taking a screenshot or a video capture. + </summary> +</histogram> + +<histogram name="Enterprise.Dlp.ScreenshotWarnSilentProceeded" enum="Boolean" + expires_after="2022-12-01"> + <owner>aidazolic@chromium.org</owner> + <owner>chromeos-dlp@google.com</owner> + <summary> + Recorded when there is a "warn" level Data Leak Prevention + evaluation for initiating capture mode, or taking a screenshot or a video + capture, but it is caused by the content that the user was already warned + about and decided to proceed, so no new warning is shown and the screen + share is silently allowed. + </summary> +</histogram> + <histogram name="Enterprise.Dlp.VideoCaptureInterrupted" enum="Boolean" expires_after="2022-12-01"> <owner>poromov@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml index c7e3afa9..2b4deff4 100644 --- a/tools/metrics/histograms/metadata/file/histograms.xml +++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -220,7 +220,7 @@ </histogram> <histogram name="FileBrowser.Downloads.DirectorySizeMiB" units="MiB" - expires_after="2022-04-17"> + expires_after="2023-04-17"> <owner>simmonsjosh@google.com</owner> <owner>src/ui/file_manager/OWNERS</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml index 115e6854..0620137e 100644 --- a/tools/metrics/histograms/metadata/others/histograms.xml +++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -3111,6 +3111,17 @@ </summary> </histogram> +<histogram name="Conversions.AttributionSrcRequestStatus" + enum="ConversionAttributionSrcRequestStatus" expires_after="2022-09-12"> + <owner>linnan@chromium.org</owner> + <owner>johnidel@chromium.org</owner> + <owner>measurement-api-dev+metrics@google.com</owner> + <summary> + Records the status of the attributionsrc requests. Recorded when the request + started and finished. + </summary> +</histogram> + <histogram name="Conversions.ClearDataTime" units="ms" expires_after="M105"> <owner>johnidel@chromium.org</owner> <owner>csharrison@chromium.org</owner> @@ -3271,6 +3282,28 @@ </summary> </histogram> +<histogram name="Conversions.RegisteredSourcesPerDataHost" units="sources" + expires_after="2022-09-12"> + <owner>linnan@chromium.org</owner> + <owner>johnidel@chromium.org</owner> + <owner>measurement-api-dev+metrics@google.com</owner> + <summary> + Measures the number of sources registered per data host. Recorded when the + data host is disconnected or when the data host manager is destroyed. + </summary> +</histogram> + +<histogram name="Conversions.RegisteredTriggersPerDataHost" units="triggers" + expires_after="2022-09-12"> + <owner>linnan@chromium.org</owner> + <owner>johnidel@chromium.org</owner> + <owner>measurement-api-dev+metrics@google.com</owner> + <summary> + Measures the number of triggers registered per data host. Recorded when the + data host is disconnected or when the data host manager is destroyed. + </summary> +</histogram> + <histogram name="Conversions.RegisterImpressionAllowed" enum="BooleanAllowed" expires_after="M105"> <owner>apaseltiner@chromium.org</owner> @@ -6337,7 +6370,7 @@ </histogram> <histogram name="Kiosk.LaunchType" enum="KioskLaunchType" - expires_after="2022-04-24"> + expires_after="2023-04-24"> <owner>xiyuan@chromium.org</owner> <owner>aghuie@chromium.org</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/stability/histograms.xml b/tools/metrics/histograms/metadata/stability/histograms.xml index 9dbdd0cb..1336e7b 100644 --- a/tools/metrics/histograms/metadata/stability/histograms.xml +++ b/tools/metrics/histograms/metadata/stability/histograms.xml
@@ -279,7 +279,7 @@ </histogram> <histogram name="Stability.DebugScenario.Navigation" enum="DebugScenario" - expires_after="2022-03-19"> + expires_after="2022-12-19"> <owner>nasko@chromium.org</owner> <owner>altimin@chromium.org</owner> <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index a9a4d21..76e8ab8 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@ "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell" }, "win": { - "hash": "1f86891669f4e4b3a88046dd1fa78d8e9f259c59", - "remote_path": "perfetto_binaries/trace_processor_shell/win/7c9c7d6662bdceb620481ce8826d305ffd24f25e/trace_processor_shell.exe" + "hash": "15b2482fb98e1aa361460722abf1ecb3ffab894e", + "remote_path": "perfetto_binaries/trace_processor_shell/win/9b8eaa21f16d10cffefdf758a8b900e014e4788d/trace_processor_shell.exe" }, "linux_arm": { "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893", "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell" }, "mac": { - "hash": "5d82d23fadbbe147410266f39b9a72fbb5391cf7", - "remote_path": "perfetto_binaries/trace_processor_shell/mac/7c9c7d6662bdceb620481ce8826d305ffd24f25e/trace_processor_shell" + "hash": "72ae2950a4333c3450bee06ba63cbe4578da7ddb", + "remote_path": "perfetto_binaries/trace_processor_shell/mac/9b8eaa21f16d10cffefdf758a8b900e014e4788d/trace_processor_shell" }, "mac_arm64": { "hash": "c0397e87456ad6c6a7aa0133e5b81c97adbab4ab", "remote_path": "perfetto_binaries/trace_processor_shell/mac_arm64/cefb3e0ec3a0580c996f801e854fe02963c03d5c/trace_processor_shell" }, "linux": { - "hash": "6c5897970b7fe316880c315f4b40a5a68f6a0ddd", - "remote_path": "perfetto_binaries/trace_processor_shell/linux/6570222e0f7fd09e13b668cdc69d378a75295c29/trace_processor_shell" + "hash": "6b0a9130ba5c2cc1c6ff67bf6fa3064297bc7f19", + "remote_path": "perfetto_binaries/trace_processor_shell/linux/9b8eaa21f16d10cffefdf758a8b900e014e4788d/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java b/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java index 7032246..183d401f 100644 --- a/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java +++ b/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java
@@ -12,6 +12,7 @@ import android.net.Uri; import android.os.Build.VERSION_CODES; import android.os.SystemClock; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Pair; import android.view.DragEvent; @@ -162,7 +163,8 @@ return ClipData.newUri( ContextUtils.getApplicationContext().getContentResolver(), null, uri); case DragTargetType.LINK: - // TODO(https://crbug.com/1289393): Handle link dragging. + // TODO(https://crbug.com/1298308): Handle image link dragging. + return ClipData.newPlainText(null, getTextForLinkData(dropData)); case DragTargetType.INVALID: return null; case DragTargetType.NUM_ENTRIES: @@ -178,6 +180,8 @@ return View.DRAG_FLAG_GLOBAL; } else if (dropData.hasImage()) { return View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ; + } else if (dropData.hasLink()) { + return View.DRAG_FLAG_GLOBAL; } else { return 0; } @@ -300,8 +304,9 @@ return DragTargetType.TEXT; } else if (dropDataAndroid.hasImage()) { return DragTargetType.IMAGE; + } else if (dropDataAndroid.hasLink()) { + return DragTargetType.LINK; } else { - // TODO(https://crbug.com/1289393): Handle link dragging. return DragTargetType.INVALID; } } @@ -310,6 +315,15 @@ return resources.getDimensionPixelSize(R.dimen.drag_shadow_min_width); } + /** + * Return the text to be dropped when {@link DropDataAndroid} contains a link. + */ + static String getTextForLinkData(DropDataAndroid dropData) { + assert dropData.hasLink(); + if (TextUtils.isEmpty(dropData.text)) return dropData.gurl.getSpec(); + return dropData.text + "\n" + dropData.gurl.getSpec(); + } + private void reset() { mShadowHeight = 0; mShadowWidth = 0;
diff --git a/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java b/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java index f3c2aa6..9de13a5 100644 --- a/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java +++ b/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java
@@ -126,24 +126,34 @@ } /** - * Link dragging is not supported yet, adding this test to make sure status are handled - * correctly. - * TODO(https://crbug.com/1289393): Handle link dragging. + * Image link dragging is not supported yet. + * TODO(https://crbug.com/1298308): Handle image link dragging. */ @Test - public void testStartDragAndDrop_Link() { + public void testStartDragAndDrop_TextLink() { final View containerView = new View(mContext); final Bitmap shadowImage = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8); + final DropDataAndroid dropData = DropDataAndroid.create( + "text", JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), null, null); - final DropDataAndroid linkDropData = DropDataAndroid.create( - "", JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), null, null); - mDragAndDropDelegateImpl.startDragAndDrop(containerView, shadowImage, linkDropData); - Assert.assertEquals( - "Drag link is not supported.", 0, mDragAndDropDelegateImpl.getDragShadowWidth()); - Assert.assertEquals( - "Drag link is not supported.", 0, mDragAndDropDelegateImpl.getDragShadowHeight()); - Assert.assertFalse("Drag Link is not supported.", mDragAndDropDelegateImpl.isDragStarted()); - assertDragTypeNotRecorded("Drag didn't started."); + mDragAndDropDelegateImpl.startDragAndDrop(containerView, shadowImage, dropData); + + Assert.assertTrue("Drag should start.", mDragAndDropDelegateImpl.isDragStarted()); + Assert.assertEquals("Drag shadow width does not match. Should not resize for text link.", + 100, mDragAndDropDelegateImpl.getDragShadowWidth()); + Assert.assertEquals("Drag shadow height does not match. Should not resize for text link.", + 200, mDragAndDropDelegateImpl.getDragShadowHeight()); + assertDragTypeNotRecorded("Drag did not end."); + + mDragAndDropDelegateImpl.onDrag(containerView, mockDragEvent(DragEvent.ACTION_DRAG_ENDED)); + + Assert.assertFalse("Drag should end.", mDragAndDropDelegateImpl.isDragStarted()); + Assert.assertEquals("Drag shadow width should be reset.", 0, + mDragAndDropDelegateImpl.getDragShadowWidth()); + Assert.assertEquals("Drag shadow height should be reset.", 0, + mDragAndDropDelegateImpl.getDragShadowHeight()); + assertDragTypeRecorded(DragTargetType.LINK); + assertDragOutsideWebContentHistogramsRecorded(/*dropResult=*/false); } @Test @@ -292,6 +302,26 @@ /*expectedWidth=*/7, /*expectedHeight=*/700); } + @Test + public void testTextForLinkData_UrlWithNoTitle() { + final DropDataAndroid dropData = DropDataAndroid.create( + "", JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), null, null); + + String text = DragAndDropDelegateImpl.getTextForLinkData(dropData); + Assert.assertEquals("Text should match.", JUnitTestGURLs.EXAMPLE_URL, text); + } + + @Test + public void testTextForLinkData_UrlWithTitle() { + String linkTitle = "Link text"; + final DropDataAndroid dropData = DropDataAndroid.create( + linkTitle, JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), null, null); + + String text = DragAndDropDelegateImpl.getTextForLinkData(dropData); + Assert.assertEquals( + "Text should match.", linkTitle + "\n" + JUnitTestGURLs.EXAMPLE_URL, text); + } + private void doTestResizeShadowImage( String testcase, int width, int height, int expectedWidth, int expectedHeight) { Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn index 85a49141..be753e5e 100644 --- a/ui/base/BUILD.gn +++ b/ui/base/BUILD.gn
@@ -348,6 +348,8 @@ "pointer/touch_editing_controller.h", "text/bytes_formatting.cc", "text/bytes_formatting.h", + "themed_vector_icon.cc", + "themed_vector_icon.h", "ui_base_types.cc", "ui_base_types.h", "user_activity/user_activity_detector.cc", @@ -458,6 +460,7 @@ "//ui/base:data_exchange", "//ui/base/clipboard:clipboard_types", "//ui/base/dragdrop/mojom", + "//ui/color", "//ui/display", "//ui/display/util:gpu_info_util", "//ui/events", @@ -932,7 +935,10 @@ ] } if (!is_ios) { - sources += [ "user_activity/user_activity_detector_unittest.cc" ] + sources += [ + "themed_vector_icon_unittest.cc", + "user_activity/user_activity_detector_unittest.cc", + ] if (is_mac) { sources += [ @@ -950,6 +956,7 @@ "//base", "//base/test:test_support", "//build:chromeos_buildflags", + "//components/vector_icons", "//mojo/core/embedder", "//net", "//net:test_support",
diff --git a/ui/base/DEPS b/ui/base/DEPS index 03a6e31..d4d19c59 100644 --- a/ui/base/DEPS +++ b/ui/base/DEPS
@@ -1,4 +1,5 @@ include_rules = [ + "+components/vector_icons", "+media/media_buildflags.h", "+net", "+skia/ext",
diff --git a/ui/base/models/image_model.cc b/ui/base/models/image_model.cc index 8d7c2b1..27cc58b 100644 --- a/ui/base/models/image_model.cc +++ b/ui/base/models/image_model.cc
@@ -10,6 +10,10 @@ #include "ui/color/color_id.h" #include "ui/gfx/vector_icon_utils.h" +#if !BUILDFLAG(IS_IOS) +#include "ui/base/themed_vector_icon.h" +#endif + namespace ui { VectorIconModel::VectorIconModel() = default; @@ -160,6 +164,24 @@ return !(*this == other); } +#if !BUILDFLAG(IS_IOS) +gfx::ImageSkia ImageModel::Rasterize( + const ui::ColorProvider* color_provider) const { + if (IsImage()) + return GetImage().AsImageSkia(); + + if (IsVectorIcon()) { + DCHECK(color_provider); + return ThemedVectorIcon(GetVectorIcon()).GetImageSkia(color_provider); + } + + if (IsImageGenerator()) + return GetImageGenerator().Run(color_provider); + + return gfx::ImageSkia(); +} +#endif + ImageModel::ImageGeneratorAndSize::ImageGeneratorAndSize( ImageGenerator generator, gfx::Size size)
diff --git a/ui/base/models/image_model.h b/ui/base/models/image_model.h index 47fee6d..926159ff 100644 --- a/ui/base/models/image_model.h +++ b/ui/base/models/image_model.h
@@ -122,6 +122,11 @@ bool operator==(const ImageModel& other) const; bool operator!=(const ImageModel& other) const; +#if !BUILDFLAG(IS_IOS) + // Rasterizes if necessary. + gfx::ImageSkia Rasterize(const ui::ColorProvider* color_provider) const; +#endif + private: struct ImageGeneratorAndSize { ImageGeneratorAndSize(ImageGenerator generator, gfx::Size size);
diff --git a/ui/base/models/image_model_unittest.cc b/ui/base/models/image_model_unittest.cc index d28ed91..53a06f4 100644 --- a/ui/base/models/image_model_unittest.cc +++ b/ui/base/models/image_model_unittest.cc
@@ -6,8 +6,10 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "components/vector_icons/vector_icons.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/color/color_id.h" +#include "ui/color/color_provider.h" #include "ui/gfx/image/image_unittest_util.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/vector_icon_types.h" @@ -214,4 +216,28 @@ EXPECT_EQ(image_model_src, image_model_dest); } +#if !BUILDFLAG(IS_IOS) +TEST(ImageModelTest, ShouldRasterizeEmptyModel) { + gfx::ImageSkia image_skia = ui::ImageModel().Rasterize(nullptr); + EXPECT_TRUE(image_skia.isNull()); +} + +TEST(ImageModelTest, ShouldRasterizeVectorIcon) { + ui::ColorProvider color_provider; + color_provider.GenerateColorMap(); + gfx::ImageSkia image_skia = + ui::ImageModel::FromVectorIcon(vector_icons::kSyncIcon) + .Rasterize(&color_provider); + EXPECT_FALSE(image_skia.isNull()); +} + +TEST(ImageModelTest, ShouldRasterizeImage) { + gfx::Image image = gfx::test::CreateImage(16, 16); + gfx::ImageSkia image_skia = + ui::ImageModel::FromImage(image).Rasterize(nullptr); + EXPECT_FALSE(image_skia.isNull()); + EXPECT_TRUE(image_skia.BackedBySameObjectAs(image.AsImageSkia())); +} +#endif // !BUILDFLAG(IS_IOS) + } // namespace ui
diff --git a/ui/native_theme/themed_vector_icon.cc b/ui/base/themed_vector_icon.cc similarity index 98% rename from ui/native_theme/themed_vector_icon.cc rename to ui/base/themed_vector_icon.cc index 3f2f63e..0686fbda 100644 --- a/ui/native_theme/themed_vector_icon.cc +++ b/ui/base/themed_vector_icon.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/native_theme/themed_vector_icon.h" +#include "ui/base/themed_vector_icon.h" #include "ui/color/color_provider.h" #include "ui/gfx/paint_vector_icon.h"
diff --git a/ui/native_theme/themed_vector_icon.h b/ui/base/themed_vector_icon.h similarity index 89% rename from ui/native_theme/themed_vector_icon.h rename to ui/base/themed_vector_icon.h index 4bc6c7e..0e4e953 100644 --- a/ui/native_theme/themed_vector_icon.h +++ b/ui/base/themed_vector_icon.h
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_NATIVE_THEME_THEMED_VECTOR_ICON_H_ -#define UI_NATIVE_THEME_THEMED_VECTOR_ICON_H_ +#ifndef UI_BASE_THEMED_VECTOR_ICON_H_ +#define UI_BASE_THEMED_VECTOR_ICON_H_ +#include "base/component_export.h" #include "base/memory/raw_ptr.h" #include "third_party/abseil-cpp/absl/types/variant.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/models/image_model.h" #include "ui/color/color_id.h" -#include "ui/native_theme/native_theme_export.h" namespace gfx { class ImageSkia; @@ -21,7 +21,7 @@ class ColorProvider; -class NATIVE_THEME_EXPORT ThemedVectorIcon { +class COMPONENT_EXPORT(UI_BASE) ThemedVectorIcon { public: ThemedVectorIcon(); explicit ThemedVectorIcon(const gfx::VectorIcon* icon, @@ -63,4 +63,4 @@ } // namespace ui -#endif // UI_NATIVE_THEME_THEMED_VECTOR_ICON_H_ +#endif // UI_BASE_THEMED_VECTOR_ICON_H_
diff --git a/ui/native_theme/themed_vector_icon_unittest.cc b/ui/base/themed_vector_icon_unittest.cc similarity index 95% rename from ui/native_theme/themed_vector_icon_unittest.cc rename to ui/base/themed_vector_icon_unittest.cc index 544dbd6..f8fbb38c 100644 --- a/ui/native_theme/themed_vector_icon_unittest.cc +++ b/ui/base/themed_vector_icon_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/native_theme/themed_vector_icon.h" +#include "ui/base/themed_vector_icon.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/paint_vector_icon.h"
diff --git a/ui/color/color_id.h b/ui/color/color_id.h index 6db84ac..2d16acd 100644 --- a/ui/color/color_id.h +++ b/ui/color/color_id.h
@@ -116,12 +116,6 @@ E_CPONLY(kColorOverlayScrollbarStrokeHoveredDark) \ E_CPONLY(kColorOverlayScrollbarStrokeHoveredLight) \ E_CPONLY(kColorProgressBar) \ - E_CPONLY(kColorPwaSecurityChipForeground) \ - E_CPONLY(kColorPwaSecurityChipForegroundDangerous) \ - E_CPONLY(kColorPwaSecurityChipForegroundPolicyCert) \ - E_CPONLY(kColorPwaSecurityChipForegroundSecure) \ - E_CPONLY(kColorPwaToolbarBackground) \ - E_CPONLY(kColorPwaToolbarForeground) \ E_CPONLY(kColorSeparator) \ E_CPONLY(kColorShadowBase) \ E_CPONLY(kColorShadowValueAmbientShadowElevationSixteen) \
diff --git a/ui/color/ui_color_mixer.cc b/ui/color/ui_color_mixer.cc index 2c49e13..b438c8f 100644 --- a/ui/color/ui_color_mixer.cc +++ b/ui/color/ui_color_mixer.cc
@@ -135,13 +135,6 @@ SetAlpha(GetColorWithMaxContrast(kColorOverlayScrollbarFillHoveredLight), gfx::kGoogleGreyAlpha500); mixer[kColorProgressBar] = {kColorAccent}; - mixer[kColorPwaSecurityChipForeground] = {kColorSecondaryForeground}; - mixer[kColorPwaSecurityChipForegroundDangerous] = {kColorAlertHighSeverity}; - mixer[kColorPwaSecurityChipForegroundPolicyCert] = {kColorDisabledForeground}; - mixer[kColorPwaSecurityChipForegroundSecure] = { - kColorPwaSecurityChipForeground}; - mixer[kColorPwaToolbarBackground] = {kColorEndpointBackground}; - mixer[kColorPwaToolbarForeground] = {kColorEndpointForeground}; mixer[kColorSeparator] = {kColorMidground}; mixer[kColorShadowBase] = {dark_mode ? SK_ColorBLACK : gfx::kGoogleGrey800}; mixer[kColorShadowValueAmbientShadowElevationThree] =
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc index 4124a5f..6805de3 100644 --- a/ui/gl/gl_surface_egl.cc +++ b/ui/gl/gl_surface_egl.cc
@@ -635,7 +635,8 @@ EGLConfig ChooseConfig(GLSurfaceFormat format, bool surfaceless, - bool offscreen) { + bool offscreen, + EGLint visual_id) { // Choose an EGL configuration. // On X this is only used for PBuffer surfaces. @@ -722,7 +723,7 @@ } std::unique_ptr<EGLConfig[]> matching_configs(new EGLConfig[num_configs]); - if (want_rgb565) { + if (want_rgb565 || visual_id >= 0) { config_size = num_configs; config_data = matching_configs.get(); } @@ -771,6 +772,16 @@ return config; } } + } else if (visual_id >= 0) { + for (int i = 0; i < num_configs; i++) { + EGLint id; + if (eglGetConfigAttrib(g_egl_display, matching_configs[i], + EGL_NATIVE_VISUAL_ID, &id) && + id == visual_id) { + config = matching_configs[i]; + break; + } + } } return config; } @@ -990,11 +1001,16 @@ EGLConfig GLSurfaceEGL::GetConfig() { if (!config_) { - config_ = ChooseConfig(format_, IsSurfaceless(), IsOffscreen()); + config_ = ChooseConfig(format_, IsSurfaceless(), IsOffscreen(), + GetNativeVisualID()); } return config_; } +EGLint GLSurfaceEGL::GetNativeVisualID() const { + return -1; +} + // static bool GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform native_display, uint64_t system_device_id) {
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h index 87f34e2..ec63bbd 100644 --- a/ui/gl/gl_surface_egl.h +++ b/ui/gl/gl_surface_egl.h
@@ -92,6 +92,7 @@ GLSurfaceEGL(const GLSurfaceEGL&) = delete; GLSurfaceEGL& operator=(const GLSurfaceEGL&) = delete; + virtual EGLint GetNativeVisualID() const; // Implement GLSurface. EGLDisplay GetDisplay() override;
diff --git a/ui/gl/gl_surface_egl_x11.cc b/ui/gl/gl_surface_egl_x11.cc index d85d999..e39323f7 100644 --- a/ui/gl/gl_surface_egl_x11.cc +++ b/ui/gl/gl_surface_egl_x11.cc
@@ -66,6 +66,13 @@ return result; } +EGLint NativeViewGLSurfaceEGLX11::GetNativeVisualID() const { + x11::VisualId visual_id; + ui::XVisualManager::GetInstance()->ChooseVisualForWindow( + true, &visual_id, nullptr, nullptr, nullptr); + return static_cast<EGLint>(visual_id); +} + NativeViewGLSurfaceEGLX11::~NativeViewGLSurfaceEGLX11() { Destroy(); }
diff --git a/ui/gl/gl_surface_egl_x11.h b/ui/gl/gl_surface_egl_x11.h index 2c16496..efaffcd 100644 --- a/ui/gl/gl_surface_egl_x11.h +++ b/ui/gl/gl_surface_egl_x11.h
@@ -27,6 +27,7 @@ bool Initialize(GLSurfaceFormat format) override; void Destroy() override; gfx::SwapResult SwapBuffers(PresentationCallback callback) override; + EGLint GetNativeVisualID() const override; protected: ~NativeViewGLSurfaceEGLX11() override;
diff --git a/ui/native_theme/BUILD.gn b/ui/native_theme/BUILD.gn index 377d54b1..33ca89b 100644 --- a/ui/native_theme/BUILD.gn +++ b/ui/native_theme/BUILD.gn
@@ -22,8 +22,6 @@ "native_theme_observer.h", "native_theme_utils.cc", "native_theme_utils.h", - "themed_vector_icon.cc", - "themed_vector_icon.h", ] if (is_android) { @@ -126,7 +124,7 @@ test("native_theme_unittests") { use_xvfb = use_xvfb_in_this_config - sources = [ "themed_vector_icon_unittest.cc" ] + sources = [] if (use_aura) { sources += [ "native_theme_aura_unittest.cc" ]
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn index b9a8822..01c9b4e 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn
@@ -200,7 +200,6 @@ "focus/focus_manager_factory.h", "focus/focus_search.h", "focus/widget_focus_manager.h", - "image_model_utils.h", "input_event_activation_protector.h", "interaction/element_tracker_views.h", "interaction/interaction_sequence_views.h", @@ -413,7 +412,6 @@ "focus/focus_manager_factory.cc", "focus/focus_search.cc", "focus/widget_focus_manager.cc", - "image_model_utils.cc", "input_event_activation_protector.cc", "interaction/element_tracker_views.cc", "interaction/interaction_sequence_views.cc", @@ -1141,7 +1139,6 @@ "event_monitor_unittest.cc", "focus/focus_manager_unittest.cc", "focus/focus_traversal_unittest.cc", - "image_model_utils_unittest.cc", "interaction/element_tracker_views_unittest.cc", "interaction/interaction_sequence_views_unittest.cc", "interaction/interaction_test_util_views_unittest.cc",
diff --git a/ui/views/background.h b/ui/views/background.h index 3e090be..e6a44ed6 100644 --- a/ui/views/background.h +++ b/ui/views/background.h
@@ -11,9 +11,9 @@ #include "build/build_config.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/base/themed_vector_icon.h" #include "ui/color/color_id.h" #include "ui/gfx/color_palette.h" -#include "ui/native_theme/themed_vector_icon.h" #include "ui/views/views_export.h" namespace gfx {
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc index 0142629..9ead354 100644 --- a/ui/views/bubble/bubble_frame_view.cc +++ b/ui/views/bubble/bubble_frame_view.cc
@@ -34,7 +34,6 @@ #include "ui/views/controls/button/image_button_factory.h" #include "ui/views/controls/highlight_path_generator.h" #include "ui/views/controls/image_view.h" -#include "ui/views/image_model_utils.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/layout_provider.h" #include "ui/views/paint_info.h" @@ -294,8 +293,8 @@ DCHECK(GetWidget()); gfx::ImageSkia image; if (GetWidget()->widget_delegate()->ShouldShowWindowIcon()) { - image = GetImageSkiaFromImageModel( - GetWidget()->widget_delegate()->GetWindowIcon(), GetColorProvider()); + image = GetWidget()->widget_delegate()->GetWindowIcon().Rasterize( + GetColorProvider()); } title_icon_->SetImage(&image); }
diff --git a/ui/views/controls/button/image_button.cc b/ui/views/controls/button/image_button.cc index a4b117c..326e2416 100644 --- a/ui/views/controls/button/image_button.cc +++ b/ui/views/controls/button/image_button.cc
@@ -16,7 +16,6 @@ #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/scoped_canvas.h" #include "ui/views/background.h" -#include "ui/views/image_model_utils.h" #include "ui/views/painter.h" #include "ui/views/widget/widget.h" @@ -42,7 +41,7 @@ ImageButton::~ImageButton() = default; gfx::ImageSkia ImageButton::GetImage(ButtonState state) const { - return GetImageSkiaFromImageModel(images_[state], GetColorProvider()); + return images_[state].Rasterize(GetColorProvider()); } void ImageButton::SetImage(ButtonState for_state, const gfx::ImageSkia* image) { @@ -287,11 +286,9 @@ // ToggleImageButton, ImageButton overrides: gfx::ImageSkia ToggleImageButton::GetImage(ButtonState image_state) const { - if (toggled_) { - return GetImageSkiaFromImageModel(alternate_images_[image_state], - GetColorProvider()); - } - return GetImageSkiaFromImageModel(images_[image_state], GetColorProvider()); + if (toggled_) + return alternate_images_[image_state].Rasterize(GetColorProvider()); + return images_[image_state].Rasterize(GetColorProvider()); } void ToggleImageButton::SetImageModel(ButtonState image_state,
diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc index 57e3a7d..2611d27 100644 --- a/ui/views/controls/button/label_button.cc +++ b/ui/views/controls/button/label_button.cc
@@ -27,7 +27,6 @@ #include "ui/views/animation/ink_drop.h" #include "ui/views/background.h" #include "ui/views/controls/button/label_button_border.h" -#include "ui/views/image_model_utils.h" #include "ui/views/painter.h" #include "ui/views/style/platform_style.h" #include "ui/views/view_class_properties.h" @@ -67,8 +66,7 @@ gfx::ImageSkia LabelButton::GetImage(ButtonState for_state) const { for_state = ImageStateForState(for_state); - return GetImageSkiaFromImageModel(button_state_image_models_[for_state], - GetColorProvider()); + return button_state_image_models_[for_state].Rasterize(GetColorProvider()); } void LabelButton::SetImage(ButtonState for_state, const gfx::ImageSkia& image) {
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc index 1e6140cd..288bb30 100644 --- a/ui/views/controls/combobox/combobox.cc +++ b/ui/views/controls/combobox/combobox.cc
@@ -40,7 +40,6 @@ #include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/prefix_selector.h" -#include "ui/views/image_model_utils.h" #include "ui/views/layout/layout_provider.h" #include "ui/views/mouse_constants.h" #include "ui/views/style/platform_style.h" @@ -613,8 +612,7 @@ // Draw the icon. ui::ImageModel icon = GetModel()->GetIconAt(selected_index_); if (!icon.IsEmpty()) { - gfx::ImageSkia icon_skia = - GetImageSkiaFromImageModel(icon, GetColorProvider()); + gfx::ImageSkia icon_skia = icon.Rasterize(GetColorProvider()); int icon_y = y + (contents_height - icon_skia.height()) / 2; gfx::Rect icon_bounds(x, icon_y, icon_skia.width(), icon_skia.height()); AdjustBoundsForRTLUI(&icon_bounds); @@ -724,7 +722,7 @@ if (!icon.IsEmpty()) { gfx::ImageSkia icon_skia; if (GetWidget()) - icon_skia = GetImageSkiaFromImageModel(icon, GetColorProvider()); + icon_skia = icon.Rasterize(GetColorProvider()); item_width += icon_skia.width() + LayoutProvider::Get()->GetDistanceMetric( DISTANCE_RELATED_LABEL_HORIZONTAL);
diff --git a/ui/views/controls/image_view.cc b/ui/views/controls/image_view.cc index a0825bd3..1cbd698 100644 --- a/ui/views/controls/image_view.cc +++ b/ui/views/controls/image_view.cc
@@ -12,11 +12,10 @@ #include "cc/paint/paint_flags.h" #include "skia/ext/image_operations.h" #include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/base/themed_vector_icon.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image_skia_rep.h" #include "ui/gfx/paint_vector_icon.h" -#include "ui/native_theme/themed_vector_icon.h" -#include "ui/views/image_model_utils.h" namespace views { @@ -51,7 +50,7 @@ } gfx::ImageSkia ImageView::GetImage() const { - return views::GetImageSkiaFromImageModel(image_model_, GetColorProvider()); + return image_model_.Rasterize(GetColorProvider()); } ui::ImageModel ImageView::GetImageModel() const { @@ -131,8 +130,7 @@ return gfx::ImageSkia(); if (image_model_.IsImage() || image_model_.IsImageGenerator()) { - const gfx::ImageSkia image = - views::GetImageSkiaFromImageModel(image_model_, GetColorProvider()); + const gfx::ImageSkia image = image_model_.Rasterize(GetColorProvider()); if (image.isNull()) return image; @@ -154,8 +152,7 @@ scaled_size.width(), scaled_size.height()), scale)); } else if (scaled_image_.isNull()) { - scaled_image_ = - views::GetImageSkiaFromImageModel(image_model_, GetColorProvider()); + scaled_image_ = image_model_.Rasterize(GetColorProvider()); } return scaled_image_; }
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc index 9f8112be..6034e856 100644 --- a/ui/views/controls/menu/menu_item_view.cc +++ b/ui/views/controls/menu/menu_item_view.cc
@@ -49,7 +49,6 @@ #include "ui/views/controls/menu/new_badge.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/controls/separator.h" -#include "ui/views/image_model_utils.h" #include "ui/views/style/typography.h" #include "ui/views/vector_icons.h" #include "ui/views/view_class_properties.h" @@ -1142,8 +1141,7 @@ } if (!minor_icon.IsEmpty()) { - const gfx::ImageSkia image = - GetImageSkiaFromImageModel(minor_icon, GetColorProvider()); + const gfx::ImageSkia image = minor_icon.Rasterize(GetColorProvider()); int image_x = GetMirroredRect(minor_text_bounds).right() - render_text->GetContentWidth() -
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h index 8edc5a3c..4fb6b50d 100644 --- a/ui/views/controls/menu/menu_item_view.h +++ b/ui/views/controls/menu/menu_item_view.h
@@ -16,9 +16,9 @@ #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/models/menu_separator_types.h" +#include "ui/base/themed_vector_icon.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/image/image_skia.h" -#include "ui/native_theme/themed_vector_icon.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_types.h" #include "ui/views/view.h"
diff --git a/ui/views/controls/menu/menu_item_view_unittest.cc b/ui/views/controls/menu/menu_item_view_unittest.cc index 2df7dac..0a1939d 100644 --- a/ui/views/controls/menu/menu_item_view_unittest.cc +++ b/ui/views/controls/menu/menu_item_view_unittest.cc
@@ -13,10 +13,10 @@ #include "base/test/bind.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/themed_vector_icon.h" #include "ui/compositor/canvas_painter.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/insets.h" -#include "ui/native_theme/themed_vector_icon.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/menu/menu_runner.h"
diff --git a/ui/views/controls/table/table_view.cc b/ui/views/controls/table/table_view.cc index 42fa1ad1..74e2447c2 100644 --- a/ui/views/controls/table/table_view.cc +++ b/ui/views/controls/table/table_view.cc
@@ -46,7 +46,6 @@ #include "ui/views/controls/table/table_utils.h" #include "ui/views/controls/table/table_view_observer.h" #include "ui/views/focus/focus_manager.h" -#include "ui/views/image_model_utils.h" #include "ui/views/layout/layout_provider.h" #include "ui/views/style/platform_style.h" #include "ui/views/style/typography.h" @@ -941,8 +940,8 @@ // Always paint the icon in the first visible column. if (j == 0 && table_type_ == ICON_AND_TEXT) { - gfx::ImageSkia image = views::GetImageSkiaFromImageModel( - model_->GetIcon(model_index), GetColorProvider()); + gfx::ImageSkia image = + model_->GetIcon(model_index).Rasterize(GetColorProvider()); if (!image.isNull()) { int image_x = GetMirroredXWithWidthInView(text_x, ui::TableModel::kIconSize);
diff --git a/ui/views/image_model_utils.cc b/ui/views/image_model_utils.cc deleted file mode 100644 index c4264bca..0000000 --- a/ui/views/image_model_utils.cc +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/image_model_utils.h" - -#include "ui/native_theme/themed_vector_icon.h" - -namespace views { - -gfx::ImageSkia GetImageSkiaFromImageModel( - const ui::ImageModel& model, - const ui::ColorProvider* color_provider) { - if (model.IsImage()) - return model.GetImage().AsImageSkia(); - - if (model.IsVectorIcon()) { - DCHECK(color_provider); - return ui::ThemedVectorIcon(model.GetVectorIcon()) - .GetImageSkia(color_provider); - } - - if (model.IsImageGenerator()) - return model.GetImageGenerator().Run(color_provider); - - return gfx::ImageSkia(); -} - -} // namespace views
diff --git a/ui/views/image_model_utils.h b/ui/views/image_model_utils.h deleted file mode 100644 index 243ebed..0000000 --- a/ui/views/image_model_utils.h +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_IMAGE_MODEL_UTILS_H_ -#define UI_VIEWS_IMAGE_MODEL_UTILS_H_ - -#include "ui/gfx/image/image_skia.h" -#include "ui/views/views_export.h" - -namespace ui { -class ColorProvider; -class ImageModel; -} // namespace ui - -namespace views { - -// Returns an ImageSkia representation from an ImageModel representation. -// `color_provider` must be non-null if `model` represents a vector icon. If -// `model` is empty, it returns an empty ImageSkia. -VIEWS_EXPORT gfx::ImageSkia GetImageSkiaFromImageModel( - const ui::ImageModel& model, - const ui::ColorProvider* color_provider = nullptr); -} // namespace views - -#endif // UI_VIEWS_IMAGE_MODEL_UTILS_H_
diff --git a/ui/views/image_model_utils_unittest.cc b/ui/views/image_model_utils_unittest.cc deleted file mode 100644 index ebd65f9..0000000 --- a/ui/views/image_model_utils_unittest.cc +++ /dev/null
@@ -1,36 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/image_model_utils.h" - -#include "components/vector_icons/vector_icons.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/models/image_model.h" -#include "ui/color/color_provider.h" -#include "ui/gfx/image/image_unittest_util.h" - -namespace views { - -TEST(GetImageSkiaFromImageModel, ShouldConvertEmptyModel) { - gfx::ImageSkia image_skia = GetImageSkiaFromImageModel(ui::ImageModel()); - EXPECT_TRUE(image_skia.isNull()); -} - -TEST(GetImageSkiaFromImageModel, ShouldConvertVectorIcon) { - ui::ColorProvider color_provider; - color_provider.GenerateColorMap(); - gfx::ImageSkia image_skia = GetImageSkiaFromImageModel( - ui::ImageModel::FromVectorIcon(vector_icons::kSyncIcon), &color_provider); - EXPECT_FALSE(image_skia.isNull()); -} - -TEST(GetImageSkiaFromImageModel, ShouldConvertImage) { - gfx::Image image = gfx::test::CreateImage(16, 16); - gfx::ImageSkia image_skia = - GetImageSkiaFromImageModel(ui::ImageModel::FromImage(image)); - EXPECT_FALSE(image_skia.isNull()); - EXPECT_TRUE(image_skia.BackedBySameObjectAs(image.AsImageSkia())); -} - -} // namespace views
diff --git a/ui/views/views_features.cc b/ui/views/views_features.cc index a494d122..06e664b 100644 --- a/ui/views/views_features.cc +++ b/ui/views/views_features.cc
@@ -29,7 +29,7 @@ // flakes are mopped up, this feature will be removed. // https://crbug.com/1302857 const base::Feature kFullscreenControllerMac{"FullscreenControllerMac", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; #endif } // namespace features
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc index 35cb9c3..59208a1 100644 --- a/ui/views/widget/widget.cc +++ b/ui/views/widget/widget.cc
@@ -35,7 +35,6 @@ #include "ui/views/focus/focus_manager.h" #include "ui/views/focus/focus_manager_factory.h" #include "ui/views/focus/widget_focus_manager.h" -#include "ui/views/image_model_utils.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/any_widget_observer_singleton.h" #include "ui/views/widget/native_widget_private.h" @@ -979,8 +978,8 @@ if (non_client_view_) non_client_view_->UpdateWindowIcon(); - gfx::ImageSkia window_icon = GetImageSkiaFromImageModel( - widget_delegate_->GetWindowIcon(), GetColorProvider()); + gfx::ImageSkia window_icon = + widget_delegate_->GetWindowIcon().Rasterize(GetColorProvider()); // In general, icon information is read from a |widget_delegate_| and then // passed to |native_widget_|. On ChromeOS, for lacros-chrome to support the @@ -995,8 +994,8 @@ window_icon = *icon; } - gfx::ImageSkia app_icon = GetImageSkiaFromImageModel( - widget_delegate_->GetWindowAppIcon(), GetColorProvider()); + gfx::ImageSkia app_icon = + widget_delegate_->GetWindowAppIcon().Rasterize(GetColorProvider()); if (app_icon.isNull()) { const gfx::ImageSkia* icon = native_widget_->GetWindowAppIcon(); if (icon && !icon->isNull())
diff --git a/ui/views/widget/widget_delegate_unittest.cc b/ui/views/widget/widget_delegate_unittest.cc index 3b9cc7b7..7c79bc7 100644 --- a/ui/views/widget/widget_delegate_unittest.cc +++ b/ui/views/widget/widget_delegate_unittest.cc
@@ -10,7 +10,6 @@ #include "ui/base/models/image_model.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_unittest_util.h" -#include "ui/views/image_model_utils.h" #include "ui/views/test/views_test_base.h" #include "ui/views/view.h" #include "ui/views/view_tracker.h" @@ -110,10 +109,11 @@ delegate->SetIcon(window_icon); gfx::ImageSkia app_icon = gfx::test::CreateImageSkia(48, 48); delegate->SetAppIcon(app_icon); - EXPECT_TRUE(GetImageSkiaFromImageModel(delegate->GetWindowIcon(), nullptr) - .BackedBySameObjectAs(window_icon)); - EXPECT_TRUE(GetImageSkiaFromImageModel(delegate->GetWindowAppIcon(), nullptr) - .BackedBySameObjectAs(app_icon)); + EXPECT_TRUE(delegate->GetWindowIcon().Rasterize(nullptr).BackedBySameObjectAs( + window_icon)); + EXPECT_TRUE( + delegate->GetWindowAppIcon().Rasterize(nullptr).BackedBySameObjectAs( + app_icon)); } TEST_F(WidgetDelegateTest, AppIconFallsBackToWindowIcon) { @@ -122,8 +122,9 @@ gfx::ImageSkia window_icon = gfx::test::CreateImageSkia(16, 16); delegate->SetIcon(window_icon); // Don't set an independent app icon. - EXPECT_TRUE(GetImageSkiaFromImageModel(delegate->GetWindowAppIcon(), nullptr) - .BackedBySameObjectAs(window_icon)); + EXPECT_TRUE( + delegate->GetWindowAppIcon().Rasterize(nullptr).BackedBySameObjectAs( + window_icon)); } } // namespace
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc index 069f754..ea08b87 100644 --- a/ui/views/widget/widget_unittest.cc +++ b/ui/views/widget/widget_unittest.cc
@@ -3613,8 +3613,17 @@ }; // Deletes a Widget when the bounds change as part of toggling fullscreen. -// This is a regression test for https://crbug.com/1197436 . -TEST_F(DesktopWidgetTest, DeleteInSetFullscreen) { +// This is a regression test for https://crbug.com/1197436. +// Disabled on Mac: This test has historically deleted the Widget not during +// SetFullscreen, but at the end of the test. When the Widget is deleted inside +// SetFullscreen, the test crashes. +// https://crbug.com/1307486 +#if BUILDFLAG(IS_MAC) +#define MAYBE_DeleteInSetFullscreen DISABLED_DeleteInSetFullscreen +#else +#define MAYBE_DeleteInSetFullscreen DeleteInSetFullscreen +#endif +TEST_F(DesktopWidgetTest, MAYBE_DeleteInSetFullscreen) { std::unique_ptr<Widget> widget = std::make_unique<Widget>(); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; @@ -3678,7 +3687,13 @@ EXPECT_FALSE(frame->fullscreen_layout_called()); widget->SetFullscreen(true); widget->Show(); +#if BUILDFLAG(IS_MAC) + // On macOS, a fullscreen layout is triggered from within SetFullscreen. + // https://crbug.com/1307496 + EXPECT_TRUE(frame->fullscreen_layout_called()); +#else EXPECT_TRUE(ViewTestApi(frame).needs_layout()); +#endif widget->LayoutRootViewIfNecessary(); RunPendingMessages();
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS b/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS index 7027ab73..18377141 100644 --- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS +++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS
@@ -1 +1 @@ -file://chromeos/components/multidevice/OWNERS +file://ash/components/multidevice/OWNERS
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js index 6ed61574..6d70beb5f 100644 --- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js +++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js
@@ -6,7 +6,7 @@ // #import {addSingletonGetter} from 'chrome://resources/js/cr.m.js'; // #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js'; // #import 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js'; -// #import 'chrome://resources/mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js'; +// #import 'chrome://resources/mojo/ash/components/multidevice/mojom/multidevice_types.mojom-lite.js'; // #import 'chrome://resources/mojo/ash/services/device_sync/public/mojom/device_sync.mojom-lite.js'; // #import 'chrome://resources/mojo/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom-lite.js'; // clang-format on
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java index 3b27c24..210f5c6 100644 --- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java
@@ -9,6 +9,7 @@ import androidx.fragment.app.FragmentManager; import androidx.test.filters.SmallTest; +import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -23,6 +24,7 @@ import org.chromium.weblayer.Profile; import org.chromium.weblayer.shell.InstrumentationActivity; +import java.util.List; import java.util.concurrent.TimeoutException; /** @@ -32,6 +34,7 @@ public class CookieManagerTest { private CookieManager mCookieManager; private Uri mBaseUri; + private Uri mBaseUriWithPath; @Rule public InstrumentationActivityTestRule mActivityTestRule = @@ -43,6 +46,7 @@ mCookieManager = TestThreadUtils.runOnUiThreadBlockingNoException( () -> { return activity.getBrowser().getProfile().getCookieManager(); }); mBaseUri = Uri.parse(mActivityTestRule.getTestServer().getURL("/")); + mBaseUriWithPath = Uri.parse(mActivityTestRule.getTestServer().getURL("/path")); } @Test @@ -106,6 +110,47 @@ @Test @SmallTest + @MinWebLayerVersion(101) + public void testGetResponseCookiesSimple() throws Exception { + Assert.assertTrue(getResponseCookies().isEmpty()); + Assert.assertTrue(setCookie("foo=")); + Assert.assertThat(getResponseCookies(), + Matchers.containsInAnyOrder("foo=; path=/; domain=127.0.0.1; priority=medium")); + Assert.assertTrue(setCookie("foo=bar")); + Assert.assertThat(getResponseCookies(), + Matchers.containsInAnyOrder("foo=bar; path=/; domain=127.0.0.1; priority=medium")); + + Assert.assertTrue(setCookie("baz=blah")); + Assert.assertThat(getResponseCookies(), + Matchers.containsInAnyOrder("foo=bar; path=/; domain=127.0.0.1; priority=medium", + "baz=blah; path=/; domain=127.0.0.1; priority=medium")); + } + + @Test + @SmallTest + @MinWebLayerVersion(101) + public void testGetResponseCookiesAllAttributes() throws Exception { + Assert.assertTrue(getResponseCookies().isEmpty()); + + // Setting a cookie with all attributes should return the same cookie. + String cookie = "foo=bar; path=/; domain=127.0.0.1; expires=Thu, 15 Jul 2032 00:00:01 GMT; " + + "secure; httponly; samesite=lax; priority=high; sameparty"; + Assert.assertTrue(setCookie(cookie)); + Assert.assertThat(getResponseCookies(), Matchers.containsInAnyOrder(cookie)); + } + + @Test + @SmallTest + @MinWebLayerVersion(101) + public void testGetResponseCookiesWithPath() throws Exception { + Assert.assertTrue(setCookie(mBaseUriWithPath, "foo=bar; path=/path")); + Assert.assertThat(getResponseCookies(mBaseUriWithPath), + Matchers.containsInAnyOrder( + "foo=bar; path=/path; domain=127.0.0.1; priority=medium")); + } + + @Test + @SmallTest public void testCookieChanged() throws Exception { CookieChangedCallbackHelper helper = new CookieChangedCallbackHelper(); TestThreadUtils.runOnUiThreadBlocking( @@ -163,14 +208,26 @@ }); } + private boolean setCookie(Uri uri, String value) throws Exception { + return mActivityTestRule.setCookie(mCookieManager, uri, value); + } + private boolean setCookie(String value) throws Exception { - return mActivityTestRule.setCookie(mCookieManager, mBaseUri, value); + return setCookie(mBaseUri, value); } private String getCookie() throws Exception { return mActivityTestRule.getCookie(mCookieManager, mBaseUri); } + private List<String> getResponseCookies(Uri uri) throws Exception { + return mActivityTestRule.getResponseCookies(mCookieManager, uri); + } + + private List<String> getResponseCookies() throws Exception { + return getResponseCookies(mBaseUri); + } + private static class CookieChangedCallbackHelper extends CookieChangedCallback { private CallbackHelper mCallbackHelper = new CallbackHelper(); private int mCallCount;
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java index c2705ba..91ccdb3f 100644 --- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java
@@ -33,6 +33,8 @@ import org.chromium.weblayer.WebLayer; import org.chromium.weblayer.shell.InstrumentationActivity; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; @@ -289,4 +291,17 @@ callbackHelper.waitForFirst(); return resultHolder[0]; } + + public List<String> getResponseCookies(CookieManager cookieManager, Uri uri) throws Exception { + List<String> finalResult = new ArrayList<>(); + CallbackHelper callbackHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + cookieManager.getResponseCookies(uri, (List<String> result) -> { + finalResult.addAll(result); + callbackHelper.notifyCalled(); + }); + }); + callbackHelper.waitForFirst(); + return finalResult; + } }
diff --git a/weblayer/browser/cookie_manager_impl.cc b/weblayer/browser/cookie_manager_impl.cc index 7743b1e..9a2510f7 100644 --- a/weblayer/browser/cookie_manager_impl.cc +++ b/weblayer/browser/cookie_manager_impl.cc
@@ -7,10 +7,13 @@ #include "build/build_config.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/storage_partition.h" +#include "net/cookies/cookie_constants.h" #include "net/cookies/cookie_util.h" +#include "net/cookies/parsed_cookie.h" #if BUILDFLAG(IS_ANDROID) #include "base/android/callback_android.h" +#include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "weblayer/browser/java/jni/CookieManagerImpl_jni.h" @@ -27,6 +30,32 @@ std::move(callback).Run(net::CanonicalCookie::BuildCookieLine(cookie_list)); } +void GetResponseCookiesComplete( + CookieManager::GetResponseCookiesCallback callback, + const net::CookieAccessResultList& cookies, + const net::CookieAccessResultList& excluded_cookies) { + net::CookieList cookie_list = net::cookie_util::StripAccessResults(cookies); + std::vector<std::string> response_cookies; + for (const net::CanonicalCookie& cookie : cookie_list) { + net::ParsedCookie parsed(""); + parsed.SetName(cookie.Name()); + parsed.SetValue(cookie.Value()); + parsed.SetPath(cookie.Path()); + parsed.SetDomain(cookie.Domain()); + if (!cookie.ExpiryDate().is_null()) + parsed.SetExpires(base::TimeFormatHTTP(cookie.ExpiryDate())); + parsed.SetIsSecure(cookie.IsSecure()); + parsed.SetIsHttpOnly(cookie.IsHttpOnly()); + if (cookie.SameSite() != net::CookieSameSite::UNSPECIFIED) + parsed.SetSameSite(net::CookieSameSiteToString(cookie.SameSite())); + parsed.SetPriority(net::CookiePriorityToString(cookie.Priority())); + parsed.SetIsSameParty(cookie.IsSameParty()); + parsed.SetIsPartitioned(cookie.IsPartitioned()); + response_cookies.push_back(parsed.ToCookieLine()); + } + std::move(callback).Run(response_cookies); +} + #if BUILDFLAG(IS_ANDROID) void OnCookieChangedAndroid( base::android::ScopedJavaGlobalRef<jobject> callback, @@ -38,6 +67,14 @@ env, net::CanonicalCookie::BuildCookieLine({change.cookie})), static_cast<int>(change.cause)); } + +void RunGetResponseCookiesCallback( + const base::android::JavaRef<jobject>& callback, + const std::vector<std::string>& cookies) { + JNIEnv* env = base::android::AttachCurrentThread(); + base::android::RunObjectCallbackAndroid( + callback, base::android::ToJavaArrayOfStrings(env, cookies)); +} #endif void OnCookieChanged(CookieManager::CookieChangedCallbackList* callback_list, @@ -81,6 +118,17 @@ base::BindOnce(&GetCookieComplete, std::move(callback))); } +void CookieManagerImpl::GetResponseCookies( + const GURL& url, + GetResponseCookiesCallback callback) { + browser_context_->GetDefaultStoragePartition() + ->GetCookieManagerForBrowserProcess() + ->GetCookieList( + url, net::CookieOptions::MakeAllInclusive(), + net::CookiePartitionKeyCollection::Todo(), + base::BindOnce(&GetResponseCookiesComplete, std::move(callback))); +} + base::CallbackListSubscription CookieManagerImpl::AddCookieChangedCallback( const GURL& url, const std::string* name, @@ -119,6 +167,16 @@ base::android::ScopedJavaGlobalRef<jobject>(callback))); } +void CookieManagerImpl::GetResponseCookies( + JNIEnv* env, + const base::android::JavaParamRef<jstring>& url, + const base::android::JavaParamRef<jobject>& callback) { + GetResponseCookies( + GURL(ConvertJavaStringToUTF8(url)), + base::BindOnce(&RunGetResponseCookiesCallback, + base::android::ScopedJavaGlobalRef<jobject>(callback))); +} + int CookieManagerImpl::AddCookieChangedCallback( JNIEnv* env, const base::android::JavaParamRef<jstring>& url,
diff --git a/weblayer/browser/cookie_manager_impl.h b/weblayer/browser/cookie_manager_impl.h index 677b96d5..23c56e903 100644 --- a/weblayer/browser/cookie_manager_impl.h +++ b/weblayer/browser/cookie_manager_impl.h
@@ -37,6 +37,8 @@ const std::string& value, SetCookieCallback callback) override; void GetCookie(const GURL& url, GetCookieCallback callback) override; + void GetResponseCookies(const GURL& url, + GetResponseCookiesCallback callback) override; base::CallbackListSubscription AddCookieChangedCallback( const GURL& url, const std::string* name, @@ -50,6 +52,9 @@ void GetCookie(JNIEnv* env, const base::android::JavaParamRef<jstring>& url, const base::android::JavaParamRef<jobject>& callback); + void GetResponseCookies(JNIEnv* env, + const base::android::JavaParamRef<jstring>& url, + const base::android::JavaParamRef<jobject>& callback); int AddCookieChangedCallback( JNIEnv* env, const base::android::JavaParamRef<jstring>& url,
diff --git a/weblayer/browser/download_manager_delegate_impl.cc b/weblayer/browser/download_manager_delegate_impl.cc index 0dc1351..e74bfa9 100644 --- a/weblayer/browser/download_manager_delegate_impl.cc +++ b/weblayer/browser/download_manager_delegate_impl.cc
@@ -90,7 +90,8 @@ download::DownloadItem::TARGET_DISPOSITION_OVERWRITE, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, download::DownloadItem::MixedContentStatus::UNKNOWN, - item->GetForcedFilePath(), absl::nullopt /*download_schedule*/, + item->GetForcedFilePath(), base::FilePath(), + absl::nullopt /*download_schedule*/, download::DOWNLOAD_INTERRUPT_REASON_NONE); return true; } @@ -262,7 +263,7 @@ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, download::DownloadItem::MixedContentStatus::UNKNOWN, suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")), - absl::nullopt /*download_schedule*/, + base::FilePath(), absl::nullopt /*download_schedule*/, download::DOWNLOAD_INTERRUPT_REASON_NONE); }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java index ba3e80a..d820868 100644 --- a/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java +++ b/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java
@@ -19,6 +19,8 @@ import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.List; /** * Implementation of ICookieManager. @@ -54,6 +56,16 @@ } @Override + public void getResponseCookies(String url, IObjectWrapper callback) { + StrictModeWorkaround.apply(); + ValueCallback<List<String>> valueCallback = + (ValueCallback<List<String>>) ObjectWrapper.unwrap(callback, ValueCallback.class); + Callback<String[]> baseCallback = + (String[] result) -> valueCallback.onReceiveValue(Arrays.asList(result)); + CookieManagerImplJni.get().getResponseCookies(mNativeCookieManager, url, baseCallback); + } + + @Override public IObjectWrapper addCookieChangedCallback( String url, String name, ICookieChangedCallbackClient callback) { StrictModeWorkaround.apply(); @@ -105,6 +117,8 @@ boolean setCookie( long nativeCookieManagerImpl, String url, String value, Callback<Boolean> callback); void getCookie(long nativeCookieManagerImpl, String url, Callback<String> callback); + void getResponseCookies( + long nativeCookieManagerImpl, String url, Callback<String[]> callback); int addCookieChangedCallback(long nativeCookieManagerImpl, String url, String name, ICookieChangedCallbackClient callback); void removeCookieChangedCallback(long nativeCookieManagerImpl, int id);
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICookieManager.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICookieManager.aidl index 397eab9..5a648cb9 100644 --- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICookieManager.aidl +++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICookieManager.aidl
@@ -13,4 +13,8 @@ void getCookie(in String url, in IObjectWrapper callback) = 1; IObjectWrapper addCookieChangedCallback(in String url, in String name, ICookieChangedCallbackClient callback) = 2; + + // Added in 101. + void getResponseCookies(in String url, in IObjectWrapper callback) = 3; + }
diff --git a/weblayer/public/cookie_manager.h b/weblayer/public/cookie_manager.h index 0d5027b..07b14cfe 100644 --- a/weblayer/public/cookie_manager.h +++ b/weblayer/public/cookie_manager.h
@@ -31,6 +31,13 @@ using GetCookieCallback = base::OnceCallback<void(const std::string&)>; virtual void GetCookie(const GURL& url, GetCookieCallback callback) = 0; + // Gets the cookies for the given URL in the form of the 'Set-Cookie' HTTP + // response header. + using GetResponseCookiesCallback = + base::OnceCallback<void(const std::vector<std::string>&)>; + virtual void GetResponseCookies(const GURL& url, + GetResponseCookiesCallback callback) = 0; + // Adds a callback to listen for changes to cookies for the given URL. using CookieChangedCallbackList = base::RepeatingCallbackList<void(const net::CookieChangeInfo&)>;
diff --git a/weblayer/public/java/org/chromium/weblayer/CookieManager.java b/weblayer/public/java/org/chromium/weblayer/CookieManager.java index 9c71938..1b3f41c 100644 --- a/weblayer/public/java/org/chromium/weblayer/CookieManager.java +++ b/weblayer/public/java/org/chromium/weblayer/CookieManager.java
@@ -18,6 +18,8 @@ import org.chromium.weblayer_private.interfaces.ObjectWrapper; import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; +import java.util.List; + /** * Manages cookies for a WebLayer profile. */ @@ -89,6 +91,29 @@ } /** + * Gets the cookies for the given URL in the form of the 'Set-Cookie' HTTP response header. + * + * @param uri the URI to get cookies for. + * @param callback a callback to be executed with a list of cookie strings in the format of the + * 'Set-Cookie' HTTP response header. + * @since 101 + */ + public void getResponseCookies(@NonNull Uri uri, @NonNull Callback<List<String>> callback) { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 101) { + throw new UnsupportedOperationException(); + } + try { + ValueCallback<List<String>> valueCallback = (List<String> result) -> { + callback.onResult(result); + }; + mImpl.getResponseCookies(uri.toString(), ObjectWrapper.wrap(valueCallback)); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + /** * Adds a callback to listen for changes to cookies for the given URI. * * @param uri the URI to listen to cookie changes on.
diff --git a/weblayer/public/java/org/chromium/weblayer/WebLayer.java b/weblayer/public/java/org/chromium/weblayer/WebLayer.java index 2d82ee1..3156402 100644 --- a/weblayer/public/java/org/chromium/weblayer/WebLayer.java +++ b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
@@ -586,6 +586,9 @@ */ public String getXClientDataHeader() { ThreadCheck.ensureOnUiThread(); + if (getSupportedMajorVersionInternal() < 101) { + throw new UnsupportedOperationException(); + } try { return mImpl.getXClientDataHeader(); } catch (RemoteException e) {