diff --git a/DEPS b/DEPS index 537569d..2a1a07be 100644 --- a/DEPS +++ b/DEPS
@@ -308,7 +308,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'src_internal_revision': '5f3259bc268e83f385b3d235a66f8fbd380336f8', + 'src_internal_revision': '926c416205c73ac88c808786ed9f9f1e659f6bfc', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. @@ -316,7 +316,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '3ec501ccaac5680794d8f70459fde1e7c120abe6', + 'v8_revision': '705aa9d3796d3e20193c302e1a32fa05ab4363dc', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. @@ -400,7 +400,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': 'b2a86d09d5c4fa554a9a46cb1e94cf8ab52b2d0c', + 'devtools_frontend_revision': '48f965098f72daf2d89b5ded468d3264299fb095', # 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. @@ -1198,7 +1198,7 @@ 'packages': [ { 'package': 'chromium/chrome/android/orderfiles/arm', - 'version': '0ZqeWPme_NoWICTn3mP69dUMzNEc8SQg2ncYTUGhuqsC', + 'version': 'cDOrfG8L66PDDvYfQVMErlbd3NINiQ6d3z2O6s5ihpUC', }, ], 'condition': 'checkout_android and non_git_source', @@ -1209,7 +1209,7 @@ 'packages': [ { 'package': 'chromium/chrome/android/orderfiles/arm64', - 'version': 'jmCOxHNAPV4mccholEiarvq2ltMjET_NGNVgeqkUw30C', + 'version': 'RqiXQV1FtPrr3h-a-JfJ6FFGJj6daDJM58um0L9zEtIC', }, ], 'condition': 'checkout_android and non_git_source', @@ -1220,7 +1220,7 @@ 'packages': [ { 'package': 'chromium/android_webview/tools/orderfiles/arm', - 'version': 'ItzOXXvcPGAdWaR_lJus3nth8LCuRwlyouye2dX-btoC', + 'version': '3-7D9jCzhIvN_rL2RLVPkviI4Fn9KiR1utOLwk2PC6sC', }, ], 'condition': 'checkout_android and non_git_source', @@ -1231,7 +1231,7 @@ 'packages': [ { 'package': 'chromium/android_webview/tools/orderfiles/arm64', - 'version': 'LfyrmvIF5uUxUhirLR85g3ddrr5NhRYcvhBlmfQmhHUC', + 'version': '5QSFarUBfi5vcNoCCTisa4w0aHAjVvVq6WDAA2URqNcC', }, ], 'condition': 'checkout_android and non_git_source', @@ -1403,7 +1403,7 @@ 'packages': [ { 'package': 'chromium/third_party/updater/chromium_linux64', - 'version': 'version:2@1592001', + 'version': 'version:2@1593011', }, ], }, @@ -1536,7 +1536,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_linux64', - 'version': 'dgdBQfA4t9gSHP-5gcxOXXmtDZ-1ni4w0rzM93QgRkIC', + 'version': 's2DtB9zKh7jCfWVKXZf18vXBaqdjGZwpdo9VEUXsCSAC', }, ], }, @@ -1547,7 +1547,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_mac_amd64', - 'version': 'sX1BmmBiwsr1e0dhXY707jUkw-3HsdcCsrQELhIsXPAC', + 'version': 'B0tvS8a1PWD8hFE_XPevWzHTSzHybEJjf8x8ukkNUtAC', }, ], }, @@ -1558,7 +1558,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_mac_arm64', - 'version': 'H_hSnIcVieZKCu4tWZeuQkJskSRFhuox2d2UvU5Roe0C', + 'version': 'nQdNPlrkBvct2Oz-ni-HKiAAVLzCnB0X_tR8H-bYsgwC', }, ], }, @@ -1569,7 +1569,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_win_x86', - 'version': 'ucyYSu0iSSw6C1FUrErObtSuijTN7a2xPFk-Jq2TpzMC', + 'version': 'XGIfEvTTy0qwP3uiXnJjK0W2y9JHpRYgiC5e312ssu8C', }, ], }, @@ -1580,7 +1580,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_win_x86_64', - 'version': '9cMPlsepjcQD5_7wiEKc7YE4DVV1I6IasNXKAhd6-eoC', + 'version': '9sTESi5emsEA5nTDJaItT5Yd4S_WgsGoPdgsa9Pu_IUC', }, ], }, @@ -1616,7 +1616,7 @@ 'packages': [ { 'package': 'chromium/chrome/test/data/variations/cipd', - 'version': '3pIEvnJgjs4yPcLeJfcdPSNjvVVzfGu1FMa-TaNlVsoC', + 'version': 'yjj5vQuYGnuDDrAQQx3iBzCUVs4GbZS0uDHrA5BlLtcC', }, ], 'condition': 'non_git_source', @@ -2617,7 +2617,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '08b509bdad26c91f5f34d9ff97c9362a111f3a72', + Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '3a764e6f6339ae06c19540682dad4a1a9021f072', 'src/base/tracing/test/data': { 'bucket': 'perfetto', @@ -2958,16 +2958,16 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@d61243d1b0837231283e456a908637ead7917dd9', - 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@d1f52c8993a501bd52d4fbd044bfeb9ecdceb9f4', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@c29f9df0edc12f925253a95526823c15ed249d6a', + 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@959ff4a0dba11111c5c999f1235c798431475322', 'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3', 'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@f88a2d766840fc825af1fc065977953ba1fa4a91', - 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@47eef7be333fe709795fd695ba4f8e58c70a0cf0', + 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@6e9d60d80905f561858c2b1234bbb82bb529c044', 'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@74d8a6cb930c68ef617b202c3ff3c59d919e086b', 'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@67c09a1a2ebcf0c7179700ebaf2bd3f58f2d4248', 'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@59f963ce1b1d16cc92137a241a0fe98d637d21f4', 'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@56773a2b578bb5831a1d8eb1927cd9ea92dddc65', - 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@562af80edf57126f333036a707da8cb2f9c1522e', + 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@4fc964ebd2fcad5d65372512bd483767f08dc345', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'cb0597213b0fcb999caa9ed08c2f88dc45eb7d50',
diff --git a/android_webview/browser/aw_settings.cc b/android_webview/browser/aw_settings.cc index efa92a8..6d47627b 100644 --- a/android_webview/browser/aw_settings.cc +++ b/android_webview/browser/aw_settings.cc
@@ -212,6 +212,7 @@ UpdateBackForwardCacheEnabledLocked(env, obj); UpdateBackForwardCacheSettingsTimeoutLocked(env, obj); UpdateBackForwardCacheSettingsMaxPagesInCacheLocked(env, obj); + UpdateBackForwardCacheSettingsKeepForwardEntriesLocked(env, obj); UpdateGeolocationEnabledLocked(env, obj); } @@ -544,6 +545,22 @@ back_forward_cache_max_pages_in_cache_ = max_pages_in_cache; } +void AwSettings::UpdateBackForwardCacheSettingsKeepForwardEntriesLocked( + JNIEnv* env, + const JavaRef<jobject>& obj) { + bool keep_forward_entries = + Java_AwSettings_getBackForwardCacheSettingsKeepForwardEntries(env, obj); + if (web_contents()) { + if (keep_forward_entries != back_forward_cache_keep_forward_entries_) { + web_contents() + ->GetController() + .GetBackForwardCache() + .SetEmbedderSuppliedCacheForwardEntriesAllowed(keep_forward_entries); + } + } + back_forward_cache_keep_forward_entries_ = keep_forward_entries; +} + void AwSettings::UpdateGeolocationEnabledLocked(JNIEnv* env, const JavaRef<jobject>& obj) { if (!web_contents()) {
diff --git a/android_webview/browser/aw_settings.h b/android_webview/browser/aw_settings.h index 5635ab7..41dccc3 100644 --- a/android_webview/browser/aw_settings.h +++ b/android_webview/browser/aw_settings.h
@@ -135,6 +135,9 @@ void UpdateBackForwardCacheSettingsMaxPagesInCacheLocked( JNIEnv* env, const base::android::JavaRef<jobject>& obj); + void UpdateBackForwardCacheSettingsKeepForwardEntriesLocked( + JNIEnv* env, + const base::android::JavaRef<jobject>& obj); void UpdateGeolocationEnabledLocked( JNIEnv* env, const base::android::JavaRef<jobject>& obj); @@ -188,6 +191,7 @@ bool bfcache_enabled_in_java_settings_{false}; int back_forward_cache_timeout_in_seconds_{0}; int back_forward_cache_max_pages_in_cache_{0}; + bool back_forward_cache_keep_forward_entries_{true}; bool geolocation_enabled_{false}; // Whether the settings that would affect the initial page scale is set to a
diff --git a/android_webview/common/aw_feature_map.cc b/android_webview/common/aw_feature_map.cc index 34df941..6cfa9e49 100644 --- a/android_webview/common/aw_feature_map.cc +++ b/android_webview/common/aw_feature_map.cc
@@ -44,6 +44,7 @@ &features::kWebViewCacheBoundaryInterfaceMethods, &features::kWebViewCacheSizeLimitDerivedFromAppCacheQuota, &features::kWebViewConnectToComponentProviderInBackground, + &features::kWebViewContentRestrictionSupport, &features::kWebViewDeferStartupGmsCalls, &features::kWebViewEarlyStartupTracing, &features::kWebViewEarlyTracingInit,
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java b/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java index 6218d55..72d5a038 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java
@@ -3,15 +3,23 @@ // found in the LICENSE file. package org.chromium.android_webview; +import android.net.Uri; +import android.os.ParcelFileDescriptor; + import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; import org.chromium.android_webview.common.AwFeatureMap; import org.chromium.android_webview.common.AwFeatures; import org.chromium.base.AconfigFlaggedApiDelegate; +import org.chromium.base.ContextUtils; +import org.chromium.base.JniOnceCallback; import org.chromium.base.Log; +import org.chromium.base.Promise; import org.chromium.base.ServiceLoaderUtil; import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; /** * JNI bridge for tapping into the ContentRestrictionManager system service in order to enforce @@ -22,6 +30,13 @@ public class AwContentRestrictionManagerBridge { private static final String TAG = "AwCRMBridge"; + private static @Nullable Uri parseUrl(@Nullable String url) { + if (url == null) { + return null; + } + return Uri.parse(url); + } + @CalledByNative public static boolean isContentRestrictionEnabled() { if (!AwFeatureMap.isEnabled(AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT)) { @@ -38,4 +53,42 @@ } return delegate.isContentRestrictionEnabled(); } + + @CalledByNative + public static void requestContentClassification( + @JniType("std::string") String url, + @JniType("std::string") String mimeType, + JniOnceCallback<Boolean> callback) { + Uri uri = parseUrl(url); + if (uri == null) { + callback.onResult(false); + return; + } + AconfigFlaggedApiDelegate delegate = + ServiceLoaderUtil.maybeCreate(AconfigFlaggedApiDelegate.class); + if (delegate == null) { + Log.w(TAG, "Unable to retrieve the AconfigFlaggedApiDelegate instance."); + callback.onResult(false); + return; + } + + // TODO(crbug.com/481113476): Also process the request body. + ParcelFileDescriptor requestBody = null; + Promise<Boolean> promise = + delegate.requestContentRestrictionClassification( + uri, + requestBody, + mimeType, + ContextUtils.getApplicationContext().getMainExecutor()); + promise.then( + (Boolean isAllowed) -> { + callback.onResult(isAllowed); + }, + (@Nullable Exception error) -> { + if (error != null) { + Log.w(TAG, "Failed to classify content", error); + } + callback.onResult(false); + }); + } }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java index 8c715428..0b6a10a 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java +++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -216,6 +216,7 @@ private long mBackForwardCacheTimeoutInSeconds; private int mBackForwardCacheMaxPagesInCache; + private boolean mBackForwardCacheKeepForwardEntries; private boolean mCssHexAlphaColorEnabled; private boolean mScrollTopLeftInteropEnabled; @@ -373,6 +374,12 @@ AwSettings.this::updateBackForwardCacheSettingsMaxPagesInCacheOnUiThreadLocked); } + void updateBackForwardCacheSettingsKeepForwardEntries() { + runOnUiThreadBlockingAndLocked( + AwSettings.this + ::updateBackForwardCacheSettingsKeepForwardEntriesOnUiThreadLocked); + } + void updateGeolocationEnabled() { runOnUiThreadBlockingAndLocked( AwSettings.this::updateGeolocationEnabledOnUiThreadLocked); @@ -1897,6 +1904,25 @@ } } + /** + * Sets whether to keep forward entries when the user navigates back. Disabling this saves + * memory by discarding unreachable pages. + */ + public void setBackForwardCacheKeepForwardEntries(boolean keepForwardEntries) { + if (TRACE) { + Log.i(TAG, "setBackForwardCacheKeepForwardEntries=" + keepForwardEntries); + } + // Setting BackForwardCacheSettings implicitly enables BFCache as well. + setBackForwardCacheEnabled(true); + synchronized (mAwSettingsLock) { + if (mBackForwardCacheKeepForwardEntries == keepForwardEntries) { + return; + } + mBackForwardCacheKeepForwardEntries = keepForwardEntries; + mEventHandler.updateBackForwardCacheSettingsKeepForwardEntries(); + } + } + @CalledByNative public long getBackForwardCacheSettingsTimeout() { synchronized (mAwSettingsLock) { @@ -1913,6 +1939,14 @@ } } + @CalledByNative + public boolean getBackForwardCacheSettingsKeepForwardEntries() { + synchronized (mAwSettingsLock) { + assert Thread.holdsLock(mAwSettingsLock); + return mBackForwardCacheKeepForwardEntries; + } + } + @ForceDarkMode public int getForceDarkMode() { synchronized (mAwSettingsLock) { @@ -2238,6 +2272,15 @@ mNativeAwSettings, AwSettings.this); } + private void updateBackForwardCacheSettingsKeepForwardEntriesOnUiThreadLocked() { + assert mEventHandler.mHandler != null; + ThreadUtils.assertOnUiThread(); + if (mNativeAwSettings == 0) return; + AwSettingsJni.get() + .updateBackForwardCacheSettingsKeepForwardEntriesLocked( + mNativeAwSettings, AwSettings.this); + } + private void updateGeolocationEnabledOnUiThreadLocked() { assert mEventHandler.mHandler != null; ThreadUtils.assertOnUiThread(); @@ -2401,6 +2444,9 @@ void updateBackForwardCacheSettingsMaxPagesInCacheLocked( long nativeAwSettings, AwSettings caller); + void updateBackForwardCacheSettingsKeepForwardEntriesLocked( + long nativeAwSettings, AwSettings caller); + boolean isForceDarkApplied(long nativeAwSettings, AwSettings caller); boolean prefersDarkFromTheme(long nativeAwSettings, AwSettings caller);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java index 86194a3..5c0fd7a 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java
@@ -135,6 +135,31 @@ true, pageFullyLoadedFuture.get(SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); } + private void navigateForwardToUrl(String url) throws Throwable { + // Create a new future to avoid the future set in the initial load. + SettableFuture<Boolean> pageFullyLoadedFuture = SettableFuture.create(); + mLoadedNotifier.setFuture(pageFullyLoadedFuture); + // Traditionally we use onPageFinishedHelper which is no longer + // valid with BFCache working. + // The onPageFinishedHelper is called in `DidFinishLoad` callback + // in the web contents observer. If the page is restored from the + // BFCache, this function will not get called since the onload event + // is already fired when the page was navigated into for the first time. + // We use onPageStartedHelper instead. This function correspond to + // `didFinishNavigationInPrimaryMainFrame`. + OnPageStartedHelper startHelper = mContentsClient.getOnPageStartedHelper(); + int originalCallCount = startHelper.getCallCount(); + HistoryUtils.goForwardSync( + InstrumentationRegistry.getInstrumentation(), + mAwContents.getWebContents(), + startHelper); + Assert.assertEquals(startHelper.getUrl(), url); + Assert.assertEquals(startHelper.getCallCount(), originalCallCount + 1); + // Wait for the page to be fully loaded + Assert.assertEquals( + true, pageFullyLoadedFuture.get(SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } + private void navigateForwardAndBack() throws Throwable { navigateForward(); navigateBack(); @@ -272,6 +297,56 @@ @Test @LargeTest @Feature({"AndroidWebView"}) + public void testBFCacheWithKeepForwardEntries() throws Exception, Throwable { + mAwContents.getSettings().setBackForwardCacheEnabled(true); + + mAwContents.getSettings().setBackForwardCacheMaxPagesInCache(3); + mAwContents.getSettings().setBackForwardCacheTimeoutInSeconds(600); + mAwContents.getSettings().setBackForwardCacheKeepForwardEntries(true); + + // Navigate to the initial page. + mActivityTestRule.loadUrlSync( + mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl); + + // Navigate forward and back. The page should be restored from BFCache. + navigateForwardAndBack(); + Assert.assertEquals("\"null\"", getNotRestoredReasons()); + Assert.assertTrue(isPageShowPersisted()); + + // Navigate forward again to mForwardUrl. The page should be restored from BFCache. + navigateForwardToUrl(mForwardUrl); + Assert.assertEquals("\"null\"", getNotRestoredReasons()); + Assert.assertTrue(isPageShowPersisted()); + } + + @Test + @LargeTest + @Feature({"AndroidWebView"}) + public void testBFCacheWithKeepForwardEntriesDisabled() throws Exception, Throwable { + mAwContents.getSettings().setBackForwardCacheEnabled(true); + + mAwContents.getSettings().setBackForwardCacheMaxPagesInCache(3); + mAwContents.getSettings().setBackForwardCacheTimeoutInSeconds(600); + mAwContents.getSettings().setBackForwardCacheKeepForwardEntries(false); + + // Navigate to the initial page. + mActivityTestRule.loadUrlSync( + mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl); + + // Navigate forward and back. The page should be restored from BFCache. + navigateForwardAndBack(); + Assert.assertEquals("\"null\"", getNotRestoredReasons()); + Assert.assertTrue(isPageShowPersisted()); + + // Navigate forward again to mForwardUrl. The page should NOT be restored from BFCache. + navigateForwardToUrl(mForwardUrl); + Assert.assertNotEquals("\"null\"", getNotRestoredReasons()); + Assert.assertFalse(isPageShowPersisted()); + } + + @Test + @LargeTest + @Feature({"AndroidWebView"}) @CommandLineFlags.Add({"disable-features=WebViewBackForwardCache"}) public void testBackNavigationFollowsSettings() throws Exception, Throwable { mAwContents.getSettings().setBackForwardCacheEnabled(true);
diff --git a/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java b/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java index 6df596be..0b2df2f081 100644 --- a/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java +++ b/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java
@@ -20,10 +20,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowLooper; import org.chromium.android_webview.AwContentRestrictionManagerBridge; import org.chromium.android_webview.ManifestMetadataUtil; @@ -31,6 +33,8 @@ import org.chromium.android_webview.test.util.ManifestMetadataMockApplicationContext; import org.chromium.base.AconfigFlaggedApiDelegate; import org.chromium.base.ContextUtils; +import org.chromium.base.JniOnceCallback; +import org.chromium.base.Promise; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Features.DisableFeatures; @@ -47,12 +51,26 @@ "android.webkit.WebView.EnableContentRestriction"; private static final String METADATA_HOLDER_SERVICE_NAME = "android.webkit.MetaDataHolderService"; + private static final String TEST_URL = "https://example.com"; + private static final String TEST_MIME_TYPE = "text/html"; private ManifestMetadataMockApplicationContext mContext; private ComponentName mMetadataServiceName; + private Boolean mCallbackResult; + private final JniOnceCallback<Boolean> mMockCallback = + new JniOnceCallback<Boolean>() { + @Override + public void onResult(Boolean result) { + mCallbackResult = result; + } + + @Override + public void destroy() {} + }; @Before public void setUp() { + mCallbackResult = null; mContext = new ManifestMetadataMockApplicationContext(RuntimeEnvironment.application); mMetadataServiceName = new ComponentName(mContext, METADATA_HOLDER_SERVICE_NAME); ContextUtils.initApplicationContextForTests(mContext); @@ -99,4 +117,60 @@ Assert.assertFalse(AwContentRestrictionManagerBridge.isContentRestrictionEnabled()); verifyNoInteractions(mFlaggedApiDelegate); } + + @Test + @SmallTest + @Feature({"AndroidWebView"}) + @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) + public void testRequestContentClassification_invalidUrl() { + AwContentRestrictionManagerBridge.requestContentClassification( + /* url= */ null, TEST_MIME_TYPE, mMockCallback); + Assert.assertEquals(false, mCallbackResult); + verifyNoInteractions(mFlaggedApiDelegate); + } + + @Test + @SmallTest + @Feature({"AndroidWebView"}) + @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) + public void testRequestContentClassification_delegateMissing() { + AconfigFlaggedApiDelegate.setInstanceForTesting(null); + AwContentRestrictionManagerBridge.requestContentClassification( + TEST_URL, TEST_MIME_TYPE, mMockCallback); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + Assert.assertEquals(false, mCallbackResult); + verifyNoInteractions(mFlaggedApiDelegate); + } + + @Test + @SmallTest + @Feature({"AndroidWebView"}) + @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) + public void testRequestContentClassification_allowed() { + Promise<Boolean> promise = new Promise<>(); + when(mFlaggedApiDelegate.requestContentRestrictionClassification( + Mockito.any(), Mockito.eq(null), Mockito.eq(TEST_MIME_TYPE), Mockito.any())) + .thenReturn(promise); + AwContentRestrictionManagerBridge.requestContentClassification( + TEST_URL, TEST_MIME_TYPE, mMockCallback); + promise.fulfill(true); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + Assert.assertEquals(true, mCallbackResult); + } + + @Test + @SmallTest + @Feature({"AndroidWebView"}) + @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) + public void testRequestContentClassification_blocked() { + Promise<Boolean> promise = new Promise<>(); + when(mFlaggedApiDelegate.requestContentRestrictionClassification( + Mockito.any(), Mockito.eq(null), Mockito.eq(TEST_MIME_TYPE), Mockito.any())) + .thenReturn(promise); + AwContentRestrictionManagerBridge.requestContentClassification( + TEST_URL, TEST_MIME_TYPE, mMockCallback); + promise.fulfill(false); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + Assert.assertEquals(false, mCallbackResult); + } }
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebSettingsBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebSettingsBoundaryInterface.java index c9c6263c..e9ba12af 100644 --- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebSettingsBoundaryInterface.java +++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebSettingsBoundaryInterface.java
@@ -171,7 +171,12 @@ void setBackForwardCacheSettingsMaxPagesInCache(int pagesInCache); + void setBackForwardCacheSettingsKeepForwardEntries( + boolean keepForwardEntries); + long getBackForwardCacheSettingsTimeout(); int getBackForwardCacheSettingsMaxPagesInCache(); + + boolean getBackForwardCacheSettingsKeepForwardEntries(); }
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java index 35486ed..ad7928c 100644 --- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java +++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
@@ -430,6 +430,10 @@ // V2 was deleted as it didn't get released and we made a major type change in V3. public static final String BACK_FORWARD_CACHE_SETTINGS_V3 = "BACK_FORWARD_CACHE_SETTINGS_V3"; + // BackForwardCacheSettings.setKeepForwardEntries + // BackForwardCacheSettings.getKeepForwardEntries + public static final String BACK_FORWARD_CACHE_SETTINGS_V4 = "BACK_FORWARD_CACHE_SETTINGS_V4"; + // Profile.preconnect public static final String PRECONNECT = "PRECONNECT";
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsAdapter.java index 4a3946d..84b6d79 100644 --- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsAdapter.java +++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsAdapter.java
@@ -596,6 +596,16 @@ } @Override + public void setBackForwardCacheSettingsKeepForwardEntries(boolean keepForwardEntries) { + try (TraceEvent ignored = + TraceEvent.scoped( + "WebView.APICall.AndroidX.BACK_FORWARD_CACHE_SETTINGS_SET_KEEP_FORWARD_ENTRIES")) { + recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_SET_KEEP_FORWARD_ENTRIES); + mAwSettings.setBackForwardCacheKeepForwardEntries(keepForwardEntries); + } + } + + @Override public long getBackForwardCacheSettingsTimeout() { try (TraceEvent ignored = TraceEvent.scoped( @@ -614,4 +624,14 @@ return mAwSettings.getBackForwardCacheSettingsMaxPagesInCache(); } } + + @Override + public boolean getBackForwardCacheSettingsKeepForwardEntries() { + try (TraceEvent ignored = + TraceEvent.scoped( + "WebView.APICall.AndroidX.BACK_FORWARD_CACHE_SETTINGS_GET_KEEP_FORWARD_ENTRIES")) { + recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_GET_KEEP_FORWARD_ENTRIES); + return mAwSettings.getBackForwardCacheSettingsKeepForwardEntries(); + } + } }
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsNoOpAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsNoOpAdapter.java index 40a1e3b..8a75068 100644 --- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsNoOpAdapter.java +++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsNoOpAdapter.java
@@ -250,12 +250,26 @@ } @Override + public void setBackForwardCacheSettingsKeepForwardEntries( + boolean keepForwardEntries) { + recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_SET_KEEP_FORWARD_ENTRIES); + } + + @Override public long getBackForwardCacheSettingsTimeout() { + recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_GET_TIMEOUT_IN_SECONDS); return 0; } @Override public int getBackForwardCacheSettingsMaxPagesInCache() { + recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_GET_MAX_PAGES_IN_CACHE); return 0; } + + @Override + public boolean getBackForwardCacheSettingsKeepForwardEntries() { + recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_GET_KEEP_FORWARD_ENTRIES); + return true; + } }
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java index 7b15baef..e7347bb 100644 --- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java +++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
@@ -140,6 +140,7 @@ Features.PAGE_GET_URL, Features.JS_INJECTION_IN_FRAME_AND_WORLD, Features.NAVIGATION_GET_WEB_RESOURCE_ERROR, + Features.BACK_FORWARD_CACHE_SETTINGS_V4 + Features.DEV_SUFFIX, // Add new features above. New features must include `+ Features.DEV_SUFFIX` // when they're initially added (this can be removed in a future CL). The final // feature should have a trailing comma for cleaner diffs. @@ -330,6 +331,8 @@ ApiCall.SET_MAX_PRERENDERS, ApiCall.SET_MAX_PREFETCHES, ApiCall.SET_PREFETCH_TTL_SECONDS, + ApiCall.BACK_FORWARD_CACHE_SETTINGS_GET_KEEP_FORWARD_ENTRIES, + ApiCall.BACK_FORWARD_CACHE_SETTINGS_SET_KEEP_FORWARD_ENTRIES, // Add new constants above. The final constant should have a trailing comma for cleaner // diffs. ApiCall.COUNT, // Added to suppress WrongConstant in #recordApiCall @@ -520,8 +523,10 @@ int SET_MAX_PRERENDERS = 179; int SET_MAX_PREFETCHES = 180; int SET_PREFETCH_TTL_SECONDS = 181; + int BACK_FORWARD_CACHE_SETTINGS_GET_KEEP_FORWARD_ENTRIES = 182; + int BACK_FORWARD_CACHE_SETTINGS_SET_KEEP_FORWARD_ENTRIES = 183; // Remember to update AndroidXWebkitApiCall in enums.xml when adding new values here - int COUNT = 182; + int COUNT = 184; } // LINT.ThenChange(/tools/metrics/histograms/metadata/android/enums.xml:AndroidXWebkitApiCall)
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h index 130161cd..ffb3dad3 100644 --- a/ash/constants/ash_pref_names.h +++ b/ash/constants/ash_pref_names.h
@@ -3099,6 +3099,75 @@ inline constexpr char kUserPrintersAllowed[] = "native_printing.user_native_printers_allowed"; +//----------------------------------------------------------------------------- +// Child account related Prefs +//----------------------------------------------------------------------------- + +// Amount of screen time that a child user has used in the current day. +inline constexpr char kChildScreenTimeMilliseconds[] = "child_screen_time"; + +// TimeDelta pref to record the accumulated Chrome browser app usage for family +// user metrics. +inline constexpr char kFamilyUserMetricsChromeBrowserEngagementDuration[] = + "family_user.metrics.chrome_browser_engagement_duration"; + +// Integer pref to record the day id (number of days since origin of time) when +// family user metrics were last recorded. +inline constexpr char kFamilyUserMetricsDayId[] = "family_user.metrics.day_id"; + +// TimeDelta pref to record the accumulated user session duration for family +// user metrics. +inline constexpr char kFamilyUserMetricsSessionEngagementDuration[] = + "family_user.metrics.session_engagement_duration"; + +// Dictionary pref containing the configuration used to verify Parent Access +// Code. The data is sent through the ParentAccessCodeConfig policy, which is +// set for child users only, and kept on the known user storage. +inline constexpr char kKnownUserParentAccessCodeConfig[] = + "child_user.parent_access_code.config"; + +// Dictionary pref containing configuration used to verify Parent Access Code. +// Controlled by ParentAccessCodeConfig policy. +inline constexpr char kParentAccessCodeConfig[] = + "child_user.parent_access_code.config"; + +// Dictionary pref containing the allowed urls, schemes and applications +// that would not be blocked by per app time limits. +inline constexpr char kPerAppTimeLimitsAllowlistPolicy[] = + "child_user.per_app_time_limits.allowlist"; + +// List pref containing app activity and state for each application. +inline constexpr char kPerAppTimeLimitsAppActivities[] = + "child_user.per_app_time_limits.app_activities"; + +// Int64 to specify the last timestamp the AppActivityRegistry was reset. +inline constexpr char kPerAppTimeLimitsLastResetTime[] = + "child_user.per_app_time_limits.last_reset_time"; + +// Int64 to specify the last timestamp the app activity has been successfully +// reported. +inline constexpr char kPerAppTimeLimitsLastSuccessfulReportTime[] = + "child_user.per_app_time_limits.last_successful_report_time"; + +// Int64 to specify the latest AppLimit update timestamp from. +inline constexpr char kPerAppTimeLimitsLatestLimitUpdateTime[] = + "child_user.per_app_time_limits.latest_limit_update_time"; + +// Dictionary pref containing the per-app time limits configuration for +// child user. Controlled by PerAppTimeLimits policy. +inline constexpr char kPerAppTimeLimitsPolicy[] = + "child_user.per_app_time_limits.policy"; + +// Last state of the screen time limit. +inline constexpr char kScreenTimeLastState[] = "screen_time.last_state"; + +// A dictionary containing the latest Time Limits override authorized by parent +// access code. +inline constexpr char kTimeLimitLocalOverride[] = "screen_time.local_override"; + +// A dictionary preference holding the usage time limit definitions for a user. +inline constexpr char kUsageTimeLimit[] = "screen_time.limit"; + // NOTE: New prefs should start with the "ash." prefix. Existing prefs moved // into this file should not be renamed, since they may be synced.
diff --git a/base/BUILD.gn b/base/BUILD.gn index c9811ba..7ab4379d 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -1140,6 +1140,7 @@ "//base/numerics:base_numerics", "//base/third_party/icu", "//build:chromecast_buildflags", + "//build:robolectric_buildflags", "//third_party/abseil-cpp:absl", ] @@ -1434,7 +1435,6 @@ ":library_loader_jni($default_toolchain)", ":metrics_jni($default_toolchain)", ":tasks_minimal_jni($default_toolchain)", - "//build:robolectric_buildflags", ] public_deps += [ "//third_party/jni_zero" ] } # is_android || is_robolectric
diff --git a/base/android/java/src/org/chromium/base/AconfigFlaggedApiDelegate.java b/base/android/java/src/org/chromium/base/AconfigFlaggedApiDelegate.java index 6e4019f..a9ef0f0 100644 --- a/base/android/java/src/org/chromium/base/AconfigFlaggedApiDelegate.java +++ b/base/android/java/src/org/chromium/base/AconfigFlaggedApiDelegate.java
@@ -12,8 +12,10 @@ import android.graphics.Rect; import android.graphics.RectF; import android.hardware.display.DisplayManager; +import android.net.Uri; import android.os.Bundle; import android.os.OutcomeReceiver; +import android.os.ParcelFileDescriptor; import android.util.Pair; import android.util.SparseArray; import android.view.Display; @@ -463,4 +465,26 @@ default boolean isContentRestrictionEnabled() { return false; } + + /** + * Calls the platform to determine if the content should be allowed or blocked. + * + * @param uri The URI of the content to be classified. + * @param requestBody The request body of the content to be classified. Can be null for requests + * that have no body (for ex. GET requests). + * @param mimeType The MIME type of the content to be classified. + * @param executor The executor to run the callback on. + * @return A promise fulfilled with the boolean classification result (true if allowed), + * rejected otherwise with {@link UnsupportedOperationException} if not supported or with + * the exception received from the API call. + */ + default Promise<Boolean> requestContentRestrictionClassification( + Uri uri, + @Nullable ParcelFileDescriptor requestBody, + String mimeType, + Executor executor) { + Promise<Boolean> promise = new Promise<>(); + promise.reject(new UnsupportedOperationException("Not supported")); + return promise; + } }
diff --git a/base/uuid.h b/base/uuid.h index 38aa3e5..afa15e8b 100644 --- a/base/uuid.h +++ b/base/uuid.h
@@ -15,8 +15,14 @@ #include "base/base_export.h" #include "base/compiler_specific.h" #include "base/containers/span.h" +#include "build/robolectric_buildflags.h" #include "third_party/abseil-cpp/absl/numeric/int128.h" +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC) +#include "base/android/jni_string.h" +#include "third_party/jni_zero/jni_zero.h" +#endif + namespace base { class BASE_EXPORT Uuid { @@ -99,4 +105,26 @@ } // namespace base +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC) +namespace jni_zero { + +// TODO(agrieve): We map base::Uuid to String. Might be nicer to map it to +// android.util.UUID instead. +template <> +inline base::Uuid FromJniType<base::Uuid>( + JNIEnv* env, + const jni_zero::JavaRef<jobject>& obj) { + return base::Uuid::ParseLowercase(FromJniType<std::string>(env, obj)); +} + +template <> +inline ScopedJavaLocalRef<jobject> ToJniType<base::Uuid>( + JNIEnv* env, + const base::Uuid& uuid) { + return ToJniType(env, uuid.AsLowercaseString()); +} + +} // namespace jni_zero +#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC) + #endif // BASE_UUID_H_
diff --git a/cc/mojo_embedder/viz_layer_context.cc b/cc/mojo_embedder/viz_layer_context.cc index 594ea24..8342b05b 100644 --- a/cc/mojo_embedder/viz_layer_context.cc +++ b/cc/mojo_embedder/viz_layer_context.cc
@@ -4,6 +4,7 @@ #include "cc/mojo_embedder/viz_layer_context.h" +#include <cmath> #include <cstdint> #include <memory> #include <string> @@ -432,9 +433,15 @@ auto wire = viz::mojom::TransformTreeUpdate::New(); wire->page_scale_factor = new_tree.page_scale_factor(); + DUMP_WILL_BE_CHECK_GT(wire->page_scale_factor, 0.f); + DUMP_WILL_BE_CHECK(std::isfinite(wire->page_scale_factor)); wire->device_scale_factor = new_tree.device_scale_factor(); + DUMP_WILL_BE_CHECK_GT(wire->device_scale_factor, 0.f); + DUMP_WILL_BE_CHECK(std::isfinite(wire->device_scale_factor)); wire->device_transform_scale_factor = new_tree.device_transform_scale_factor(); + DUMP_WILL_BE_CHECK_GT(wire->device_transform_scale_factor, 0.f); + DUMP_WILL_BE_CHECK(std::isfinite(wire->device_transform_scale_factor)); wire->nodes_affected_by_outer_viewport_bounds_delta = new_tree.nodes_affected_by_outer_viewport_bounds_delta(); wire->nodes_affected_by_safe_area_bottom = @@ -1375,14 +1382,28 @@ tree.primary_main_frame_item_sequence_number(); update->selection = tree.selection(); update->page_scale_factor = tree.page_scale_factor()->Current(true); + DUMP_WILL_BE_CHECK_GT(update->page_scale_factor, 0.f); + DUMP_WILL_BE_CHECK(std::isfinite(update->page_scale_factor)); update->min_page_scale_factor = tree.min_page_scale_factor(); + DUMP_WILL_BE_CHECK_GT(update->min_page_scale_factor, 0.f); + DUMP_WILL_BE_CHECK(std::isfinite(update->min_page_scale_factor)); update->max_page_scale_factor = tree.max_page_scale_factor(); + DUMP_WILL_BE_CHECK_GT(update->max_page_scale_factor, 0.f); + DUMP_WILL_BE_CHECK(std::isfinite(update->max_page_scale_factor)); + DUMP_WILL_BE_CHECK_GE(update->max_page_scale_factor, + update->min_page_scale_factor); update->external_page_scale_factor = tree.external_page_scale_factor(); + DUMP_WILL_BE_CHECK_GT(update->external_page_scale_factor, 0.f); + DUMP_WILL_BE_CHECK(std::isfinite(update->external_page_scale_factor)); update->frame_has_damage = frame_has_damage; update->latency_info = std::move(latency_info); update->device_viewport = tree.GetDeviceViewport(); update->device_scale_factor = tree.device_scale_factor(); + DUMP_WILL_BE_CHECK_GT(update->device_scale_factor, 0.f); + DUMP_WILL_BE_CHECK(std::isfinite(update->device_scale_factor)); update->painted_device_scale_factor = tree.painted_device_scale_factor(); + DUMP_WILL_BE_CHECK_GT(update->painted_device_scale_factor, 0.f); + DUMP_WILL_BE_CHECK(std::isfinite(update->painted_device_scale_factor)); update->display_color_spaces = tree.display_color_spaces(); if (tree.local_surface_id_from_parent().is_valid()) { update->local_surface_id_from_parent = tree.local_surface_id_from_parent();
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index 8164a693..b505475c 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc
@@ -520,7 +520,7 @@ return true; } -bool SchedulerStateMachine::ShouldBeginMainFrameWhenIdle() const { +bool SchedulerStateMachine::ShouldBlockBeginMainFrameWhenIdle() const { return (!settings_.using_synchronous_renderer_compositor && begin_impl_frame_state_ == BeginImplFrameState::IDLE); } @@ -577,7 +577,7 @@ // might have new user input arriving soon. // TODO(brianderson): Allow sending BeginMainFrame while idle when the main // thread isn't consuming user input for non-synchronous compositor. - if (ShouldBeginMainFrameWhenIdle()) { + if (ShouldBlockBeginMainFrameWhenIdle()) { return false; }
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h index 37826fe23..7facf7e 100644 --- a/cc/scheduler/scheduler_state_machine.h +++ b/cc/scheduler/scheduler_state_machine.h
@@ -420,7 +420,7 @@ bool ShouldActivateSyncTree() const; virtual bool ShouldActivateSyncTreeBeforeDraw() const; bool ShouldSendBeginMainFrame() const; - virtual bool ShouldBeginMainFrameWhenIdle() const; + virtual bool ShouldBlockBeginMainFrameWhenIdle() const; bool ShouldCommit() const; bool ShouldRunPostCommit() const; virtual bool ShouldPrepareTiles() const;
diff --git a/cc/scheduler/webview_scheduler_state_machine.cc b/cc/scheduler/webview_scheduler_state_machine.cc index 9d339fb6..b4a12ae 100644 --- a/cc/scheduler/webview_scheduler_state_machine.cc +++ b/cc/scheduler/webview_scheduler_state_machine.cc
@@ -21,7 +21,7 @@ return false; } -bool WebviewSchedulerStateMachine::ShouldBeginMainFrameWhenIdle() const { +bool WebviewSchedulerStateMachine::ShouldBlockBeginMainFrameWhenIdle() const { // It's okay to send BeginMainFrame for the synchronous compositor // because the main thread is always high latency in that case. return false;
diff --git a/cc/scheduler/webview_scheduler_state_machine.h b/cc/scheduler/webview_scheduler_state_machine.h index fcb900b..ab3e422f 100644 --- a/cc/scheduler/webview_scheduler_state_machine.h +++ b/cc/scheduler/webview_scheduler_state_machine.h
@@ -17,7 +17,7 @@ ~WebviewSchedulerStateMachine() override; bool ShouldActivateSyncTreeBeforeDraw() const override; - bool ShouldBeginMainFrameWhenIdle() const override; + bool ShouldBlockBeginMainFrameWhenIdle() const override; bool ShouldInvalidateLayerTreeFrameSink() const override; BeginImplFrameDeadlineMode CurrentBeginImplFrameDeadlineMode() const override; };
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index 1ff726a..c59d0a2 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -844,6 +844,7 @@ "java/src/org/chromium/chrome/browser/ntp/IncognitoNtpUtils.java", "java/src/org/chromium/chrome/browser/ntp/NativePageRootFrameLayout.java", "java/src/org/chromium/chrome/browser/ntp/NewTabPage.java", + "java/src/org/chromium/chrome/browser/ntp/NewTabPageCoordinator.java", "java/src/org/chromium/chrome/browser/ntp/NewTabPageCreationState.java", "java/src/org/chromium/chrome/browser/ntp/NewTabPageCreationTracker.java", "java/src/org/chromium/chrome/browser/ntp/NewTabPageLaunchOrigin.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java index a021127a..2f22469 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -201,8 +201,8 @@ private static final float GLIC_BUTTON_BACKGROUND_Y_OFFSET_DP = 5.f; private static final float GLIC_BUTTON_BACKGROUND_WIDTH_DP = 28.f; private static final float GLIC_BUTTON_BACKGROUND_HEIGHT_DP = 28.f; - private static final float GLIC_BUTTON_HOVER_BACKGROUND_PRESSED_OPACITY = 0.24f; - private static final float GLIC_BUTTON_HOVER_BACKGROUND_DEFAULT_OPACITY = 0.16f; + private static final float GLIC_BUTTON_HOVER_BACKGROUND_PRESSED_OPACITY = 0.30f; + private static final float GLIC_BUTTON_HOVER_BACKGROUND_DEFAULT_OPACITY = 0.20f; private static final float GLIC_BUTTON_CLICK_SLOP_DP = (BUTTON_DESIRED_TOUCH_TARGET_SIZE - GLIC_BUTTON_BACKGROUND_WIDTH_DP) / 2; private static final float GLIC_BUTTON_START_PADDING_DP = 6.f; @@ -777,24 +777,20 @@ mGlicButton.setVisible(false); @ColorInt - int backgroundDefaultColor = - TabUiThemeUtil.getTabStripSelectedTabColor(context, /* isIncognito= */ false); + int backgroundDefaultColor = SemanticColorUtils.getColorSurfaceContainerLow(context); @ColorInt int apsBackgroundHoveredColor = ColorUtils.setAlphaComponentWithFloat( - SemanticColorUtils.getDefaultTextColor(context), + SemanticColorUtils.getColorPrimary(context), GLIC_BUTTON_HOVER_BACKGROUND_DEFAULT_OPACITY); @ColorInt int backgroundPressedColor = ColorUtils.setAlphaComponentWithFloat( - SemanticColorUtils.getDefaultTextColor(context), + SemanticColorUtils.getColorPrimary(context), GLIC_BUTTON_HOVER_BACKGROUND_PRESSED_OPACITY); - @ColorInt - int iconDefaultColor = - AppCompatResources.getColorStateList(context, R.color.default_icon_color_tint_list) - .getDefaultColor(); + @ColorInt int iconDefaultColor = SemanticColorUtils.getDefaultIconColor(context); mGlicButton.setTint( iconDefaultColor, iconDefaultColor, Color.TRANSPARENT, Color.TRANSPARENT);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java index 570e16c3..3e28c4a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java
@@ -218,10 +218,7 @@ assumeNonNull(mDelegate); switch (action) { case CaptureAction.CAPTURE_CANCELLED: - mDelegate.onCancel(); - MediaCapturePickerManager.recordResult( - MediaCapturePickerManager.Result.CANCELLED); - break; + return; case CaptureAction.CAPTURE_WINDOW: mDelegate.onPickWindow(); MediaCapturePickerManager.recordResult(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java index d1555078..f3cdee8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -174,6 +174,7 @@ private FeedSurfaceProvider mFeedSurfaceProvider; private NewTabPageLayout mNewTabPageLayout; + private NewTabPageCoordinator mNewTabPageCoordinator; private @Nullable TabObserver mTabObserver; private @Nullable LifecycleObserver mLifecycleObserver; protected boolean mSearchProviderHasLogo; @@ -292,7 +293,7 @@ @Override public boolean isLocationBarShownInNtp() { if (mIsDestroyed) return false; - return isInSingleUrlBarMode() && !mNewTabPageLayout.urlFocusAnimationsDisabled(); + return isInSingleUrlBarMode() && !mNewTabPageCoordinator.urlFocusAnimationsDisabled(); } @Override @@ -385,7 +386,7 @@ if (mIsDestroyed) return; super.onLoadingComplete(tiles); - mNewTabPageLayout.onTilesLoaded(); + mNewTabPageCoordinator.onTilesLoaded(); } @Override @@ -512,7 +513,7 @@ recordNtpShown(); } maybeUpdateMagicStack(); - mNewTabPageLayout.onSwitchToForeground(); + mNewTabPageCoordinator.onSwitchToForeground(); } @Override @@ -602,9 +603,7 @@ NewTabPageUma.recordContentSuggestionsDisplayStatus(profile); - mNewTabPageLayout.initialize( - mNewTabPageManager, - activity, + mNewTabPageCoordinator.initialize( mTileGroupDelegate, mSearchProviderHasLogo, mIsDefaultSearchEngineGoogle, @@ -643,7 +642,7 @@ * @param edgeToEdgeControllerSupplier The supplier to {@link EdgeToEdgeController}. * @param startupMetricsTracker Used to record NTP startup metric. */ - @EnsuresNonNull({"mNewTabPageLayout", "mFeedSurfaceProvider"}) + @EnsuresNonNull({"mNewTabPageCoordinator", "mNewTabPageLayout", "mFeedSurfaceProvider"}) protected void initializeMainView( Activity activity, WindowAndroid windowAndroid, @@ -661,6 +660,8 @@ // TODO(crbug.com/347509698): Remove the log statements after fixing the bug. Log.i(TAG, "NewTabPageLayout inflate"); mNewTabPageLayout = (NewTabPageLayout) inflater.inflate(R.layout.new_tab_page_layout, null); + mNewTabPageCoordinator = + new NewTabPageCoordinator(mNewTabPageManager, activity, mNewTabPageLayout); FeedSurfaceCoordinator.ActionDelegateFactory createActionDelegate = () -> @@ -689,7 +690,7 @@ activity, snackbarManager, windowAndroid, - new SnapScrollHelperImpl(mNewTabPageManager, mNewTabPageLayout), + new SnapScrollHelperImpl(mNewTabPageManager, mNewTabPageCoordinator), mNewTabPageLayout, mBrowserControlsStateProvider.getTopControlsHeight(), isInNightMode, @@ -789,7 +790,7 @@ if (!mIsTablet) { mUseLightIconTint = applyWhiteBackgroundOnSearchBox; } - mNewTabPageLayout.onCustomizedBackgroundChanged(applyWhiteBackgroundOnSearchBox); + mNewTabPageCoordinator.onCustomizedBackgroundChanged(applyWhiteBackgroundOnSearchBox); } /** Initializes whether to use a light tint color on icons of toolbar and status bar. */ @@ -821,7 +822,7 @@ // However, consumeTopInset being false takes priority — if the parent is not consuming top // insets (e.g. status indicator is visible), the NTP must not apply its own top inset // either, since the content view already has the system top padding. - mNewTabPageLayout.onToEdgeChange( + mNewTabPageCoordinator.onToEdgeChange( systemTopInset, consumeTopInset && supportsEdgeToEdgeOnTop()); } @@ -880,16 +881,16 @@ - mTabStripHeightSupplier.get(); } - /** Returns the instance of {@link NewTabPageLayout}. */ + /** Returns the instance of {@link NewTabPageCoordinator}. */ @VisibleForTesting - public NewTabPageLayout getNewTabPageLayout() { - return mNewTabPageLayout; + public NewTabPageCoordinator getNewTabPageCoordinator() { + return mNewTabPageCoordinator; } /** Returns the view container for the new tab layout. */ @VisibleForTesting public View getLayout() { - return mNewTabPageLayout; + return mNewTabPageCoordinator.getNewTabPageLayout(); } /** @@ -898,7 +899,7 @@ * @param disable Whether to disable the animations. */ public void setUrlFocusAnimationsDisabled(boolean disable) { - mNewTabPageLayout.setUrlFocusAnimationsDisabled(disable); + mNewTabPageCoordinator.setUrlFocusAnimationsDisabled(disable); } private boolean isInSingleUrlBarMode() { @@ -914,7 +915,7 @@ private void onSearchEngineUpdated() { updateSearchProvider(); - mNewTabPageLayout.setSearchProviderInfo( + mNewTabPageCoordinator.setSearchProviderInfo( mSearchProviderHasLogo, mIsDefaultSearchEngineGoogle); // TODO(crbug.com/40226731): Remove this call when the Feed position experiment is // cleaned up. @@ -929,7 +930,7 @@ * @param percent The percentage of the URL bar focus animation. */ public void setUrlFocusChangeAnimationPercent(float percent) { - mNewTabPageLayout.setUrlFocusChangeAnimationPercent(percent); + mNewTabPageCoordinator.setUrlFocusChangeAnimationPercent(percent); } /** @@ -940,7 +941,7 @@ * to the NewTabPage view. */ public void getSearchBoxBounds(Rect bounds, Point translation) { - mNewTabPageLayout.getSearchBoxBounds(bounds, translation, getView()); + mNewTabPageCoordinator.getSearchBoxBounds(bounds, translation, getView()); } /** @@ -949,7 +950,7 @@ * @return The vertical inset in pixels. */ public int getSearchBoxBoundsVerticalInset() { - return mNewTabPageLayout.getSearchBoxBoundsVerticalInset(); + return mNewTabPageCoordinator.getSearchBoxBoundsVerticalInset(); } /** @@ -958,7 +959,7 @@ * @param alpha opacity (alpha) value to use. */ public void setSearchBoxAlpha(float alpha) { - mNewTabPageLayout.setSearchBoxAlpha(alpha); + mNewTabPageCoordinator.setSearchBoxAlpha(alpha); } /** @@ -967,7 +968,7 @@ * @param alpha opacity (alpha) value to use. */ public void setSearchProviderLogoAlpha(float alpha) { - mNewTabPageLayout.setSearchProviderLogoAlpha(alpha); + mNewTabPageCoordinator.setSearchProviderLogoAlpha(alpha); } /** @@ -981,14 +982,14 @@ * @see org.chromium.chrome.browser.omnibox.NewTabPageDelegate#hasCompletedFirstLayout(). */ public boolean hasCompletedFirstLayout() { - return mNewTabPageLayout.getHeight() > 0; + return mNewTabPageCoordinator.getNewTabPageLayout().getHeight() > 0; } /** * @return Whether the location bar has been scrolled to top in the NTP. */ public boolean isLocationBarScrolledToTopInNtp() { - return mNewTabPageLayout.getToolbarTransitionPercentage() == 1; + return mNewTabPageCoordinator.getToolbarTransitionPercentage() == 1; } /** @@ -997,7 +998,7 @@ * @param listener The listener to be notified on changes. */ public void setSearchBoxScrollListener(@Nullable OnSearchBoxScrollListener listener) { - mNewTabPageLayout.setSearchBoxScrollListener(listener); + mNewTabPageCoordinator.setSearchBoxScrollListener(listener); } /** Sets the OmniboxStub that this page interacts with. */ @@ -1007,7 +1008,7 @@ // The toolbar can't get the reference to the native page until its initialization is // finished, so we can't cache it here and transfer it to the view later. We pull that // state from the location bar when we get a reference to it as a workaround. - mNewTabPageLayout.setUrlFocusChangeAnimationPercent( + mNewTabPageCoordinator.setUrlFocusChangeAnimationPercent( omniboxStub.isUrlBarFocused() ? 1f : 0f); FeedReliabilityLogger feedReliabilityLogger = @@ -1020,7 +1021,7 @@ mVoiceRecognitionHandler = mOmniboxStub.getVoiceRecognitionHandler(); if (mVoiceRecognitionHandler != null) { mVoiceRecognitionHandler.addObserver(this); - mNewTabPageLayout.updateActionButtonVisibility(); + mNewTabPageCoordinator.updateActionButtonVisibility(); } } @@ -1042,7 +1043,7 @@ @Override public void onVoiceAvailabilityImpacted() { - mNewTabPageLayout.updateActionButtonVisibility(); + mNewTabPageCoordinator.updateActionButtonVisibility(); } /** Adds an observer to be notified on most visited tile clicks. */ @@ -1247,26 +1248,26 @@ @Override public void reload() { mFeedSurfaceProvider.reload(); - mNewTabPageLayout.reload(); + mNewTabPageCoordinator.reload(); } @Override public int getTopInset() { - return mNewTabPageLayout.getTopInset(); + return mNewTabPageCoordinator.getTopInset(); } // InvalidationAwareThumbnailProvider @Override public boolean shouldCaptureThumbnail() { - return mNewTabPageLayout.shouldCaptureThumbnail() + return mNewTabPageCoordinator.shouldCaptureThumbnail() || mFeedSurfaceProvider.shouldCaptureThumbnail() || mSnapshotSingleTabCardChanged; } @Override public void captureThumbnail(Canvas canvas) { - mNewTabPageLayout.onPreCaptureThumbnail(); + mNewTabPageCoordinator.onPreCaptureThumbnail(); mFeedSurfaceProvider.captureThumbnail(canvas); mSnapshotSingleTabCardChanged = false; } @@ -1331,7 +1332,7 @@ mSearchResumptionModuleCoordinator = SearchResumptionModuleUtils.mayCreateSearchResumptionModule( - mNewTabPageLayout, + mNewTabPageCoordinator.getNewTabPageLayout(), mTabModelSelector.getCurrentModel(), mTab, profile, @@ -1384,8 +1385,10 @@ mSingleTabCardContainer = (FrameLayout) ((ViewStub) - mNewTabPageLayout.findViewById( - R.id.tab_switcher_module_container_stub)) + mNewTabPageCoordinator + .getNewTabPageLayout() + .findViewById( + R.id.tab_switcher_module_container_stub)) .inflate(); mSingleTabSwitcherCoordinator = new SingleTabSwitcherCoordinator( @@ -1410,10 +1413,11 @@ */ @EnsuresNonNull({"mHomeModulesContainer", "mHomeModulesCoordinator"}) private void initializeMagicStack() { + NewTabPageLayout newTabPageLayout = mNewTabPageCoordinator.getNewTabPageLayout(); mHomeModulesContainer = (ViewGroup) ((ViewStub) - mNewTabPageLayout.findViewById( + newTabPageLayout.findViewById( R.id.home_modules_recycler_view_stub)) .inflate(); MonotonicObservableSupplier<Profile> profileSupplier = @@ -1422,7 +1426,7 @@ new HomeModulesCoordinator( mActivity, this, - mNewTabPageLayout, + newTabPageLayout, HomeModulesConfigManager.getInstance(), profileSupplier, assertNonNull(assumeNonNull(mModuleRegistrySupplier).get())); @@ -1487,7 +1491,7 @@ return mHomeModulesContainer.getVisibility() == View.VISIBLE; } - /** Destroy the single tab card on the {@link NewTabPageLayout}. */ + /** Destroy the single tab card on the {@link NewTabPageCoordinator}. */ @VisibleForTesting void destroySingleTabCard() { if (mSingleTabCardContainer != null) mSingleTabCardContainer.removeAllViews(); @@ -1586,6 +1590,6 @@ } public void enableSearchBoxEditText(boolean enable) { - mNewTabPageLayout.enableSearchBoxEditText(enable); + mNewTabPageCoordinator.enableSearchBoxEditText(enable); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageCoordinator.java new file mode 100644 index 0000000..b6856b24b --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageCoordinator.java
@@ -0,0 +1,1205 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.ntp; + +import static org.chromium.build.NullUtil.assumeNonNull; + +import android.app.Activity; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.Rect; +import android.text.Editable; +import android.view.DragEvent; +import android.view.View; +import android.view.View.OnLayoutChangeListener; +import android.view.ViewGroup; +import android.view.ViewStub; + +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.Callback; +import org.chromium.base.CallbackController; +import org.chromium.base.Log; +import org.chromium.base.TraceEvent; +import org.chromium.build.annotations.Initializer; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.composeplate.ComposeplateCoordinator; +import org.chromium.chrome.browser.composeplate.ComposeplateMetricsUtils; +import org.chromium.chrome.browser.composeplate.ComposeplateUtils; +import org.chromium.chrome.browser.device_lock.DeviceLockActivityLauncherImpl; +import org.chromium.chrome.browser.feed.FeedSurfaceScrollDelegate; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.incognito.IncognitoUtils; +import org.chromium.chrome.browser.lens.LensEntryPoint; +import org.chromium.chrome.browser.lens.LensMetrics; +import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; +import org.chromium.chrome.browser.logo.LogoBridge.Logo; +import org.chromium.chrome.browser.logo.LogoCoordinator; +import org.chromium.chrome.browser.logo.LogoUtils; +import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; +import org.chromium.chrome.browser.ntp.NewTabPage.OnSearchBoxScrollListener; +import org.chromium.chrome.browser.ntp.search.SearchBoxCoordinator; +import org.chromium.chrome.browser.ntp_customization.NtpCustomizationConfigManager; +import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils; +import org.chromium.chrome.browser.omnibox.SearchEngineUtils; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.setup_list.SetupListModuleUtils; +import org.chromium.chrome.browser.signin.SigninAndHistorySyncActivityLauncherImpl; +import org.chromium.chrome.browser.suggestions.tile.MostVisitedTilesCoordinator; +import org.chromium.chrome.browser.suggestions.tile.TileGroup; +import org.chromium.chrome.browser.tab_ui.InvalidationAwareThumbnailProvider; +import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; +import org.chromium.chrome.browser.ui.native_page.TouchEnabledDelegate; +import org.chromium.chrome.browser.ui.signin.signin_promo.NtpSigninPromoCoordinator; +import org.chromium.chrome.browser.url_constants.UrlConstantResolver; +import org.chromium.chrome.browser.url_constants.UrlConstantResolverFactory; +import org.chromium.chrome.browser.util.BrowserUiUtils; +import org.chromium.chrome.browser.util.BrowserUiUtils.ModuleTypeOnStartAndNtp; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; +import org.chromium.components.browser_ui.widget.displaystyle.DisplayStyleObserver; +import org.chromium.components.browser_ui.widget.displaystyle.UiConfig; +import org.chromium.components.omnibox.AutocompleteRequestType; +import org.chromium.components.omnibox.OmniboxFeatures; +import org.chromium.components.signin.SigninFeatureMap; +import org.chromium.components.signin.SigninFeatures; +import org.chromium.content_public.browser.LoadUrlParams; +import org.chromium.ui.base.ActivityResultTracker; +import org.chromium.ui.base.MimeTypeUtils; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.ui.modaldialog.ModalDialogManager; +import org.chromium.ui.text.EmptyTextWatcher; +import org.chromium.url.GURL; + +import java.util.function.Supplier; + +/** + * Layout for the new tab page. This positions the page elements in the correct vertical positions. + * There are no separate phone and tablet UIs; this layout adapts based on the available space. + */ +@NullMarked +public class NewTabPageCoordinator { + private static final String TAG = "NewTabPageLayout"; + + private final NewTabPageManager mManager; + private final Activity mActivity; + private final NewTabPageLayout mNewTabPageLayout; + private final NewTabPageLayout.Delegate mLayoutDelegate; + private LogoCoordinator mLogoCoordinator; + private SearchBoxCoordinator mSearchBoxCoordinator; + private @Nullable MostVisitedTilesCoordinator mMostVisitedTilesCoordinator; + + private @Nullable OnSearchBoxScrollListener mSearchBoxScrollListener; + + private WindowAndroid mWindowAndroid; + private Profile mProfile; + private ActivityResultTracker mActivityResultTracker; + private BottomSheetController mBottomSheetController; + private ModalDialogManager mModalDialogManager; + private SnackbarManager mSnackbarManager; + private UiConfig mUiConfig; + private @Nullable DisplayStyleObserver mDisplayStyleObserver; + private CallbackController mCallbackController = new CallbackController(); + private SearchEngineUtils.@Nullable SearchEngineIconObserver mSearchEngineIconObserver; + private SearchEngineUtils.@Nullable SearchBoxHintTextObserver mSearchBoxHintTextObserver; + + /** + * Whether the tiles shown in the layout have finished loading. With {@link #mHasShownView}, + * it's one of the 2 flags used to track initialisation progress. + */ + private boolean mTilesLoaded; + + /** + * Whether the view has been shown at least once. With {@link #mTilesLoaded}, it's one of the 2 + * flags used to track initialization progress. + */ + private boolean mHasShownView; + + private boolean mSearchProviderHasLogo = true; + private boolean mSearchProviderIsGoogle; + private boolean mShowingNonStandardGoogleLogo; + + private boolean mInitialized; + + private float mUrlFocusChangePercent; + private boolean mDisableUrlFocusChangeAnimations; + + /** Flag used to request some layout changes after the next layout pass is completed. */ + private boolean mTileCountChanged; + + private boolean mSnapshotTileGridChanged; + private int mSearchBoxTwoSideMargin; + + /** + * Vertical inset to add to the top and bottom of the search box bounds. May be 0 if no inset + * should be applied. See {@link Rect#inset(int, int)}. + */ + private int mSearchBoxBoundsVerticalInset; + + private FeedSurfaceScrollDelegate mScrollDelegate; + + private boolean mIsTablet; + private @Nullable Supplier<Integer> mTabStripHeightSupplier; + + private Callback<Logo> mOnLogoAvailableCallback; + + // mIsComposeplateEnabled is null before checking whether to initialize composeplate view in + // NewTabPageCoordinator#initialize(). + private @Nullable Boolean mIsComposeplateEnabled; + private boolean mIsComposeplatePolicyEnabled; + private boolean mIsComposeplateViewInitialized; + private Supplier<GURL> mComposeplateUrlSupplier; + private @Nullable ComposeplateCoordinator mComposeplateCoordinator; + // Previous visibility states for metrics. + private @Nullable Boolean mPreviousVoiceSearchButtonVisible; + private @Nullable Boolean mPreviousLensButtonVisible; + private SearchEngineUtils mSearchEngineUtils; + private final int mNtpSearchBoxTransitionStartOffset; + private final int mNtpSearchBoxTopMarginWithoutLogo; + private final boolean mEnableLogs; + private int mCurrentNtpFakeSearchBoxTransitionStartOffset; + private int mTopInset; + private @Nullable OnLayoutChangeListener mOnLayoutChangeListener; + // TODO(crbug.com/451602301): remove @Nullable and all null checks once + // ENABLE_SEAMLESS_SIGNIN is removed after the experiment. + private @Nullable NtpSigninPromoCoordinator mSigninPromoCoordinator; + + private @Nullable Boolean mIsWhiteBackgroundOnSearchBoxApplied; + + /** + * Constructor of the NewTabPageCoordinator. + * + * @param manager NewTabPageManager used to perform various actions when the user interacts with + * the page. + * @param activity The activity that currently owns the new tab page + * @param newTabPageLayout The new tab page layout. + */ + public NewTabPageCoordinator( + NewTabPageManager manager, Activity activity, NewTabPageLayout newTabPageLayout) { + mManager = manager; + mActivity = activity; + mNewTabPageLayout = newTabPageLayout; + + Resources resources = mActivity.getResources(); + mNtpSearchBoxTopMarginWithoutLogo = + resources.getDimensionPixelSize(R.dimen.mvt_container_top_margin); + mNtpSearchBoxTransitionStartOffset = + resources.getDimensionPixelSize(R.dimen.ntp_search_box_transition_start_offset); + + mEnableLogs = ChromeFeatureList.sNewTabPageCustomizationV2EnableLogs.getValue(); + + mLayoutDelegate = + new NewTabPageLayout.Delegate() { + @Override + public void onMeasure(int width) { + NewTabPageCoordinator.this.onMeasure(width); + } + + @Override + public void onAttachedToWindow() { + NewTabPageCoordinator.this.onAttachedToWindow(); + } + + @Override + public void updateActionButtonVisibility() { + NewTabPageCoordinator.this.updateActionButtonVisibility(); + } + }; + mNewTabPageLayout.setDelegate(mLayoutDelegate); + } + + /** + * Initializes the NewTabPageLayout. This must be called immediately after inflation, before is + * object is used in any other way. + * + * @param tileGroupDelegate Delegate for {@link TileGroup}. + * @param searchProviderHasLogo Whether the search provider has a logo. + * @param searchProviderIsGoogle Whether the search provider is Google. + * @param scrollDelegate The delegate used to obtain information about scroll state. + * @param touchEnabledDelegate The {@link TouchEnabledDelegate} for handling whether touch + * events are allowed. + * @param uiConfig UiConfig that provides display information about this view. + * @param lifecycleDispatcher Activity lifecycle dispatcher. + * @param profile The {@link Profile} associated with the NTP. + * @param windowAndroid An instance of a {@link WindowAndroid}. + * @param activityResultTracker Tracker of activity results. + * @param bottomSheetController Used to interact with the bottom sheet. + * @param snackbarManager Manages snackbars shown in the app. + * @param isTablet {@code true} if the NTP surface is in tablet mode. + * @param tabStripHeightSupplier Supplier of the tab strip height. + */ + @Initializer + public void initialize( + TileGroup.Delegate tileGroupDelegate, + boolean searchProviderHasLogo, + boolean searchProviderIsGoogle, + FeedSurfaceScrollDelegate scrollDelegate, + TouchEnabledDelegate touchEnabledDelegate, + UiConfig uiConfig, + ActivityLifecycleDispatcher lifecycleDispatcher, + Profile profile, + WindowAndroid windowAndroid, + ActivityResultTracker activityResultTracker, + BottomSheetController bottomSheetController, + ModalDialogManager modalDialogManager, + SnackbarManager snackbarManager, + boolean isTablet, + Supplier<Integer> tabStripHeightSupplier, + Supplier<GURL> composeplateUrlSupplier) { + TraceEvent.begin(TAG + ".initialize()"); + mScrollDelegate = scrollDelegate; + mProfile = profile; + mUiConfig = uiConfig; + mWindowAndroid = windowAndroid; + mActivityResultTracker = activityResultTracker; + mBottomSheetController = bottomSheetController; + mModalDialogManager = modalDialogManager; + mSnackbarManager = snackbarManager; + mIsTablet = isTablet; + mTabStripHeightSupplier = tabStripHeightSupplier; + mSearchEngineUtils = SearchEngineUtils.getForProfile(mProfile); + mComposeplateUrlSupplier = composeplateUrlSupplier; + + if (mIsTablet) { + mDisplayStyleObserver = this::onDisplayStyleChanged; + mUiConfig.addObserver(mDisplayStyleObserver); + } else { + // On first run, the NewTabPageLayout is initialized behind the First Run Experience, + // meaning the UiConfig will pickup the screen layout then. However + // onConfigurationChanged is not called on orientation changes until the FRE is + // completed. This means that if a user starts the FRE in one orientation, changes an + // orientation and then leaves the FRE the UiConfig will have the wrong orientation. + // https://crbug.com/683886. + mUiConfig.updateDisplayStyle(); + } + + mSearchBoxCoordinator = new SearchBoxCoordinator(mActivity, mNewTabPageLayout, mIsTablet); + mSearchBoxCoordinator.initialize( + lifecycleDispatcher, mProfile.isOffTheRecord(), mWindowAndroid); + + updateSearchBoxTwoSideMargin(); + initializeLogoCoordinator(); + setSearchProviderInfo(searchProviderHasLogo, searchProviderIsGoogle); + initializeMostVisitedTilesCoordinator( + mProfile, lifecycleDispatcher, tileGroupDelegate, touchEnabledDelegate); + + mSearchEngineIconObserver = (newIcon) -> mSearchBoxCoordinator.setSearchEngineIcon(newIcon); + mSearchEngineUtils.addIconObserver(mSearchEngineIconObserver); + setSearchBoxTextAppearance(); + + initializeSearchBoxTextView(); + initializeVoiceSearchButton(); + initializeLensButton(); + + initializeComposeplateFlags(mProfile); + if (assumeNonNull(mIsComposeplateEnabled)) { + initializeComposeplate(); + } + + // This should be called after both mSearchBoxCoordinator and mComposeplateCoordinator are + // initialized. + onCustomizedBackgroundChanged( + NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox()); + + // This should called after flags of composeplate view are initialized. + setSearchBoxHeightBoundsVerticalInset(); + + updateActionButtonVisibility(); + initializeLayoutChangeListener(); + if (SigninFeatureMap.isEnabled(SigninFeatures.ENABLE_SEAMLESS_SIGNIN)) { + initializeSigninPromoCoordinator(); + } + + // Initialize Searchbox observers + mSearchBoxHintTextObserver = this::onSearchBoxHintTextChanged; + mSearchEngineUtils.addSearchBoxHintTextObserver(mSearchBoxHintTextObserver); + + // TODO(https://crbug.com/487641528): Destroy NewTabPageCoordinator in + // NewTabPage#destroy(). + mManager.addDestructionObserver(NewTabPageCoordinator.this::onDestroy); + mInitialized = true; + + TraceEvent.end(TAG + ".initialize()"); + } + + /** Sets the height of the search box and mSearchBoxBoundsVerticalInset. */ + private void setSearchBoxHeightBoundsVerticalInset() { + Resources resources = mActivity.getResources(); + int searchBoxHeight = + NtpCustomizationUtils.getSearchBoxHeightWithShadows( + resources, + assumeNonNull(mIsComposeplateEnabled), + Boolean.TRUE.equals(mIsWhiteBackgroundOnSearchBoxApplied)); + mSearchBoxCoordinator.setHeight(searchBoxHeight); + + mSearchBoxBoundsVerticalInset = + Math.round( + (searchBoxHeight + - resources.getDimensionPixelSize( + R.dimen.toolbar_height_no_shadow)) + / 2f); + } + + public void reload() { + // TODO(crbug.com/41487877): Add handler in Magic Stack and dispatcher. + } + + public void enableSearchBoxEditText(boolean enable) { + mSearchBoxCoordinator.enableSearchBoxEditText(enable); + } + + /** + * @return The {@link FeedSurfaceScrollDelegate} for this class. + */ + FeedSurfaceScrollDelegate getScrollDelegate() { + return mScrollDelegate; + } + + /** Sets up the hint text and event handlers for the search box text view. */ + private void initializeSearchBoxTextView() { + TraceEvent.begin(TAG + ".initializeSearchBoxTextView()"); + + mSearchBoxCoordinator.setSearchBoxClickListener( + v -> mManager.focusSearchBox(false, AutocompleteRequestType.SEARCH, null)); + + // @TODO(crbug.com/41492572): Add test case for search box OnDragListener. + mSearchBoxCoordinator.setSearchBoxDragListener( + new View.OnDragListener() { + @Override + public boolean onDrag(View view, DragEvent dragEvent) { + // Disable search box EditText when browser content is dropped, its + // re-enabled in {@link ChromeTabbedOnDragListener}, since a disabled view + // will stop receiving further drag events. Given the child-first drag event + // dispatch, disabling the TextView at ACTION_DRAG_STARTED is necessary to + // prevent it from registering as a drop target and consuming the + // ACTION_DROP event, thereby ensuring {@link ChromeTabbedOnDragListener} + // receives it. + if (MimeTypeUtils.clipDescriptionHasBrowserContent( + dragEvent.getClipDescription()) + && dragEvent.getAction() == DragEvent.ACTION_DRAG_STARTED) { + enableSearchBoxEditText(false); + } + return false; + } + }); + + mSearchBoxCoordinator.setSearchBoxTextWatcher( + new EmptyTextWatcher() { + @Override + public void afterTextChanged(Editable s) { + if (s.length() == 0) return; + mManager.focusSearchBox( + false, AutocompleteRequestType.SEARCH, s.toString()); + mSearchBoxCoordinator.setSearchText(""); + } + }); + TraceEvent.end(TAG + ".initializeSearchBoxTextView()"); + } + + public void onSearchBoxHintTextChanged() { + mSearchBoxCoordinator.setSearchBoxHintText( + mSearchEngineUtils.getOmniboxHintText(AutocompleteRequestType.SEARCH)); + } + + private void setSearchBoxTextAppearance() { + boolean shouldApplyWhiteBackground = + NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox(); + + if (shouldApplyWhiteBackground) { + mSearchBoxCoordinator.setSearchBoxTextAppearance( + R.style.TextAppearance_FakeSearchBoxTextMediumDark); + } else { + mSearchBoxCoordinator.setSearchBoxTextAppearance( + R.style.TextAppearance_FakeSearchBoxTextMedium); + } + } + + private void initializeVoiceSearchButton() { + TraceEvent.begin(TAG + ".initializeVoiceSearchButton()"); + View.OnClickListener voiceSearchButtonClickListener = + v -> mManager.focusSearchBox(true, AutocompleteRequestType.SEARCH, null); + mSearchBoxCoordinator.addVoiceSearchButtonClickListener(voiceSearchButtonClickListener); + TraceEvent.end(TAG + ".initializeVoiceSearchButton()"); + } + + private void initializeLensButton() { + TraceEvent.begin(TAG + ".initializeLensButton()"); + View.OnClickListener lensButtonClickListener = + v -> { + LensMetrics.recordClicked(LensEntryPoint.NEW_TAB_PAGE); + mSearchBoxCoordinator.startLens(LensEntryPoint.NEW_TAB_PAGE); + }; + mSearchBoxCoordinator.addLensButtonClickListener(lensButtonClickListener); + TraceEvent.end(TAG + ".initializeLensButton()"); + } + + private void initializeComposeplateFlags(Profile profile) { + mIsComposeplateEnabled = ComposeplateUtils.isComposeplateEnabled(mIsTablet, profile); + mIsComposeplatePolicyEnabled = + mIsComposeplateEnabled && ComposeplateUtils.isEnabledByPolicy(profile); + } + + private void initializeComposeplate() { + if (mIsComposeplateViewInitialized) return; + + mIsComposeplateViewInitialized = true; + + boolean shouldApplyWhiteBackgroundOnSearchBox = + NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox(); + + ViewStub composeplateViewStub = + mNewTabPageLayout.findViewById(R.id.composeplate_view_v2_stub); + ViewGroup composeplateView = (ViewGroup) composeplateViewStub.inflate(); + if (NtpCustomizationUtils.isNtpThemeCustomizationEnabled()) { + // TODO(https://crbug.com/423579377): Moves the layout parameters to + // composeplate_view_layout_v2.xml after the feature NewTabPageCustomizationV2 is + // launched. + NewTabPageUtils.applyUpdatedLayoutParamsForComposeplateView(composeplateView); + } + mComposeplateCoordinator = new ComposeplateCoordinator(composeplateView, mProfile); + mComposeplateCoordinator.setIncognitoClickListener(this::onIncognitoButtonClicked); + // Don't log click metrics in this listener, since the mComposeplateCoordinator will + // log. + mComposeplateCoordinator.setComposeplateButtonClickListener( + this::onComposeplateButtonClicked); + + if (shouldApplyWhiteBackgroundOnSearchBox) { + // It is safe to call mComposeplateCoordinator.applyWhiteBackgroundWithShadow() again + // since it is no-op if the white background has been applied. + mComposeplateCoordinator.applyWhiteBackgroundWithShadow(/* apply= */ true); + } + } + + private void onComposeplateButtonClicked(View view) { + if (OmniboxFeatures.sOmniboxMultimodalInput.isEnabled() + && OmniboxFeatures.sRedirectComposeplateButton.getValue() + && !mIsTablet + && mIsComposeplatePolicyEnabled) { + mManager.focusSearchBox(false, AutocompleteRequestType.AI_MODE, null); + return; + } + + GURL composeplateUrl = mComposeplateUrlSupplier.get(); + if (composeplateUrl == null) return; + + mManager.getNativePageHost() + .loadUrl(new LoadUrlParams(composeplateUrl), /* incognito= */ false); + } + + private void onIncognitoButtonClicked(View view) { + if (!IncognitoUtils.isIncognitoModeEnabled(mProfile)) return; + + UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile); + mManager.getNativePageHost().loadUrl(new LoadUrlParams(resolver.getNtpUrl()), true); + } + + private void initializeLayoutChangeListener() { + TraceEvent.begin(TAG + ".initializeLayoutChangeListener()"); + mOnLayoutChangeListener = this::onLayoutChanged; + mNewTabPageLayout.addOnLayoutChangeListener(mOnLayoutChangeListener); + TraceEvent.end(TAG + ".initializeLayoutChangeListener()"); + } + + private void onLayoutChanged( + View view, + int left, + int top, + int right, + int bottom, + int oldLeft, + int oldTop, + int oldRight, + int oldBottom) { + int oldHeight = oldBottom - oldTop; + int newHeight = bottom - top; + + if (oldHeight == newHeight && !mTileCountChanged) return; + mTileCountChanged = false; + + // Re-apply the url focus change amount after a rotation to ensure the views are + // correctly placed with their new layout configurations. + onUrlFocusAnimationChanged(); + updateSearchBoxOnScroll(); + + // The positioning of elements may have been changed (since the elements expand + // to fill the available vertical space), so adjust the scroll. + if (mScrollDelegate.isScrollViewInitialized()) mScrollDelegate.snapScroll(); + } + + private void initializeLogoCoordinator() { + Callback<LoadUrlParams> logoClickedCallback = + mCallbackController.makeCancelable( + (urlParams) -> { + mManager.getNativePageHost().loadUrl(urlParams, /* incognito= */ false); + BrowserUiUtils.recordModuleClickHistogram( + ModuleTypeOnStartAndNtp.DOODLE); + }); + mOnLogoAvailableCallback = + mCallbackController.makeCancelable( + (logo) -> { + mSnapshotTileGridChanged = true; + mShowingNonStandardGoogleLogo = logo != null && mSearchProviderIsGoogle; + NtpCustomizationConfigManager.getInstance() + .setDefaultSearchEngineLogoBitmap( + logo == null ? null : logo.image); + }); + + mLogoCoordinator = + new LogoCoordinator( + mActivity, + logoClickedCallback, + mNewTabPageLayout, + mOnLogoAvailableCallback, + /* visibilityObserver= */ null, + () -> MultiWindowUtils.getInstance().isInMultiWindowMode(mActivity)); + mLogoCoordinator.initWithNative(mProfile); + } + + private void initializeMostVisitedTilesCoordinator( + Profile profile, + ActivityLifecycleDispatcher activityLifecycleDispatcher, + TileGroup.Delegate tileGroupDelegate, + TouchEnabledDelegate touchEnabledDelegate) { + View mvTilesContainerLayout = mNewTabPageLayout.findViewById(R.id.mv_tiles_container); + assert mvTilesContainerLayout != null; + + mMostVisitedTilesCoordinator = + new MostVisitedTilesCoordinator( + mActivity, + activityLifecycleDispatcher, + mvTilesContainerLayout, + () -> mSnapshotTileGridChanged = true, + () -> { + if (mUrlFocusChangePercent == 1f) mTileCountChanged = true; + }); + + mMostVisitedTilesCoordinator.initWithNative( + profile, mManager, tileGroupDelegate, touchEnabledDelegate); + + if (ChromeFeatureList.sNewTabPageCustomizationForMvt.isEnabled()) { + mMostVisitedTilesCoordinator.updateMvtVisibility(); + } + } + + private void initializeSigninPromoCoordinator() { + ViewStub signinPromoViewContainerStub = + mNewTabPageLayout.findViewById(R.id.signin_promo_view_container_stub); + mSigninPromoCoordinator = + new NtpSigninPromoCoordinator( + mWindowAndroid, + mActivity, + mProfile, + mActivityResultTracker, + SigninAndHistorySyncActivityLauncherImpl.get(), + mBottomSheetController, + mModalDialogManager, + mSnackbarManager, + DeviceLockActivityLauncherImpl.get(), + signinPromoViewContainerStub, + SetupListModuleUtils::isSetupListActive); + } + + /** Updates the search box when the parent view's scroll position is changed. */ + void updateSearchBoxOnScroll() { + if (mDisableUrlFocusChangeAnimations) return; + + // When the page changes (tab switching or new page loading), it is possible that events + // (e.g. delayed view change notifications) trigger calls to these methods after + // the current page changes. We check it again to make sure we don't attempt to update the + // wrong page. + if (!mManager.isCurrentPage()) return; + + if (mSearchBoxScrollListener != null) { + mSearchBoxScrollListener.onNtpScrollChanged(getToolbarTransitionPercentage()); + } + } + + /** + * Calculates the percentage (between 0 and 1) of the transition from the search box to the + * omnibox at the top of the New Tab Page, which is determined by the amount of scrolling and + * the position of the search box. + * + * @return the transition percentage + */ + float getToolbarTransitionPercentage() { + return mSearchBoxCoordinator.getToolbarTransitionPercentage( + mScrollDelegate, + mTabStripHeightSupplier, + mCurrentNtpFakeSearchBoxTransitionStartOffset); + } + + /** + * @return The fake search box view. + */ + public View getSearchBoxView() { + return mSearchBoxCoordinator.getView(); + } + + public void onSwitchToForeground() { + if (mMostVisitedTilesCoordinator != null) { + mMostVisitedTilesCoordinator.onSwitchToForeground(); + } + } + + /** + * Should be called every time one of the flags used to track initialization progress changes. + * Finalizes initialization once all the preliminary steps are complete. + * + * @see #mHasShownView + * @see #mTilesLoaded + */ + private void onInitializationProgressChanged() { + if (!hasLoadCompleted()) return; + + mManager.onLoadingComplete(); + + // Load the logo after everything else is finished, since it's lower priority. + if (mLogoCoordinator != null) mLogoCoordinator.loadSearchProviderLogoWithAnimation(); + } + + /** + * To be called to notify that the tiles have finished loading. Will do nothing if a load was + * previously completed. + */ + public void onTilesLoaded() { + if (mTilesLoaded) return; + mTilesLoaded = true; + + onInitializationProgressChanged(); + } + + /** + * Changes the layout depending on whether the selected search provider (e.g. Google, Bing) has + * a logo. + * + * @param hasLogo Whether the search provider has a logo. + * @param isGoogle Whether the search provider is Google. + */ + void setSearchProviderInfo(boolean hasLogo, boolean isGoogle) { + if (hasLogo == mSearchProviderHasLogo + && isGoogle == mSearchProviderIsGoogle + && mInitialized) { + return; + } + boolean isSearchProviderIsGoogleChanged = mSearchProviderIsGoogle != isGoogle; + mSearchProviderHasLogo = hasLogo; + mSearchProviderIsGoogle = isGoogle; + + if (!mSearchProviderIsGoogle) { + mShowingNonStandardGoogleLogo = false; + } + + setSearchProviderTopMargin(); + setLogoViewBottomMargin(); + + updateTilesLayoutMargins(); + + // Hide or show the views above the most visited tiles as needed, e.g, spacers. The + // visibility of Logo is handled by LogoCoordinator. + setSearchBoxTextAppearance(); + + // Skips if the flag hasn't been initialized since the initialization of the following + // components will be called again in #initialize(). + if (mIsComposeplateEnabled != null) { + // when mSearchProviderIsGoogle is changed, mIsComposeplateEnabled might be changed too, + // recalculate its value. + if (isSearchProviderIsGoogleChanged) { + boolean previousIsComposeplateEnabled = mIsComposeplateEnabled; + initializeComposeplateFlags(mProfile); + if (!previousIsComposeplateEnabled + && mIsComposeplateEnabled + && mComposeplateCoordinator == null) { + // If the composeplate view is enabled while mComposeplateCoordinator hasn't + // been initialized yet, initialize it now. + initializeComposeplate(); + } + + if (previousIsComposeplateEnabled != mIsComposeplateEnabled) { + // When the flag value is changed, the height of search box might be changed. + setSearchBoxHeightBoundsVerticalInset(); + // Updates the composeplate view's visibility. + updateActionButtonVisibility(); + } + } + } + + onUrlFocusAnimationChanged(); + + mSnapshotTileGridChanged = true; + } + + /** Updates the margins for the most visited tiles layout based on what is shown above it. */ + private void updateTilesLayoutMargins() { + if (mMostVisitedTilesCoordinator == null) return; + + mMostVisitedTilesCoordinator.updateTilesLayoutMargins( + shouldShowLogo(), + mIsWhiteBackgroundOnSearchBoxApplied == null + ? false + : mIsWhiteBackgroundOnSearchBoxApplied, + mIsTablet); + } + + /** + * Updates whether the NewTabPage should animate on URL focus changes. + * + * @param disable Whether to disable the animations. + */ + void setUrlFocusAnimationsDisabled(boolean disable) { + if (disable == mDisableUrlFocusChangeAnimations) return; + mDisableUrlFocusChangeAnimations = disable; + if (!disable) onUrlFocusAnimationChanged(); + } + + /** + * @return Whether URL focus animations are currently disabled. + */ + boolean urlFocusAnimationsDisabled() { + return mDisableUrlFocusChangeAnimations; + } + + /** + * Specifies the percentage the URL is focused during an animation. 1.0 specifies that the URL + * bar has focus and has completed the focus animation. 0 is when the URL bar is does not have + * any focus. + * + * @param percent The percentage of the URL bar focus animation. + */ + void setUrlFocusChangeAnimationPercent(float percent) { + mUrlFocusChangePercent = percent; + onUrlFocusAnimationChanged(); + } + + /** + * @return The percentage that the URL bar is focused during an animation. + */ + @VisibleForTesting + float getUrlFocusChangeAnimationPercent() { + return mUrlFocusChangePercent; + } + + void onUrlFocusAnimationChanged() { + /* + * Avoid Y-translation when animation is disabled, view is moving or on tablet form-factor. + * Context for tablets - Unlike phones, this method is not called on tablets during URL + * focus post NTP load. However when physical keyboard is present, we try to auto-focus URL + * during page load causing this method to be called. Disabling this for all cases on this + * form-factor since this translation does not WAI. (see crbug.com/40910640) + */ + if (mDisableUrlFocusChangeAnimations || mIsTablet) return; + + // Translate so that the search box is at the top, but only upwards. + int basePosition = + mScrollDelegate.getVerticalScrollOffset() + mNewTabPageLayout.getPaddingTop(); + int target = + Math.max( + basePosition, + getSearchBoxView().getBottom() + - getSearchBoxView().getPaddingBottom() + - mSearchBoxBoundsVerticalInset); + + float translationY = mUrlFocusChangePercent * (basePosition - target); + setTranslationYOfFakeboxAndAbove(translationY); + } + + /** + * Sets the translation_y of the fakebox and all views above it, but not the views below. Used + * when the url focus animation is combined with the omnibox suggestions list animation to + * reduce the number of visual elements in motion. + */ + private void setTranslationYOfFakeboxAndAbove(float translationY) { + for (int i = 0; i < mNewTabPageLayout.getChildCount(); i++) { + View view = mNewTabPageLayout.getChildAt(i); + view.setTranslationY(translationY); + if (view.getId() == R.id.search_box) return; + } + } + + /** + * Updates the opacity of the search box when scrolling. + * + * @param alpha opacity (alpha) value to use. + */ + public void setSearchBoxAlpha(float alpha) { + mSearchBoxCoordinator.setAlpha(alpha); + } + + /** + * Updates the opacity of the search provider logo when scrolling. + * + * @param alpha opacity (alpha) value to use. + */ + public void setSearchProviderLogoAlpha(float alpha) { + if (mLogoCoordinator != null) mLogoCoordinator.setAlpha(alpha); + } + + /** + * Get the bounds of the search box in relation to the top level {@code parentView}. + * + * @param bounds The current drawing location of the search box. + * @param translation The translation applied to the search box by the parent view hierarchy up + * to the {@code parentView}. + * @param parentView The top level parent view used to translate search box bounds. + */ + void getSearchBoxBounds(Rect bounds, Point translation, View parentView) { + mSearchBoxCoordinator.getSearchBoxBounds( + bounds, translation, parentView, mScrollDelegate, mSearchBoxBoundsVerticalInset); + } + + /** Returns the fake search box's transition start offset on NTP. */ + private int getNtpSearchBoxTransitionStartOffset(boolean showFakeSearchBoxWithoutLogo) { + if (mIsTablet && showFakeSearchBoxWithoutLogo) { + // On tablets, it is possible to show fake search box if DSE doesn't have logo if DSE + // mobile parity v2 is enabled. The mNTPFakeSearchBoxTransitionStartOffset is used to + // calculate scrolling percentage in getToolbarTransitionPercentage(). Reset to 0 when + // no doodle is shown for 3p DSE to prevent the alpha of fake search box being set to 0 + // (transparent) by ToolbarTablet#updateNtp(). + return 0; + } else { + return mNtpSearchBoxTransitionStartOffset; + } + } + + private void setSearchProviderTopMargin() { + boolean showFakeSearchBoxWithoutLogo = !mSearchProviderHasLogo; + mCurrentNtpFakeSearchBoxTransitionStartOffset = + getNtpSearchBoxTransitionStartOffset(showFakeSearchBoxWithoutLogo); + + int topMargin = showFakeSearchBoxWithoutLogo ? mNtpSearchBoxTopMarginWithoutLogo : 0; + mSearchBoxCoordinator.setTopMargin(topMargin); + + if (mLogoCoordinator != null) { + mLogoCoordinator.setTopMargin(getLogoTopMargin()); + } + } + + private void setLogoViewBottomMargin() { + if (mLogoCoordinator == null) return; + + boolean shouldShowShadow = Boolean.TRUE.equals(mIsWhiteBackgroundOnSearchBoxApplied); + int logoViewBottomMarginPx = + NtpCustomizationUtils.getLogoViewBottomMarginPx( + mActivity.getResources(), shouldShowShadow); + mLogoCoordinator.setBottomMargin(logoViewBottomMarginPx); + } + + private int getLogoTopMargin() { + Resources resources = mActivity.getResources(); + + if (mShowingNonStandardGoogleLogo && mSearchProviderHasLogo) { + return LogoUtils.getTopMarginForDoodle(resources); + } + + return resources.getDimensionPixelSize(R.dimen.ntp_logo_margin_top); + } + + /** + * Sets the listener for search box scroll changes. + * + * @param listener The listener to be notified on changes. + */ + void setSearchBoxScrollListener(@Nullable OnSearchBoxScrollListener listener) { + mSearchBoxScrollListener = listener; + if (mSearchBoxScrollListener != null) updateSearchBoxOnScroll(); + } + + // NewTabPageLayout.Delegate implementations. + public void onMeasure(int width) { + if (mIsTablet && mMostVisitedTilesCoordinator != null) { + mMostVisitedTilesCoordinator.calculateTabletMvtWidth(width); + } + + unifyElementWidths(width); + } + + public void onAttachedToWindow() { + if (!mHasShownView) { + mHasShownView = true; + onInitializationProgressChanged(); + TraceEvent.instant("NewTabPageSearchAvailable"); + } + } + + /** Update the visibility of the action buttons. */ + public void updateActionButtonVisibility() { + boolean shouldShowVoiceSearchButton = mManager.isVoiceSearchEnabled(); + boolean shouldShowLensButton = + mSearchBoxCoordinator.isLensEnabled(LensEntryPoint.NEW_TAB_PAGE); + if (mIsComposeplateEnabled == null) return; + + mSearchBoxCoordinator.setVoiceSearchButtonVisibility(shouldShowVoiceSearchButton); + mSearchBoxCoordinator.setLensButtonVisibility(shouldShowLensButton); + boolean shouldShowComposeplateButton = false; + // As long as mComposeplateCoordinator has been initialized, we should update its + // visibility. + if (mComposeplateCoordinator != null) { + shouldShowComposeplateButton = + mIsComposeplateEnabled + && mSearchProviderIsGoogle + && IncognitoUtils.isIncognitoModeEnabled(mProfile); + mComposeplateCoordinator.setVisibility( + shouldShowComposeplateButton, mManager.isCurrentPage()); + } + updatePreviousButtonVisibilityAndRecordMetrics( + shouldShowVoiceSearchButton, shouldShowLensButton, shouldShowComposeplateButton); + } + + /** + * Updates the previous visibility state of the voice search and lens buttons and records + * metrics if their visibility has changed on the current page. + * + * @param isVoiceSearchButtonVisible The new visibility state of the voice search button. + * @param isLensButtonVisible The new visibility state of the lens button. + * @param isComposeplateButtonVisible The new visibility state of the composeplate button. + */ + private void updatePreviousButtonVisibilityAndRecordMetrics( + boolean isVoiceSearchButtonVisible, + boolean isLensButtonVisible, + boolean isComposeplateButtonVisible) { + if (!mManager.isCurrentPage() + || (mPreviousVoiceSearchButtonVisible != null + && isVoiceSearchButtonVisible == mPreviousVoiceSearchButtonVisible + && mPreviousLensButtonVisible != null + && isLensButtonVisible == mPreviousLensButtonVisible)) { + return; + } + + if (mPreviousLensButtonVisible == null + || isLensButtonVisible != mPreviousLensButtonVisible) { + LensMetrics.recordShown(LensEntryPoint.NEW_TAB_PAGE, isLensButtonVisible); + } + + ComposeplateMetricsUtils.recordFakeSearchBoxImpression2(); + ComposeplateMetricsUtils.recordFakeSearchBoxComposeplateButtonImpression2( + isComposeplateButtonVisible); + + mPreviousVoiceSearchButtonVisible = isVoiceSearchButtonVisible; + mPreviousLensButtonVisible = isLensButtonVisible; + } + + /** + * @see InvalidationAwareThumbnailProvider#shouldCaptureThumbnail() + */ + public boolean shouldCaptureThumbnail() { + return mSnapshotTileGridChanged; + } + + /** + * Should be called before a thumbnail of the parent view is captured. + * + * @see InvalidationAwareThumbnailProvider#captureThumbnail(Canvas) + */ + public void onPreCaptureThumbnail() { + if (mLogoCoordinator != null) mLogoCoordinator.endFadeAnimation(); + mSnapshotTileGridChanged = false; + } + + private boolean shouldShowLogo() { + return mSearchProviderHasLogo; + } + + private boolean hasLoadCompleted() { + return mHasShownView && mTilesLoaded; + } + + @SuppressWarnings("NullAway") + private void onDestroy() { + if (mCallbackController != null) { + mCallbackController.destroy(); + mCallbackController = null; + } + + if (mLogoCoordinator != null) { + mLogoCoordinator.destroy(); + mLogoCoordinator = null; + } + + mSearchBoxCoordinator.destroy(); + mSearchBoxCoordinator = null; + + if (mMostVisitedTilesCoordinator != null) { + mMostVisitedTilesCoordinator.destroyMvtiles(); + mMostVisitedTilesCoordinator = null; + } + + if (mIsTablet) { + mUiConfig.removeObserver(mDisplayStyleObserver); + mDisplayStyleObserver = null; + } + + if (mSearchEngineUtils != null) { + if (mSearchBoxHintTextObserver != null) { + mSearchEngineUtils.removeSearchBoxHintTextObserver(mSearchBoxHintTextObserver); + mSearchBoxHintTextObserver = null; + } + if (mSearchEngineIconObserver != null) { + mSearchEngineUtils.removeIconObserver(mSearchEngineIconObserver); + mSearchEngineIconObserver = null; + } + mSearchEngineUtils = null; + } + + mNewTabPageLayout.removeOnLayoutChangeListener(mOnLayoutChangeListener); + mOnLayoutChangeListener = null; + mNewTabPageLayout.setDelegate(null); + + if (mComposeplateCoordinator != null) { + mComposeplateCoordinator.destroy(); + mComposeplateCoordinator = null; + } + + if (mSigninPromoCoordinator != null) { + mSigninPromoCoordinator.destroy(); + mSigninPromoCoordinator = null; + } + + mSearchBoxScrollListener = null; + mComposeplateUrlSupplier = null; + } + + /** Makes the Search Box and Logo as wide as Most Visited. */ + private void unifyElementWidths(int width) { + int searchBoxWidth = width - mSearchBoxTwoSideMargin; + if (mSearchBoxCoordinator != null) { + mSearchBoxCoordinator.setLayoutWidth(searchBoxWidth); + } + + if (mLogoCoordinator != null) { + mLogoCoordinator.setLayoutWidth(width); + } + + if (mComposeplateCoordinator != null) { + mComposeplateCoordinator.setLayoutWidth(searchBoxWidth); + } + } + + LogoCoordinator getLogoCoordinatorForTesting() { + return mLogoCoordinator; + } + + private void onDisplayStyleChanged(UiConfig.DisplayStyle newDisplayStyle) { + if (!mIsTablet) return; + + updateDoodleOnTablet(); + if (mMostVisitedTilesCoordinator != null) { + mMostVisitedTilesCoordinator.updateMvtOnTablet(); + } + updateSearchBoxTwoSideMargin(); + } + + /** + * Adjusts the doodle size while the tablet transitions to or from a multi-screen layout, + * ensuring the change occurs post-logo initialization. + */ + private void updateDoodleOnTablet() { + if (!mIsTablet || mLogoCoordinator == null) return; + + mLogoCoordinator.updateDoodleOnTablet(mShowingNonStandardGoogleLogo); + } + + private void updateSearchBoxTwoSideMargin() { + mSearchBoxTwoSideMargin = + NtpCustomizationUtils.getSearchBoxTwoSideMargin( + mActivity.getResources(), mUiConfig, mIsTablet); + } + + /** + * Called when the layout changes between edge-to-edge and standard. + * + * @param systemTopInset The system's top inset, i.e., the height of the Status bar. It is + * always bigger than 0. + * @param supportsEdgeToEdgeOnTop Determines if the NTP should consume this top inset, extending + * itself to the Status bar area. + */ + void onToEdgeChange(int systemTopInset, boolean supportsEdgeToEdgeOnTop) { + // Exits early if the NTP's top padding doesn't require adjustment. + if (NtpCustomizationUtils.shouldSkipTopInsetsChange( + mTopInset, systemTopInset, supportsEdgeToEdgeOnTop)) { + return; + } + + mTopInset = supportsEdgeToEdgeOnTop ? systemTopInset : 0; + mCurrentNtpFakeSearchBoxTransitionStartOffset = + getNtpSearchBoxTransitionStartOffset(!mSearchProviderHasLogo) + mTopInset; + + int toolbarHeightNoShadow = + mActivity.getResources().getDimensionPixelSize(R.dimen.toolbar_height_no_shadow); + // Top padding is applied to the NTP layout, ensuring all UI components remain in their + // original positions after Status bar is hidden. + mNewTabPageLayout.setPaddingRelative( + mNewTabPageLayout.getPaddingStart(), + toolbarHeightNoShadow + mTopInset, + mNewTabPageLayout.getPaddingEnd(), + mNewTabPageLayout.getPaddingBottom()); + + if (mEnableLogs) { + Log.i(TAG, "The top padding to add on the NTP is %d.", mTopInset); + } + } + + /** + * Called when a customized background image is selected or deselected. + * + * @param applyWhiteBackgroundOnSearchBox Whether to apply a white background color to the fake + * search box. + */ + void onCustomizedBackgroundChanged(boolean applyWhiteBackgroundOnSearchBox) { + // If shouldn't apply a white background and the background hasn't been updated before, + // returns now. + if (mIsWhiteBackgroundOnSearchBoxApplied == null && !applyWhiteBackgroundOnSearchBox) { + return; + } + + // If composeplate view flags haven't been initialized yet, returns now. + if (mIsComposeplateEnabled == null) { + return; + } + + // If the background has been updated before and it should remain the same, returns now. + if (mIsWhiteBackgroundOnSearchBoxApplied != null + && mIsWhiteBackgroundOnSearchBoxApplied == applyWhiteBackgroundOnSearchBox) { + return; + } + + // If the fake search box hasn't been initialized, returns now. It is fine to skip here + // because applyWhiteBackgroundWithShadow() will be called immediately after the + // mSearchBoxCoordinator is initialized. + if (mSearchBoxCoordinator == null) return; + + mIsWhiteBackgroundOnSearchBoxApplied = applyWhiteBackgroundOnSearchBox; + + if (mSearchBoxCoordinator != null) { + mSearchBoxCoordinator.applyWhiteBackgroundWithShadow(applyWhiteBackgroundOnSearchBox); + } + + if (mComposeplateCoordinator != null) { + mComposeplateCoordinator.applyWhiteBackgroundWithShadow( + applyWhiteBackgroundOnSearchBox); + } + + setLogoViewBottomMargin(); + // Update the search box height and bounds vertical inset since the shadow padding changed. + setSearchBoxHeightBoundsVerticalInset(); + + if (mMostVisitedTilesCoordinator != null) { + updateTilesLayoutMargins(); + } + } + + /** Returns the top inset of the NTP. */ + int getTopInset() { + return mTopInset; + } + + /** Returns the vertical inset applied to search box bounds. */ + int getSearchBoxBoundsVerticalInset() { + return mSearchBoxBoundsVerticalInset; + } + + NewTabPageLayout getNewTabPageLayout() { + return mNewTabPageLayout; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java index b3d83a7..cea1761 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -1,83 +1,20 @@ -// Copyright 2015 The Chromium Authors +// Copyright 2026 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.chrome.browser.ntp; -import static org.chromium.build.NullUtil.assumeNonNull; - -import android.app.Activity; import android.content.Context; -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.Rect; -import android.text.Editable; import android.util.AttributeSet; -import android.view.DragEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; import android.widget.LinearLayout; -import androidx.annotation.VisibleForTesting; - -import org.chromium.base.Callback; -import org.chromium.base.CallbackController; import org.chromium.base.Log; -import org.chromium.base.TraceEvent; -import org.chromium.build.annotations.Initializer; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; -import org.chromium.chrome.browser.composeplate.ComposeplateCoordinator; -import org.chromium.chrome.browser.composeplate.ComposeplateMetricsUtils; -import org.chromium.chrome.browser.composeplate.ComposeplateUtils; -import org.chromium.chrome.browser.device_lock.DeviceLockActivityLauncherImpl; -import org.chromium.chrome.browser.feed.FeedSurfaceScrollDelegate; -import org.chromium.chrome.browser.flags.ChromeFeatureList; -import org.chromium.chrome.browser.incognito.IncognitoUtils; -import org.chromium.chrome.browser.lens.LensEntryPoint; -import org.chromium.chrome.browser.lens.LensMetrics; -import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; -import org.chromium.chrome.browser.logo.LogoBridge.Logo; -import org.chromium.chrome.browser.logo.LogoCoordinator; -import org.chromium.chrome.browser.logo.LogoUtils; -import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; -import org.chromium.chrome.browser.ntp.NewTabPage.OnSearchBoxScrollListener; -import org.chromium.chrome.browser.ntp.search.SearchBoxCoordinator; -import org.chromium.chrome.browser.ntp_customization.NtpCustomizationConfigManager; -import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils; -import org.chromium.chrome.browser.omnibox.SearchEngineUtils; -import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.setup_list.SetupListModuleUtils; -import org.chromium.chrome.browser.signin.SigninAndHistorySyncActivityLauncherImpl; -import org.chromium.chrome.browser.suggestions.tile.MostVisitedTilesCoordinator; -import org.chromium.chrome.browser.suggestions.tile.TileGroup; -import org.chromium.chrome.browser.suggestions.tile.TileGroup.Delegate; -import org.chromium.chrome.browser.tab_ui.InvalidationAwareThumbnailProvider; -import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; -import org.chromium.chrome.browser.ui.native_page.TouchEnabledDelegate; -import org.chromium.chrome.browser.ui.signin.signin_promo.NtpSigninPromoCoordinator; -import org.chromium.chrome.browser.url_constants.UrlConstantResolver; -import org.chromium.chrome.browser.url_constants.UrlConstantResolverFactory; -import org.chromium.chrome.browser.util.BrowserUiUtils; -import org.chromium.chrome.browser.util.BrowserUiUtils.ModuleTypeOnStartAndNtp; -import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; -import org.chromium.components.browser_ui.widget.displaystyle.DisplayStyleObserver; -import org.chromium.components.browser_ui.widget.displaystyle.UiConfig; -import org.chromium.components.omnibox.AutocompleteRequestType; -import org.chromium.components.omnibox.OmniboxFeatures; -import org.chromium.components.signin.SigninFeatureMap; -import org.chromium.components.signin.SigninFeatures; -import org.chromium.content_public.browser.LoadUrlParams; -import org.chromium.ui.base.ActivityResultTracker; -import org.chromium.ui.base.MimeTypeUtils; -import org.chromium.ui.base.WindowAndroid; -import org.chromium.ui.modaldialog.ModalDialogManager; -import org.chromium.ui.text.EmptyTextWatcher; -import org.chromium.url.GURL; - -import java.util.function.Supplier; /** * Layout for the new tab page. This positions the page elements in the correct vertical positions. @@ -85,103 +22,29 @@ */ @NullMarked public class NewTabPageLayout extends LinearLayout { + /** Delegate to handle layout-related events. */ + public interface Delegate { + /** + * Called during the {@link #onMeasure(int, int)} pass. + * + * @param width The measured width of the layout. + */ + void onMeasure(int width); + + /** Called when the layout is attached to a window. */ + void onAttachedToWindow(); + + /** Called when the layout's window visibility changes to {@link View#VISIBLE}. */ + void updateActionButtonVisibility(); + } + private static final String TAG = "NewTabPageLayout"; - private int mSearchBoxTwoSideMargin; - private final Context mContext; - private LogoCoordinator mLogoCoordinator; - private SearchBoxCoordinator mSearchBoxCoordinator; - private @Nullable MostVisitedTilesCoordinator mMostVisitedTilesCoordinator; - - private @Nullable OnSearchBoxScrollListener mSearchBoxScrollListener; - - private NewTabPageManager mManager; - private WindowAndroid mWindowAndroid; - private Activity mActivity; - private Profile mProfile; - private ActivityResultTracker mActivityResultTracker; - private BottomSheetController mBottomSheetController; - private ModalDialogManager mModalDialogManager; - private SnackbarManager mSnackbarManager; - private UiConfig mUiConfig; - private @Nullable DisplayStyleObserver mDisplayStyleObserver; - private CallbackController mCallbackController = new CallbackController(); - private SearchEngineUtils.@Nullable SearchEngineIconObserver mSearchEngineIconObserver; - private SearchEngineUtils.@Nullable SearchBoxHintTextObserver mSearchBoxHintTextObserver; - - /** - * Whether the tiles shown in the layout have finished loading. With {@link #mHasShownView}, - * it's one of the 2 flags used to track initialisation progress. - */ - private boolean mTilesLoaded; - - /** - * Whether the view has been shown at least once. - * With {@link #mTilesLoaded}, it's one of the 2 flags used to track initialization progress. - */ - private boolean mHasShownView; - - private boolean mSearchProviderHasLogo = true; - private boolean mSearchProviderIsGoogle; - private boolean mShowingNonStandardGoogleLogo; - - private boolean mInitialized; - - private float mUrlFocusChangePercent; - private boolean mDisableUrlFocusChangeAnimations; - - /** Flag used to request some layout changes after the next layout pass is completed. */ - private boolean mTileCountChanged; - - private boolean mSnapshotTileGridChanged; - - /** - * Vertical inset to add to the top and bottom of the search box bounds. May be 0 if no inset - * should be applied. See {@link Rect#inset(int, int)}. - */ - private int mSearchBoxBoundsVerticalInset; - - private FeedSurfaceScrollDelegate mScrollDelegate; - - private boolean mIsTablet; - private @Nullable Supplier<Integer> mTabStripHeightSupplier; - - private Callback<Logo> mOnLogoAvailableCallback; - - // mIsComposeplateEnabled is null before checking whether to initialize composeplate view in - // NewTabPageLayout#initialize(). - private @Nullable Boolean mIsComposeplateEnabled; - private boolean mIsComposeplatePolicyEnabled; - private boolean mIsComposeplateViewInitialized; - private Supplier<GURL> mComposeplateUrlSupplier; - private @Nullable ComposeplateCoordinator mComposeplateCoordinator; - // Previous visibility states for metrics. - private @Nullable Boolean mPreviousVoiceSearchButtonVisible; - private @Nullable Boolean mPreviousLensButtonVisible; - private SearchEngineUtils mSearchEngineUtils; - private final int mNtpSearchBoxTransitionStartOffset; - private final int mNtpSearchBoxTopMarginWithoutLogo; - private final boolean mEnableLogs; - private int mCurrentNtpFakeSearchBoxTransitionStartOffset; - private int mTopInset; - private @Nullable OnLayoutChangeListener mOnLayoutChangeListener; - // TODO(crbug.com/451602301): remove @Nullable and all null checks once - // ENABLE_SEAMLESS_SIGNIN is removed after the experiment. - private @Nullable NtpSigninPromoCoordinator mSigninPromoCoordinator; - - private @Nullable Boolean mIsWhiteBackgroundOnSearchBoxApplied; + private @Nullable Delegate mDelegate; /** Constructor for inflating from XML. */ public NewTabPageLayout(Context context, AttributeSet attrs) { super(context, attrs); - mContext = context; - Resources resources = getResources(); - mNtpSearchBoxTopMarginWithoutLogo = - resources.getDimensionPixelSize(R.dimen.mvt_container_top_margin); - mNtpSearchBoxTransitionStartOffset = - resources.getDimensionPixelSize(R.dimen.ntp_search_box_transition_start_offset); - - mEnableLogs = ChromeFeatureList.sNewTabPageCustomizationV2EnableLogs.getValue(); } @Override @@ -200,428 +63,31 @@ Log.i(TAG, "NewTabPageLayout.onFinishInflate after insertSiteSectionView"); } - /** - * Initializes the NewTabPageLayout. This must be called immediately after inflation, before - * this object is used in any other way. - * - * @param manager NewTabPageManager used to perform various actions when the user interacts with - * the page. - * @param activity The activity that currently owns the new tab page - * @param tileGroupDelegate Delegate for {@link TileGroup}. - * @param searchProviderHasLogo Whether the search provider has a logo. - * @param searchProviderIsGoogle Whether the search provider is Google. - * @param scrollDelegate The delegate used to obtain information about scroll state. - * @param touchEnabledDelegate The {@link TouchEnabledDelegate} for handling whether touch - * events are allowed. - * @param uiConfig UiConfig that provides display information about this view. - * @param lifecycleDispatcher Activity lifecycle dispatcher. - * @param profile The {@link Profile} associated with the NTP. - * @param windowAndroid An instance of a {@link WindowAndroid}. - * @param activityResultTracker Tracker of activity results. - * @param bottomSheetController Used to interact with the bottom sheet. - * @param modalDialogManagerSupplier Supplies the {@link ModalDialogManager}. - * @param snackbarManager Manages snackbars shown in the app. - * @param isTablet {@code true} if the NTP surface is in tablet mode. - * @param tabStripHeightSupplier Supplier of the tab strip height. - */ - @Initializer - public void initialize( - NewTabPageManager manager, - Activity activity, - Delegate tileGroupDelegate, - boolean searchProviderHasLogo, - boolean searchProviderIsGoogle, - FeedSurfaceScrollDelegate scrollDelegate, - TouchEnabledDelegate touchEnabledDelegate, - UiConfig uiConfig, - ActivityLifecycleDispatcher lifecycleDispatcher, - Profile profile, - WindowAndroid windowAndroid, - ActivityResultTracker activityResultTracker, - BottomSheetController bottomSheetController, - ModalDialogManager modalDialogManager, - SnackbarManager snackbarManager, - boolean isTablet, - Supplier<Integer> tabStripHeightSupplier, - Supplier<GURL> composeplateUrlSupplier) { - TraceEvent.begin(TAG + ".initialize()"); - mScrollDelegate = scrollDelegate; - mManager = manager; - mActivity = activity; - mProfile = profile; - mUiConfig = uiConfig; - mWindowAndroid = windowAndroid; - mActivityResultTracker = activityResultTracker; - mBottomSheetController = bottomSheetController; - mModalDialogManager = modalDialogManager; - mSnackbarManager = snackbarManager; - mIsTablet = isTablet; - mTabStripHeightSupplier = tabStripHeightSupplier; - mSearchEngineUtils = SearchEngineUtils.getForProfile(mProfile); - mComposeplateUrlSupplier = composeplateUrlSupplier; - - if (mIsTablet) { - mDisplayStyleObserver = this::onDisplayStyleChanged; - mUiConfig.addObserver(mDisplayStyleObserver); - } else { - // On first run, the NewTabPageLayout is initialized behind the First Run Experience, - // meaning the UiConfig will pickup the screen layout then. However - // onConfigurationChanged is not called on orientation changes until the FRE is - // completed. This means that if a user starts the FRE in one orientation, changes an - // orientation and then leaves the FRE the UiConfig will have the wrong orientation. - // https://crbug.com/683886. - mUiConfig.updateDisplayStyle(); + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mDelegate != null) { + int width = MeasureSpec.getSize(widthMeasureSpec); + mDelegate.onMeasure(width); } - mSearchBoxCoordinator = new SearchBoxCoordinator(getContext(), this, mIsTablet); - mSearchBoxCoordinator.initialize( - lifecycleDispatcher, mProfile.isOffTheRecord(), mWindowAndroid); - - updateSearchBoxTwoSideMargin(); - initializeLogoCoordinator(); - setSearchProviderInfo(searchProviderHasLogo, searchProviderIsGoogle); - initializeMostVisitedTilesCoordinator( - mProfile, lifecycleDispatcher, tileGroupDelegate, touchEnabledDelegate); - - mSearchEngineIconObserver = (newIcon) -> mSearchBoxCoordinator.setSearchEngineIcon(newIcon); - mSearchEngineUtils.addIconObserver(mSearchEngineIconObserver); - setSearchBoxTextAppearance(); - - initializeSearchBoxTextView(); - initializeVoiceSearchButton(); - initializeLensButton(); - - initializeComposeplateFlags(mProfile); - if (assumeNonNull(mIsComposeplateEnabled)) { - initializeComposeplate(); - } - - // This should be called after both mSearchBoxCoordinator and mComposeplateCoordinator are - // initialized. - onCustomizedBackgroundChanged( - NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox()); - - // This should called after flags of composeplate view are initialized. - setSearchBoxHeightBoundsVerticalInset(); - - updateActionButtonVisibility(); - initializeLayoutChangeListener(); - if (SigninFeatureMap.isEnabled(SigninFeatures.ENABLE_SEAMLESS_SIGNIN)) { - initializeSigninPromoCoordinator(); - } - - // Initialize Searchbox observers - mSearchBoxHintTextObserver = this::onSearchBoxHintTextChanged; - mSearchEngineUtils.addSearchBoxHintTextObserver(mSearchBoxHintTextObserver); - - manager.addDestructionObserver(NewTabPageLayout.this::onDestroy); - mInitialized = true; - - TraceEvent.end(TAG + ".initialize()"); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); } - /** Sets the height of the search box and mSearchBoxBoundsVerticalInset. */ - private void setSearchBoxHeightBoundsVerticalInset() { - int searchBoxHeight = - NtpCustomizationUtils.getSearchBoxHeightWithShadows( - getResources(), - assumeNonNull(mIsComposeplateEnabled), - Boolean.TRUE.equals(mIsWhiteBackgroundOnSearchBoxApplied)); - mSearchBoxCoordinator.setHeight(searchBoxHeight); - - mSearchBoxBoundsVerticalInset = - Math.round( - (searchBoxHeight - - getResources() - .getDimensionPixelSize( - R.dimen.toolbar_height_no_shadow)) - / 2f); - } - - public void reload() { - // TODO(crbug.com/41487877): Add handler in Magic Stack and dispatcher. - } - - public void enableSearchBoxEditText(boolean enable) { - mSearchBoxCoordinator.enableSearchBoxEditText(enable); - } - - /** - * @return The {@link FeedSurfaceScrollDelegate} for this class. - */ - FeedSurfaceScrollDelegate getScrollDelegate() { - return mScrollDelegate; - } - - /** Sets up the hint text and event handlers for the search box text view. */ - private void initializeSearchBoxTextView() { - TraceEvent.begin(TAG + ".initializeSearchBoxTextView()"); - - mSearchBoxCoordinator.setSearchBoxClickListener( - v -> mManager.focusSearchBox(false, AutocompleteRequestType.SEARCH, null)); - - // @TODO(crbug.com/41492572): Add test case for search box OnDragListener. - mSearchBoxCoordinator.setSearchBoxDragListener( - new OnDragListener() { - @Override - public boolean onDrag(View view, DragEvent dragEvent) { - // Disable search box EditText when browser content is dropped, its - // re-enabled in {@link ChromeTabbedOnDragListener}, since a disabled view - // will stop receiving further drag events. Given the child-first drag event - // dispatch, disabling the TextView at ACTION_DRAG_STARTED is necessary to - // prevent it from registering as a drop target and consuming the - // ACTION_DROP event, thereby ensuring {@link ChromeTabbedOnDragListener} - // receives it. - if (MimeTypeUtils.clipDescriptionHasBrowserContent( - dragEvent.getClipDescription()) - && dragEvent.getAction() == DragEvent.ACTION_DRAG_STARTED) { - enableSearchBoxEditText(false); - } - return false; - } - }); - - mSearchBoxCoordinator.setSearchBoxTextWatcher( - new EmptyTextWatcher() { - @Override - public void afterTextChanged(Editable s) { - if (s.length() == 0) return; - mManager.focusSearchBox( - false, AutocompleteRequestType.SEARCH, s.toString()); - mSearchBoxCoordinator.setSearchText(""); - } - }); - TraceEvent.end(TAG + ".initializeSearchBoxTextView()"); - } - - public void onSearchBoxHintTextChanged() { - mSearchBoxCoordinator.setSearchBoxHintText( - mSearchEngineUtils.getOmniboxHintText(AutocompleteRequestType.SEARCH)); - } - - private void setSearchBoxTextAppearance() { - boolean shouldApplyWhiteBackground = - NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox(); - - if (shouldApplyWhiteBackground) { - mSearchBoxCoordinator.setSearchBoxTextAppearance( - R.style.TextAppearance_FakeSearchBoxTextMediumDark); - } else { - mSearchBoxCoordinator.setSearchBoxTextAppearance( - R.style.TextAppearance_FakeSearchBoxTextMedium); + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mDelegate != null) { + mDelegate.onAttachedToWindow(); } } - private void initializeVoiceSearchButton() { - TraceEvent.begin(TAG + ".initializeVoiceSearchButton()"); - OnClickListener voiceSearchButtonClickListener = - v -> mManager.focusSearchBox(true, AutocompleteRequestType.SEARCH, null); - mSearchBoxCoordinator.addVoiceSearchButtonClickListener(voiceSearchButtonClickListener); - TraceEvent.end(TAG + ".initializeVoiceSearchButton()"); - } + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); - private void initializeLensButton() { - TraceEvent.begin(TAG + ".initializeLensButton()"); - OnClickListener lensButtonClickListener = - v -> { - LensMetrics.recordClicked(LensEntryPoint.NEW_TAB_PAGE); - mSearchBoxCoordinator.startLens(LensEntryPoint.NEW_TAB_PAGE); - }; - mSearchBoxCoordinator.addLensButtonClickListener(lensButtonClickListener); - TraceEvent.end(TAG + ".initializeLensButton()"); - } - - private void initializeComposeplateFlags(Profile profile) { - mIsComposeplateEnabled = ComposeplateUtils.isComposeplateEnabled(mIsTablet, profile); - mIsComposeplatePolicyEnabled = - mIsComposeplateEnabled && ComposeplateUtils.isEnabledByPolicy(profile); - } - - private void initializeComposeplate() { - if (mIsComposeplateViewInitialized) return; - - mIsComposeplateViewInitialized = true; - - boolean shouldApplyWhiteBackgroundOnSearchBox = - NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox(); - - ViewStub composeplateViewStub = findViewById(R.id.composeplate_view_v2_stub); - ViewGroup composeplateView = (ViewGroup) composeplateViewStub.inflate(); - if (NtpCustomizationUtils.isNtpThemeCustomizationEnabled()) { - // TODO(https://crbug.com/423579377): Moves the layout parameters to - // composeplate_view_layout_v2.xml after the feature NewTabPageCustomizationV2 is - // launched. - NewTabPageUtils.applyUpdatedLayoutParamsForComposeplateView(composeplateView); + if (visibility == VISIBLE && mDelegate != null) { + mDelegate.updateActionButtonVisibility(); } - mComposeplateCoordinator = new ComposeplateCoordinator(composeplateView, mProfile); - mComposeplateCoordinator.setIncognitoClickListener(this::onIncognitoButtonClicked); - // Don't log click metrics in this listener, since the mComposeplateCoordinator will - // log. - mComposeplateCoordinator.setComposeplateButtonClickListener( - this::onComposeplateButtonClicked); - - if (shouldApplyWhiteBackgroundOnSearchBox) { - // It is safe to call mComposeplateCoordinator.applyWhiteBackgroundWithShadow() again - // since it is no-op if the white background has been applied. - mComposeplateCoordinator.applyWhiteBackgroundWithShadow(/* apply= */ true); - } - } - - private void onComposeplateButtonClicked(View view) { - if (OmniboxFeatures.sOmniboxMultimodalInput.isEnabled() - && OmniboxFeatures.sRedirectComposeplateButton.getValue() - && !mIsTablet - && mIsComposeplatePolicyEnabled) { - mManager.focusSearchBox(false, AutocompleteRequestType.AI_MODE, null); - return; - } - - GURL composeplateUrl = mComposeplateUrlSupplier.get(); - if (composeplateUrl == null) return; - - mManager.getNativePageHost() - .loadUrl(new LoadUrlParams(composeplateUrl), /* incognito= */ false); - } - - private void onIncognitoButtonClicked(View view) { - if (!IncognitoUtils.isIncognitoModeEnabled(mProfile)) return; - - UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile); - mManager.getNativePageHost().loadUrl(new LoadUrlParams(resolver.getNtpUrl()), true); - } - - private void initializeLayoutChangeListener() { - TraceEvent.begin(TAG + ".initializeLayoutChangeListener()"); - mOnLayoutChangeListener = this::onLayoutChanged; - addOnLayoutChangeListener(mOnLayoutChangeListener); - TraceEvent.end(TAG + ".initializeLayoutChangeListener()"); - } - - private void onLayoutChanged( - View view, - int left, - int top, - int right, - int bottom, - int oldLeft, - int oldTop, - int oldRight, - int oldBottom) { - int oldHeight = oldBottom - oldTop; - int newHeight = bottom - top; - - if (oldHeight == newHeight && !mTileCountChanged) return; - mTileCountChanged = false; - - // Re-apply the url focus change amount after a rotation to ensure the views are - // correctly placed with their new layout configurations. - onUrlFocusAnimationChanged(); - updateSearchBoxOnScroll(); - - // The positioning of elements may have been changed (since the elements expand - // to fill the available vertical space), so adjust the scroll. - if (mScrollDelegate.isScrollViewInitialized()) mScrollDelegate.snapScroll(); - } - - private void initializeLogoCoordinator() { - Callback<LoadUrlParams> logoClickedCallback = - mCallbackController.makeCancelable( - (urlParams) -> { - mManager.getNativePageHost().loadUrl(urlParams, /* incognito= */ false); - BrowserUiUtils.recordModuleClickHistogram( - ModuleTypeOnStartAndNtp.DOODLE); - }); - mOnLogoAvailableCallback = - mCallbackController.makeCancelable( - (logo) -> { - mSnapshotTileGridChanged = true; - mShowingNonStandardGoogleLogo = logo != null && mSearchProviderIsGoogle; - NtpCustomizationConfigManager.getInstance() - .setDefaultSearchEngineLogoBitmap( - logo == null ? null : logo.image); - }); - - mLogoCoordinator = - new LogoCoordinator( - mContext, - logoClickedCallback, - /* parentView= */ this, - mOnLogoAvailableCallback, - /* visibilityObserver= */ null, - () -> MultiWindowUtils.getInstance().isInMultiWindowMode(mActivity)); - mLogoCoordinator.initWithNative(mProfile); - } - - private void initializeMostVisitedTilesCoordinator( - Profile profile, - ActivityLifecycleDispatcher activityLifecycleDispatcher, - TileGroup.Delegate tileGroupDelegate, - TouchEnabledDelegate touchEnabledDelegate) { - View mvTilesContainerLayout = findViewById(R.id.mv_tiles_container); - assert mvTilesContainerLayout != null; - - mMostVisitedTilesCoordinator = - new MostVisitedTilesCoordinator( - mActivity, - activityLifecycleDispatcher, - mvTilesContainerLayout, - () -> mSnapshotTileGridChanged = true, - () -> { - if (mUrlFocusChangePercent == 1f) mTileCountChanged = true; - }); - - mMostVisitedTilesCoordinator.initWithNative( - profile, mManager, tileGroupDelegate, touchEnabledDelegate); - - if (ChromeFeatureList.sNewTabPageCustomizationForMvt.isEnabled()) { - mMostVisitedTilesCoordinator.updateMvtVisibility(); - } - } - - private void initializeSigninPromoCoordinator() { - ViewStub signinPromoViewContainerStub = findViewById(R.id.signin_promo_view_container_stub); - mSigninPromoCoordinator = - new NtpSigninPromoCoordinator( - mWindowAndroid, - mActivity, - mProfile, - mActivityResultTracker, - SigninAndHistorySyncActivityLauncherImpl.get(), - mBottomSheetController, - mModalDialogManager, - mSnackbarManager, - DeviceLockActivityLauncherImpl.get(), - signinPromoViewContainerStub, - SetupListModuleUtils::isSetupListActive); - } - - /** Updates the search box when the parent view's scroll position is changed. */ - void updateSearchBoxOnScroll() { - if (mDisableUrlFocusChangeAnimations) return; - - // When the page changes (tab switching or new page loading), it is possible that events - // (e.g. delayed view change notifications) trigger calls to these methods after - // the current page changes. We check it again to make sure we don't attempt to update the - // wrong page. - if (!mManager.isCurrentPage()) return; - - if (mSearchBoxScrollListener != null) { - mSearchBoxScrollListener.onNtpScrollChanged(getToolbarTransitionPercentage()); - } - } - - /** - * Calculates the percentage (between 0 and 1) of the transition from the search box to the - * omnibox at the top of the New Tab Page, which is determined by the amount of scrolling and - * the position of the search box. - * - * @return the transition percentage - */ - float getToolbarTransitionPercentage() { - return mSearchBoxCoordinator.getToolbarTransitionPercentage( - mScrollDelegate, - mTabStripHeightSupplier, - mCurrentNtpFakeSearchBoxTransitionStartOffset); } private void initializeSiteSectionView() { @@ -634,582 +100,8 @@ if (getVisibility() != View.VISIBLE) setVisibility(View.VISIBLE); } - /** - * @return The fake search box view. - */ - public View getSearchBoxView() { - return mSearchBoxCoordinator.getView(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int width = MeasureSpec.getSize(widthMeasureSpec); - if (mIsTablet && mMostVisitedTilesCoordinator != null) { - mMostVisitedTilesCoordinator.calculateTabletMvtWidth(width); - } - - unifyElementWidths(width); - - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - public void onSwitchToForeground() { - if (mMostVisitedTilesCoordinator != null) { - mMostVisitedTilesCoordinator.onSwitchToForeground(); - } - } - - /** - * Should be called every time one of the flags used to track initialization progress changes. - * Finalizes initialization once all the preliminary steps are complete. - * - * @see #mHasShownView - * @see #mTilesLoaded - */ - private void onInitializationProgressChanged() { - if (!hasLoadCompleted()) return; - - mManager.onLoadingComplete(); - - // Load the logo after everything else is finished, since it's lower priority. - if (mLogoCoordinator != null) mLogoCoordinator.loadSearchProviderLogoWithAnimation(); - } - - /** - * To be called to notify that the tiles have finished loading. Will do nothing if a load was - * previously completed. - */ - public void onTilesLoaded() { - if (mTilesLoaded) return; - mTilesLoaded = true; - - onInitializationProgressChanged(); - } - - /** - * Changes the layout depending on whether the selected search provider (e.g. Google, Bing) has - * a logo. - * - * @param hasLogo Whether the search provider has a logo. - * @param isGoogle Whether the search provider is Google. - */ - void setSearchProviderInfo(boolean hasLogo, boolean isGoogle) { - if (hasLogo == mSearchProviderHasLogo - && isGoogle == mSearchProviderIsGoogle - && mInitialized) { - return; - } - boolean isSearchProviderIsGoogleChanged = mSearchProviderIsGoogle != isGoogle; - mSearchProviderHasLogo = hasLogo; - mSearchProviderIsGoogle = isGoogle; - - if (!mSearchProviderIsGoogle) { - mShowingNonStandardGoogleLogo = false; - } - - setSearchProviderTopMargin(); - setLogoViewBottomMargin(); - - updateTilesLayoutMargins(); - - // Hide or show the views above the most visited tiles as needed, e.g, spacers. The - // visibility of Logo is handled by LogoCoordinator. - setSearchBoxTextAppearance(); - - // Skips if the flag hasn't been initialized since the initialization of the following - // components will be called again in #initialize(). - if (mIsComposeplateEnabled != null) { - // when mSearchProviderIsGoogle is changed, mIsComposeplateEnabled might be changed too, - // recalculate its value. - if (isSearchProviderIsGoogleChanged) { - boolean previousIsComposeplateEnabled = mIsComposeplateEnabled; - initializeComposeplateFlags(mProfile); - if (!previousIsComposeplateEnabled - && mIsComposeplateEnabled - && mComposeplateCoordinator == null) { - // If the composeplate view is enabled while mComposeplateCoordinator hasn't - // been initialized yet, initialize it now. - initializeComposeplate(); - } - - if (previousIsComposeplateEnabled != mIsComposeplateEnabled) { - // When the flag value is changed, the height of search box might be changed. - setSearchBoxHeightBoundsVerticalInset(); - // Updates the composeplate view's visibility. - updateActionButtonVisibility(); - } - } - } - - onUrlFocusAnimationChanged(); - - mSnapshotTileGridChanged = true; - } - - /** Updates the margins for the most visited tiles layout based on what is shown above it. */ - private void updateTilesLayoutMargins() { - if (mMostVisitedTilesCoordinator == null) return; - - mMostVisitedTilesCoordinator.updateTilesLayoutMargins( - shouldShowLogo(), - mIsWhiteBackgroundOnSearchBoxApplied == null - ? false - : mIsWhiteBackgroundOnSearchBoxApplied, - mIsTablet); - } - - /** - * Updates whether the NewTabPage should animate on URL focus changes. - * - * @param disable Whether to disable the animations. - */ - void setUrlFocusAnimationsDisabled(boolean disable) { - if (disable == mDisableUrlFocusChangeAnimations) return; - mDisableUrlFocusChangeAnimations = disable; - if (!disable) onUrlFocusAnimationChanged(); - } - - /** - * @return Whether URL focus animations are currently disabled. - */ - boolean urlFocusAnimationsDisabled() { - return mDisableUrlFocusChangeAnimations; - } - - /** - * Specifies the percentage the URL is focused during an animation. 1.0 specifies that the URL - * bar has focus and has completed the focus animation. 0 is when the URL bar is does not have - * any focus. - * - * @param percent The percentage of the URL bar focus animation. - */ - void setUrlFocusChangeAnimationPercent(float percent) { - mUrlFocusChangePercent = percent; - onUrlFocusAnimationChanged(); - } - - /** - * @return The percentage that the URL bar is focused during an animation. - */ - @VisibleForTesting - float getUrlFocusChangeAnimationPercent() { - return mUrlFocusChangePercent; - } - - void onUrlFocusAnimationChanged() { - /* - * Avoid Y-translation when animation is disabled, view is moving or on tablet form-factor. - * Context for tablets - Unlike phones, this method is not called on tablets during URL - * focus post NTP load. However when physical keyboard is present, we try to auto-focus URL - * during page load causing this method to be called. Disabling this for all cases on this - * form-factor since this translation does not WAI. (see crbug.com/40910640) - */ - if (mDisableUrlFocusChangeAnimations || mIsTablet) return; - - // Translate so that the search box is at the top, but only upwards. - int basePosition = mScrollDelegate.getVerticalScrollOffset() + getPaddingTop(); - int target = - Math.max( - basePosition, - getSearchBoxView().getBottom() - - getSearchBoxView().getPaddingBottom() - - mSearchBoxBoundsVerticalInset); - - float translationY = mUrlFocusChangePercent * (basePosition - target); - setTranslationYOfFakeboxAndAbove(translationY); - } - - /** - * Sets the translation_y of the fakebox and all views above it, but not the views below. Used - * when the url focus animation is combined with the omnibox suggestions list animation to - * reduce the number of visual elements in motion. - */ - private void setTranslationYOfFakeboxAndAbove(float translationY) { - for (int i = 0; i < getChildCount(); i++) { - View view = getChildAt(i); - view.setTranslationY(translationY); - if (view.getId() == R.id.search_box) return; - } - } - - /** - * Updates the opacity of the search box when scrolling. - * - * @param alpha opacity (alpha) value to use. - */ - public void setSearchBoxAlpha(float alpha) { - mSearchBoxCoordinator.setAlpha(alpha); - } - - /** - * Updates the opacity of the search provider logo when scrolling. - * - * @param alpha opacity (alpha) value to use. - */ - public void setSearchProviderLogoAlpha(float alpha) { - if (mLogoCoordinator != null) mLogoCoordinator.setAlpha(alpha); - } - - /** - * Get the bounds of the search box in relation to the top level {@code parentView}. - * - * @param bounds The current drawing location of the search box. - * @param translation The translation applied to the search box by the parent view hierarchy up - * to the {@code parentView}. - * @param parentView The top level parent view used to translate search box bounds. - */ - void getSearchBoxBounds(Rect bounds, Point translation, View parentView) { - mSearchBoxCoordinator.getSearchBoxBounds( - bounds, translation, parentView, mScrollDelegate, mSearchBoxBoundsVerticalInset); - } - - /** Returns the fake search box's transition start offset on NTP. */ - private int getNtpSearchBoxTransitionStartOffset(boolean showFakeSearchBoxWithoutLogo) { - if (mIsTablet && showFakeSearchBoxWithoutLogo) { - // On tablets, it is possible to show fake search box if DSE doesn't have logo if DSE - // mobile parity v2 is enabled. The mNTPFakeSearchBoxTransitionStartOffset is used to - // calculate scrolling percentage in getToolbarTransitionPercentage(). Reset to 0 when - // no doodle is shown for 3p DSE to prevent the alpha of fake search box being set to 0 - // (transparent) by ToolbarTablet#updateNtp(). - return 0; - } else { - return mNtpSearchBoxTransitionStartOffset; - } - } - - private void setSearchProviderTopMargin() { - boolean showFakeSearchBoxWithoutLogo = !mSearchProviderHasLogo; - mCurrentNtpFakeSearchBoxTransitionStartOffset = - getNtpSearchBoxTransitionStartOffset(showFakeSearchBoxWithoutLogo); - - int topMargin = showFakeSearchBoxWithoutLogo ? mNtpSearchBoxTopMarginWithoutLogo : 0; - mSearchBoxCoordinator.setTopMargin(topMargin); - - if (mLogoCoordinator != null) { - mLogoCoordinator.setTopMargin(getLogoTopMargin()); - } - } - - private void setLogoViewBottomMargin() { - if (mLogoCoordinator == null) return; - - boolean shouldShowShadow = Boolean.TRUE.equals(mIsWhiteBackgroundOnSearchBoxApplied); - int logoViewBottomMarginPx = - NtpCustomizationUtils.getLogoViewBottomMarginPx(getResources(), shouldShowShadow); - mLogoCoordinator.setBottomMargin(logoViewBottomMarginPx); - } - - private int getLogoTopMargin() { - Resources resources = getResources(); - - if (mShowingNonStandardGoogleLogo && mSearchProviderHasLogo) { - return LogoUtils.getTopMarginForDoodle(resources); - } - - return resources.getDimensionPixelSize(R.dimen.ntp_logo_margin_top); - } - - /** - * Sets the listener for search box scroll changes. - * - * @param listener The listener to be notified on changes. - */ - void setSearchBoxScrollListener(@Nullable OnSearchBoxScrollListener listener) { - mSearchBoxScrollListener = listener; - if (mSearchBoxScrollListener != null) updateSearchBoxOnScroll(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - assert mManager != null; - - if (!mHasShownView) { - mHasShownView = true; - onInitializationProgressChanged(); - TraceEvent.instant("NewTabPageSearchAvailable"); - } - } - - /** Update the visibility of the action buttons. */ - void updateActionButtonVisibility() { - boolean shouldShowVoiceSearchButton = mManager.isVoiceSearchEnabled(); - boolean shouldShowLensButton = - mSearchBoxCoordinator.isLensEnabled(LensEntryPoint.NEW_TAB_PAGE); - if (mIsComposeplateEnabled == null) return; - - mSearchBoxCoordinator.setVoiceSearchButtonVisibility(shouldShowVoiceSearchButton); - mSearchBoxCoordinator.setLensButtonVisibility(shouldShowLensButton); - boolean shouldShowComposeplateButton = false; - // As long as mComposeplateCoordinator has been initialized, we should update its - // visibility. - if (mComposeplateCoordinator != null) { - shouldShowComposeplateButton = - mIsComposeplateEnabled - && mSearchProviderIsGoogle - && IncognitoUtils.isIncognitoModeEnabled(mProfile); - mComposeplateCoordinator.setVisibility( - shouldShowComposeplateButton, mManager.isCurrentPage()); - } - updatePreviousButtonVisibilityAndRecordMetrics( - shouldShowVoiceSearchButton, shouldShowLensButton, shouldShowComposeplateButton); - } - - /** - * Updates the previous visibility state of the voice search and lens buttons and records - * metrics if their visibility has changed on the current page. - * - * @param isVoiceSearchButtonVisible The new visibility state of the voice search button. - * @param isLensButtonVisible The new visibility state of the lens button. - * @param isComposeplateButtonVisible The new visibility state of the composeplate button. - */ - private void updatePreviousButtonVisibilityAndRecordMetrics( - boolean isVoiceSearchButtonVisible, - boolean isLensButtonVisible, - boolean isComposeplateButtonVisible) { - if (!mManager.isCurrentPage() - || (mPreviousVoiceSearchButtonVisible != null - && isVoiceSearchButtonVisible == mPreviousVoiceSearchButtonVisible - && mPreviousLensButtonVisible != null - && isLensButtonVisible == mPreviousLensButtonVisible)) { - return; - } - - if (mPreviousLensButtonVisible == null - || isLensButtonVisible != mPreviousLensButtonVisible) { - LensMetrics.recordShown(LensEntryPoint.NEW_TAB_PAGE, isLensButtonVisible); - } - - ComposeplateMetricsUtils.recordFakeSearchBoxImpression2(); - ComposeplateMetricsUtils.recordFakeSearchBoxComposeplateButtonImpression2( - isComposeplateButtonVisible); - - mPreviousVoiceSearchButtonVisible = isVoiceSearchButtonVisible; - mPreviousLensButtonVisible = isLensButtonVisible; - } - - @Override - protected void onWindowVisibilityChanged(int visibility) { - super.onWindowVisibilityChanged(visibility); - - if (visibility == VISIBLE) { - updateActionButtonVisibility(); - } - } - - /** - * @see InvalidationAwareThumbnailProvider#shouldCaptureThumbnail() - */ - public boolean shouldCaptureThumbnail() { - return mSnapshotTileGridChanged; - } - - /** - * Should be called before a thumbnail of the parent view is captured. - * - * @see InvalidationAwareThumbnailProvider#captureThumbnail(Canvas) - */ - public void onPreCaptureThumbnail() { - if (mLogoCoordinator != null) mLogoCoordinator.endFadeAnimation(); - mSnapshotTileGridChanged = false; - } - - private boolean shouldShowLogo() { - return mSearchProviderHasLogo; - } - - private boolean hasLoadCompleted() { - return mHasShownView && mTilesLoaded; - } - - @SuppressWarnings("NullAway") - private void onDestroy() { - if (mCallbackController != null) { - mCallbackController.destroy(); - mCallbackController = null; - } - - if (mLogoCoordinator != null) { - mLogoCoordinator.destroy(); - mLogoCoordinator = null; - } - - mSearchBoxCoordinator.destroy(); - mSearchBoxCoordinator = null; - - if (mMostVisitedTilesCoordinator != null) { - mMostVisitedTilesCoordinator.destroyMvtiles(); - mMostVisitedTilesCoordinator = null; - } - - if (mIsTablet) { - mUiConfig.removeObserver(mDisplayStyleObserver); - mDisplayStyleObserver = null; - } - - if (mSearchEngineUtils != null) { - if (mSearchBoxHintTextObserver != null) { - mSearchEngineUtils.removeSearchBoxHintTextObserver(mSearchBoxHintTextObserver); - mSearchBoxHintTextObserver = null; - } - if (mSearchEngineIconObserver != null) { - mSearchEngineUtils.removeIconObserver(mSearchEngineIconObserver); - mSearchEngineIconObserver = null; - } - mSearchEngineUtils = null; - } - - removeOnLayoutChangeListener(mOnLayoutChangeListener); - mOnLayoutChangeListener = null; - - if (mComposeplateCoordinator != null) { - mComposeplateCoordinator.destroy(); - mComposeplateCoordinator = null; - } - - if (mSigninPromoCoordinator != null) { - mSigninPromoCoordinator.destroy(); - mSigninPromoCoordinator = null; - } - - mSearchBoxScrollListener = null; - mComposeplateUrlSupplier = null; - } - - /** Makes the Search Box and Logo as wide as Most Visited. */ - private void unifyElementWidths(int width) { - int searchBoxWidth = width - mSearchBoxTwoSideMargin; - if (mSearchBoxCoordinator != null) { - mSearchBoxCoordinator.setLayoutWidth(searchBoxWidth); - } - - if (mLogoCoordinator != null) { - mLogoCoordinator.setLayoutWidth(width); - } - - if (mComposeplateCoordinator != null) { - mComposeplateCoordinator.setLayoutWidth(searchBoxWidth); - } - } - - LogoCoordinator getLogoCoordinatorForTesting() { - return mLogoCoordinator; - } - - private void onDisplayStyleChanged(UiConfig.DisplayStyle newDisplayStyle) { - if (!mIsTablet) return; - - updateDoodleOnTablet(); - if (mMostVisitedTilesCoordinator != null) { - mMostVisitedTilesCoordinator.updateMvtOnTablet(); - } - updateSearchBoxTwoSideMargin(); - } - - /** - * Adjusts the doodle size while the tablet transitions to or from a multi-screen layout, - * ensuring the change occurs post-logo initialization. - */ - private void updateDoodleOnTablet() { - if (!mIsTablet || mLogoCoordinator == null) return; - - mLogoCoordinator.updateDoodleOnTablet(mShowingNonStandardGoogleLogo); - } - - private void updateSearchBoxTwoSideMargin() { - mSearchBoxTwoSideMargin = - NtpCustomizationUtils.getSearchBoxTwoSideMargin( - getResources(), mUiConfig, mIsTablet); - } - - /** - * Called when the layout changes between edge-to-edge and standard. - * - * @param systemTopInset The system's top inset, i.e., the height of the Status bar. It is - * always bigger than 0. - * @param supportsEdgeToEdgeOnTop Determines if the NTP should consume this top inset, extending - * itself to the Status bar area. - */ - void onToEdgeChange(int systemTopInset, boolean supportsEdgeToEdgeOnTop) { - // Exits early if the NTP's top padding doesn't require adjustment. - if (NtpCustomizationUtils.shouldSkipTopInsetsChange( - mTopInset, systemTopInset, supportsEdgeToEdgeOnTop)) { - return; - } - - mTopInset = supportsEdgeToEdgeOnTop ? systemTopInset : 0; - mCurrentNtpFakeSearchBoxTransitionStartOffset = - getNtpSearchBoxTransitionStartOffset(!mSearchProviderHasLogo) + mTopInset; - - // Top padding is applied to the NTP layout, ensuring all UI components remain in their - // original positions after Status bar is hidden. - setPaddingRelative( - getPaddingStart(), - getResources().getDimensionPixelSize(R.dimen.toolbar_height_no_shadow) + mTopInset, - getPaddingEnd(), - getPaddingBottom()); - - if (mEnableLogs) { - Log.i(TAG, "The top padding to add on the NTP is %d.", mTopInset); - } - } - - /** - * Called when a customized background image is selected or deselected. - * - * @param applyWhiteBackgroundOnSearchBox Whether to apply a white background color to the fake - * search box. - */ - void onCustomizedBackgroundChanged(boolean applyWhiteBackgroundOnSearchBox) { - // If shouldn't apply a white background and the background hasn't been updated before, - // returns now. - if (mIsWhiteBackgroundOnSearchBoxApplied == null && !applyWhiteBackgroundOnSearchBox) { - return; - } - - // If composeplate view flags haven't been initialized yet, returns now. - if (mIsComposeplateEnabled == null) { - return; - } - - // If the background has been updated before and it should remain the same, returns now. - if (mIsWhiteBackgroundOnSearchBoxApplied != null - && mIsWhiteBackgroundOnSearchBoxApplied == applyWhiteBackgroundOnSearchBox) { - return; - } - - // If the fake search box hasn't been initialized, returns now. It is fine to skip here - // because applyWhiteBackgroundWithShadow() will be called immediately after the - // mSearchBoxCoordinator is initialized. - if (mSearchBoxCoordinator == null) return; - - mIsWhiteBackgroundOnSearchBoxApplied = applyWhiteBackgroundOnSearchBox; - - if (mSearchBoxCoordinator != null) { - mSearchBoxCoordinator.applyWhiteBackgroundWithShadow(applyWhiteBackgroundOnSearchBox); - } - - if (mComposeplateCoordinator != null) { - mComposeplateCoordinator.applyWhiteBackgroundWithShadow( - applyWhiteBackgroundOnSearchBox); - } - - setLogoViewBottomMargin(); - // Update the search box height and bounds vertical inset since the shadow padding changed. - setSearchBoxHeightBoundsVerticalInset(); - - if (mMostVisitedTilesCoordinator != null) { - updateTilesLayoutMargins(); - } - } - - /** Returns the top inset of the NTP. */ - int getTopInset() { - return mTopInset; - } - - /** Returns the vertical inset applied to search box bounds. */ - int getSearchBoxBoundsVerticalInset() { - return mSearchBoxBoundsVerticalInset; + /** Sets the delegate instance. */ + void setDelegate(Delegate delegate) { + mDelegate = delegate; } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/SnapScrollHelperImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/SnapScrollHelperImpl.java index 493d5d46..a92c8f8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/SnapScrollHelperImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/SnapScrollHelperImpl.java
@@ -25,7 +25,7 @@ private static final long SNAP_SCROLL_DELAY_MS = 30; private final NewTabPageManager mManager; - private final NewTabPageLayout mNewTabPageLayout; + private final NewTabPageCoordinator mNewTabPageCoordinator; private final Runnable mSnapScrollRunnable; private final Runnable mUpdateSearchBoxOnScrollRunnable; private final int mToolbarHeight; @@ -38,19 +38,23 @@ /** * @param manager The {@link NewTabPageManager} to get information about user interactions on - * the {@link NewTabPage}. - * @param newTabPageLayout The {@link NewTabPageLayout} associated with the {@link NewTabPage}. + * the {@link NewTabPage}. + * @param newTabPageCoordinator The {@link NewTabPageCoordinator} associated with the {@link + * NewTabPage}. */ - public SnapScrollHelperImpl(NewTabPageManager manager, NewTabPageLayout newTabPageLayout) { + public SnapScrollHelperImpl( + NewTabPageManager manager, NewTabPageCoordinator newTabPageCoordinator) { mManager = manager; - mNewTabPageLayout = newTabPageLayout; + mNewTabPageCoordinator = newTabPageCoordinator; mSnapScrollRunnable = new SnapScrollRunnable(); - mUpdateSearchBoxOnScrollRunnable = mNewTabPageLayout::updateSearchBoxOnScroll; + mUpdateSearchBoxOnScrollRunnable = mNewTabPageCoordinator::updateSearchBoxOnScroll; - Resources res = newTabPageLayout.getResources(); + Resources res = newTabPageCoordinator.getNewTabPageLayout().getResources(); if (ChromeFeatureList.sAndroidProgressBarVisualUpdate.isEnabled()) { - mToolbarHeight = res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow) - + res.getDimensionPixelSize(R.dimen.toolbar_progress_bar_increased_height); + mToolbarHeight = + res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow) + + res.getDimensionPixelSize( + R.dimen.toolbar_progress_bar_increased_height); } else { mToolbarHeight = res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow) @@ -97,7 +101,7 @@ /** Update scroll offset and perform snap scroll if necessary. */ @Override public void handleScroll() { - int scrollY = mNewTabPageLayout.getScrollDelegate().getVerticalScrollOffset(); + int scrollY = mNewTabPageCoordinator.getScrollDelegate().getVerticalScrollOffset(); if (mLastScrollY == scrollY) return; mLastScrollY = scrollY; @@ -106,7 +110,7 @@ mView.removeCallbacks(mSnapScrollRunnable); mView.postDelayed(mSnapScrollRunnable, SNAP_SCROLL_DELAY_MS); } - mNewTabPageLayout.updateSearchBoxOnScroll(); + mNewTabPageCoordinator.updateSearchBoxOnScroll(); } /** @@ -136,7 +140,7 @@ scrollPosition = calculateSnapPositionForRegion(scrollPosition, 0, mToolbarHeight); // Snap scroll to prevent resting in the middle of the omnibox transition. - View fakeBox = mNewTabPageLayout.getSearchBoxView(); + View fakeBox = mNewTabPageCoordinator.getSearchBoxView(); int fakeBoxUpperBound = fakeBox.getTop() + fakeBox.getPaddingTop(); scrollPosition = calculateSnapPositionForRegion( @@ -187,7 +191,7 @@ assert mPendingSnapScroll; mPendingSnapScroll = false; - mNewTabPageLayout.getScrollDelegate().snapScroll(); + mNewTabPageCoordinator.getScrollDelegate().snapScroll(); } } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java index 4116447..82858c0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java
@@ -88,7 +88,7 @@ new MostVisitedTilesMediator( activity, mUiConfig, - tilesLayout, + mvTilesContainerLayout, mRenderer, propertyModel, isTablet,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java index fb71b629..50c7a3d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java
@@ -12,6 +12,7 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; @@ -50,6 +51,7 @@ private final Context mContext; private final Resources mResources; private final UiConfig mUiConfig; + private final View mMvTilesContainerLayout; private final MostVisitedTilesLayout mMvTilesLayout; private final PropertyModel mModel; private final boolean mIsTablet; @@ -74,7 +76,7 @@ public MostVisitedTilesMediator( Context context, UiConfig uiConfig, - MostVisitedTilesLayout mvTilesLayout, + View mvTilesContainerLayout, TileRenderer renderer, PropertyModel propertyModel, boolean isTablet, @@ -88,7 +90,8 @@ mIsTablet = isTablet; mSnapshotTileGridChangedRunnable = snapshotTileGridChangedRunnable; mTileCountChangedRunnable = tileCountChangedRunnable; - mMvTilesLayout = mvTilesLayout; + mMvTilesContainerLayout = mvTilesContainerLayout; + mMvTilesLayout = mvTilesContainerLayout.findViewById(R.id.mv_tiles_layout); mTileViewLandscapePadding = mResources.getDimensionPixelSize(R.dimen.tile_view_padding_landscape); @@ -266,7 +269,7 @@ } MarginLayoutParams marginLayoutParams = - (MarginLayoutParams) mMvTilesLayout.getLayoutParams(); + (MarginLayoutParams) mMvTilesContainerLayout.getLayoutParams(); marginLayoutParams.width = mMvtContentFits ? ViewGroup.LayoutParams.WRAP_CONTENT @@ -292,7 +295,10 @@ void updateTilesLayoutMargins( boolean shouldShowLogo, boolean isWhiteBackgroundOnSearchBoxApplied, boolean isTablet) { NewTabPageUtils.updateTilesLayoutTopMargin( - mMvTilesLayout, shouldShowLogo, isWhiteBackgroundOnSearchBoxApplied, isTablet); + mMvTilesContainerLayout, + shouldShowLogo, + isWhiteBackgroundOnSearchBoxApplied, + isTablet); } public void onSwitchToForeground() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java index bc02833..111d5a7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -1807,16 +1807,18 @@ || (getNewTabPageForCurrentTab() != null); } - private void back(int metaState) { + private void back(int metaState, int buttonState) { setUrlBarFocus(false, OmniboxFocusReason.UNFOCUS); boolean hasControl = (metaState & KeyEvent.META_CTRL_ON) != 0; boolean hasShift = (metaState & KeyEvent.META_SHIFT_ON) != 0; - if (hasControl && hasShift) { + boolean isMiddleClick = (buttonState & MotionEvent.BUTTON_TERTIARY) != 0; + + if ((hasControl && hasShift) || (isMiddleClick && hasShift)) { // Holding ALT is allowed as well (reference desktop behavior). final boolean isSuccess = mToolbarTabController.backInNewTab(/* foregroundNewTab= */ true); if (isSuccess) RecordUserAction.record("MobileToolbarBackInNewForegroundTab"); - } else if (hasControl) { + } else if (hasControl || isMiddleClick) { final boolean isSuccess = mToolbarTabController.backInNewTab(/* foregroundNewTab= */ false); if (isSuccess) RecordUserAction.record("MobileToolbarBackInNewBackgroundTab");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java index 0fe2017..b22bfda8 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -460,25 +460,27 @@ new Runnable() { @Override public void run() { - NewTabPageLayout ntpLayout = mNtp.getNewTabPageLayout(); + NewTabPageCoordinator ntpCoordinator = mNtp.getNewTabPageCoordinator(); View logoView = mNtp.getLayout().findViewById(R.id.search_provider_logo); Assert.assertEquals(View.VISIBLE, logoView.getVisibility()); - ntpLayout.setSearchProviderInfo(/* hasLogo= */ false, /* isGoogle= */ true); + ntpCoordinator.setSearchProviderInfo( + /* hasLogo= */ false, /* isGoogle= */ true); // Mock to notify the template URL service observer. when(mTemplateUrlService.doesDefaultSearchEngineHaveLogo()) .thenReturn(false); when(mTemplateUrlService.isDefaultSearchEngineGoogle()).thenReturn(true); - ntpLayout + ntpCoordinator .getLogoCoordinatorForTesting() .onTemplateURLServiceChangedForTesting(); Assert.assertEquals(View.GONE, logoView.getVisibility()); - ntpLayout.setSearchProviderInfo(/* hasLogo= */ true, /* isGoogle= */ true); + ntpCoordinator.setSearchProviderInfo( + /* hasLogo= */ true, /* isGoogle= */ true); // Mock to notify the template URL service observer. when(mTemplateUrlService.doesDefaultSearchEngineHaveLogo()) .thenReturn(true); - ntpLayout + ntpCoordinator .getLogoCoordinatorForTesting() .onTemplateURLServiceChangedForTesting(); Assert.assertEquals(View.VISIBLE, logoView.getVisibility()); @@ -802,8 +804,8 @@ @Feature({"NewTabPage"}) public void testRecordHistogramLogoClick_Ntp() { LogoBridgeJni.setInstanceForTesting(mLogoBridgeJniMock); - NewTabPageLayout ntpLayout = mNtp.getNewTabPageLayout(); - LogoCoordinator logoCoordinator = ntpLayout.getLogoCoordinatorForTesting(); + NewTabPageCoordinator ntpCoordinator = mNtp.getNewTabPageCoordinator(); + LogoCoordinator logoCoordinator = ntpCoordinator.getLogoCoordinatorForTesting(); logoCoordinator.setLogoBridgeForTesting(mLogoBridge); logoCoordinator.setOnLogoClickUrlForTesting(TEST_URL); HistogramWatcher histogramWatcher = @@ -996,7 +998,7 @@ new Callable<>() { @Override public Boolean call() { - return mNtp.getNewTabPageLayout().urlFocusAnimationsDisabled(); + return mNtp.getNewTabPageCoordinator().urlFocusAnimationsDisabled(); } }); } @@ -1021,7 +1023,7 @@ CriteriaHelper.pollUiThread( () -> { Criteria.checkThat( - ntp.getNewTabPageLayout().getUrlFocusChangeAnimationPercent(), + ntp.getNewTabPageCoordinator().getUrlFocusChangeAnimationPercent(), is(percent)); }); }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/NewTabPageUtilUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/NewTabPageUtilUnitTest.java index d328f1f..2b060bf 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/NewTabPageUtilUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/NewTabPageUtilUnitTest.java
@@ -34,7 +34,9 @@ import org.chromium.components.browser_ui.widget.displaystyle.UiConfig.DisplayStyle; import org.chromium.components.browser_ui.widget.displaystyle.VerticalDisplayStyle; -/** Unit tests for helper functions in {@link NewTabPage} and {@link NewTabPageLayout} classes. */ +/** + * Unit tests for helper functions in {@link NewTabPage} and {@link NewTabPageCoordinator} classes. + */ @RunWith(BaseRobolectricTestRunner.class) @Config(manifest = Config.NONE) public class NewTabPageUtilUnitTest {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java index f6c77b6..612f745 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java
@@ -74,6 +74,7 @@ public class MostVisitedMediatorUnitTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Mock UiConfig mUiConfig; + @Mock ViewGroup mMvTilesContainerLayout; @Mock MostVisitedTilesLayout mMvTilesLayout; @Mock Tile mTile; @Mock SuggestionsTileView mTileView; @@ -557,7 +558,7 @@ ViewGroup.MarginLayoutParams marginLayoutParams = new ViewGroup.MarginLayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - when(mMvTilesLayout.getLayoutParams()).thenReturn(marginLayoutParams); + when(mMvTilesContainerLayout.getLayoutParams()).thenReturn(marginLayoutParams); when(mMvTilesLayout.contentFitsOnTablet(totalWidth)).thenReturn(true); // Test case of regular tablets. @@ -591,6 +592,7 @@ private void createMediator(boolean isTablet) { mMvTilesLayout = Mockito.mock(MostVisitedTilesLayout.class); + when(mMvTilesContainerLayout.findViewById(R.id.mv_tiles_layout)).thenReturn(mMvTilesLayout); when(mMvTilesLayout.getResources()).thenReturn(mResources); when(mMvTilesLayout.getChildCount()).thenReturn(1); @@ -602,7 +604,7 @@ new MostVisitedTilesMediator( mContext, mUiConfig, - mMvTilesLayout, + mMvTilesContainerLayout, mTileRenderer, mModel, isTablet,
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt index cd47db4f..5b83c7d 100644 --- a/chrome/android/profiles/arm.newest.txt +++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@ -chromeos-chrome-arm-147.0.7725.0_pre1595991_rc-r1-merged.afdo.bz2 +chromeos-chrome-arm-148.0.7729.0_pre1597507_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index bdff405..8b7ae1d 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -2987,33 +2987,6 @@ <message name="IDS_NEW_PORTAL_DETECTION_NOTIFICATION_BUTTON" desc="The text to display on the button when the user needs to sign in to the captive portal"> Sign in </message> - <message name="IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIRED" desc="Title for the system notification that current wired network is behind captive portal"> - Connect to network - </message> - <message name="IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI" desc="Title for the system notification that current Wi-Fi network is behind captive portal"> - Connect to Wi-Fi network - </message> - <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIRED" desc="Body of the system notification that current wired network is behind captive portal"> - The network you are using (<ph name="NETWORK_ID">$1<ex>Public Network</ex></ph>) may require you to visit its login page. - </message> - <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIFI" desc="Body of the system notification that current Wi-Fi network is behind captive portal"> - The Wi-Fi network you are using (<ph name="NETWORK_ID">$1<ex>Public Network</ex></ph>) may require you to visit its login page. - </message> - <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_ASK_WIFI" desc="Body of the system notification that asks a user whether to use an extension to authenticate to the network"> - The Wi-Fi network you are using (<ph name="NETWORK_ID">$1<ex>Public Wifi</ex></ph>) may require authentication. - </message> - <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_FAILED_WIFI" desc="Body of the system notification that authentication failed and current network is still behind captive portal"> - Authentication failed. Click to visit the login page for the Wi-Fi network you are using (<ph name="NETWORK_ID">$1<ex>Public Wifi</ex></ph>). - </message> - <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION" desc="Button caption to connect using the authenticating extension"> - Connect using <ph name="ExtensionName">$1<ex>WiFi Company Authenticator</ex></ph> - </message> - <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION_RETRY" desc="Button caption to connect using the authenticating extension"> - Retry using <ph name="ExtensionName">$1<ex>WiFi Company Authenticator</ex></ph> - </message> - <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL" desc="Button caption to open the captive portal login page."> - Visit captive portal login page - </message> <!-- 3G data Notifications --> <message name="IDS_MOBILE_DATA_NOTIFICATION_TITLE" desc="Title of notification telling that mobile data is enabled.">
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 93ff0e8..6ef44799 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -8884,6 +8884,9 @@ <message name="IDS_NTP_ACTION_CHIP_DISABLE_TEXT" desc="Text for menu option to disable action chip feature."> Disable suggestions </message> + <message name="IDS_NTP_ACTION_CHIPS_UNDO_DISABLEMENT_TOAST_MESSAGE" desc="Text for the toast that appears when the user disables action chips from the NTP. Clicking on it re-enables the action chips."> + You won't see suggestions on this page again + </message> <message name="IDS_COMPOSE_REMOVE_TOOL_CHIP_A11Y_LABEL" desc="A11y text for removing a tool in the composebox."> Remove <ph name="TOOL_NAME">$1<ex>Deep Search</ex></ph> </message>
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_ACTION_CHIPS_UNDO_DISABLEMENT_TOAST_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_ACTION_CHIPS_UNDO_DISABLEMENT_TOAST_MESSAGE.png.sha1 new file mode 100644 index 0000000..86f88ee --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_ACTION_CHIPS_UNDO_DISABLEMENT_TOAST_MESSAGE.png.sha1
@@ -0,0 +1 @@ +abecad12f2fe7b2a294234e0067248f73bfc4333 \ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn index b56b8a98..ef50431 100644 --- a/chrome/app/vector_icons/BUILD.gn +++ b/chrome/app/vector_icons/BUILD.gn
@@ -22,6 +22,10 @@ "add_chrome_refresh.icon", "add_circle.icon", "add_photo_alternate.icon", + "arrow_back.icon", + "arrow_downward.icon", + "arrow_forward.icon", + "arrow_upward.icon", "astrophotography_mode.icon", "attach_file.icon", "auto_tab_groups.icon",
diff --git a/chrome/app/vector_icons/arrow_back.icon b/chrome/app/vector_icons/arrow_back.icon new file mode 100644 index 0000000..b4cae2c --- /dev/null +++ b/chrome/app/vector_icons/arrow_back.icon
@@ -0,0 +1,16 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +FILL_RULE_NONZERO, +R_MOVE_TO, 6.88f, 10.75f, +R_LINE_TO, 4.19f, 4.19f, +LINE_TO, 10, 16, +R_LINE_TO, -6, -6, +R_LINE_TO, 6, -6, +R_LINE_TO, 1.06f, 1.06f, +LINE_TO, 6.88f, 9.25f, +H_LINE_TO, 16, +R_V_LINE_TO, 1.5f, +CLOSE
diff --git a/chrome/app/vector_icons/arrow_downward.icon b/chrome/app/vector_icons/arrow_downward.icon new file mode 100644 index 0000000..4d09d08 --- /dev/null +++ b/chrome/app/vector_icons/arrow_downward.icon
@@ -0,0 +1,16 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +FILL_RULE_NONZERO, +MOVE_TO, 9.25f, 4, +R_V_LINE_TO, 9.13f, +LINE_TO, 5.06f, 8.94f, +LINE_TO, 4, 10, +R_LINE_TO, 6, 6, +R_LINE_TO, 6, -6, +R_LINE_TO, -1.06f, -1.06f, +R_LINE_TO, -4.19f, 4.19f, +V_LINE_TO, 4, +CLOSE
diff --git a/chrome/app/vector_icons/arrow_forward.icon b/chrome/app/vector_icons/arrow_forward.icon new file mode 100644 index 0000000..311382bd --- /dev/null +++ b/chrome/app/vector_icons/arrow_forward.icon
@@ -0,0 +1,16 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +FILL_RULE_NONZERO, +MOVE_TO, 13.13f, 10.75f, +H_LINE_TO, 4, +R_V_LINE_TO, -1.5f, +R_H_LINE_TO, 9.13f, +LINE_TO, 8.94f, 5.06f, +LINE_TO, 10, 4, +R_LINE_TO, 6, 6, +R_LINE_TO, -6, 6, +R_LINE_TO, -1.06f, -1.06f, +CLOSE
diff --git a/chrome/app/vector_icons/arrow_upward.icon b/chrome/app/vector_icons/arrow_upward.icon new file mode 100644 index 0000000..8806fe8 --- /dev/null +++ b/chrome/app/vector_icons/arrow_upward.icon
@@ -0,0 +1,16 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +FILL_RULE_NONZERO, +MOVE_TO, 9.25f, 16, +V_LINE_TO, 6.88f, +R_LINE_TO, -4.19f, 4.19f, +LINE_TO, 4, 10, +R_LINE_TO, 6, -6, +R_LINE_TO, 6, 6, +R_LINE_TO, -1.06f, 1.06f, +R_LINE_TO, -4.19f, -4.19f, +V_LINE_TO, 16, +CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index d313f89f..3e56fb02 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -323,15 +323,6 @@ "contextual_cueing/contextual_cueing_service.cc", "contextual_cueing/contextual_cueing_service.h", "contextual_cueing/contextual_cueing_service_factory.cc", - "data_sharing/data_sharing_navigation_throttle.cc", - "data_sharing/data_sharing_navigation_throttle.h", - "data_sharing/data_sharing_navigation_utils.cc", - "data_sharing/data_sharing_navigation_utils.h", - "data_sharing/data_sharing_service_factory.cc", - "data_sharing/migration/migratable_sync_service_coordinator_factory.cc", - "data_sharing/migration/migratable_sync_service_coordinator_factory.h", - "data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.cc", - "data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.h", "defaults.cc", "defaults.h", "download/background_download_service_factory.cc", @@ -703,16 +694,6 @@ "page_content_annotations/page_content_extraction_service_factory.cc", "page_content_annotations/page_content_screenshot_service_factory.cc", "page_content_annotations/page_content_screenshot_service_factory.h", - "page_info/about_this_site_service_factory.cc", - "page_info/about_this_site_service_factory.h", - "page_info/about_this_site_tab_helper.cc", - "page_info/about_this_site_tab_helper.h", - "page_info/merchant_trust_service_delegate.cc", - "page_info/merchant_trust_service_delegate.h", - "page_info/merchant_trust_service_factory.cc", - "page_info/merchant_trust_service_factory.h", - "page_info/privacy_policy_insights_service_factory.cc", - "page_info/privacy_policy_insights_service_factory.h", "page_load_metrics/observers/bookmark_bar_page_load_metrics_observer.cc", "page_load_metrics/observers/bookmark_bar_page_load_metrics_observer.h", "page_load_metrics/observers/captcha_metrics_observer.cc", @@ -1674,6 +1655,7 @@ "//chrome/browser/custom_handlers", "//chrome/browser/data_saver", "//chrome/browser/data_sharing", + "//chrome/browser/data_sharing:impl", "//chrome/browser/desktop_to_mobile_promos:utils", "//chrome/browser/devtools", "//chrome/browser/diagnostics", @@ -2848,11 +2830,6 @@ "commerce/price_tracking/android/price_tracking_notification_bridge.cc", "commerce/price_tracking/android/price_tracking_notification_bridge.h", "composeplate/android/composeplate_utils.cc", - "data_sharing/android/data_sharing_service_factory_android.cc", - "data_sharing/android/data_sharing_ui_delegate_android.cc", - "data_sharing/android/data_sharing_ui_delegate_android.h", - "data_sharing/data_sharing_service_factory_bridge.cc", - "data_sharing/data_sharing_service_factory_bridge.h", "device_reauth/android/device_authenticator_android.cc", "device_reauth/android/device_authenticator_android.h", "device_reauth/android/device_authenticator_bridge.h", @@ -3002,7 +2979,6 @@ "page_content_annotations/android/page_content_extraction_service_factory_android.cc", "page_content_annotations/android/page_content_extraction_tab_model_observer_android.cc", "page_content_annotations/android/page_content_extraction_tab_model_observer_android.h", - "page_info/about_this_site_controller_android.cc", "page_load_metrics/observers/android_page_load_metrics_observer.cc", "page_load_metrics/observers/android_page_load_metrics_observer.h", "password_manager/android/account_chooser_dialog_android.cc", @@ -3534,6 +3510,12 @@ # - c/b/android/android_theme_resources.h # - c/b/android/resource_mapper.h "//chrome/browser/lookalikes:impl", + + # TODO(crbug.com/353332589): Remove this circular dependency when the following + # headers get componentized: + # - c/b/android/android_theme_resources.h + # - c/b/android/resource_mapper.h + "//chrome/browser/page_info:impl", ] if (safe_browsing_mode == 2) { @@ -3596,12 +3578,6 @@ "component_updater/soda_language_pack_component_installer.h", "component_updater/zxcvbn_data_component_installer.cc", "component_updater/zxcvbn_data_component_installer.h", - "data_sharing/desktop/data_sharing_conversion_utils.cc", - "data_sharing/desktop/data_sharing_conversion_utils.h", - "data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc", - "data_sharing/desktop/data_sharing_sdk_delegate_desktop.h", - "data_sharing/desktop/data_sharing_ui_delegate_desktop.cc", - "data_sharing/desktop/data_sharing_ui_delegate_desktop.h", "download/default_download_dir_policy_handler.cc", "download/default_download_dir_policy_handler.h", "download/download_auto_open_policy_handler.cc", @@ -3831,8 +3807,6 @@ "notifications/profile_notification.h", "notifications/screen_capture_notification_blocker.cc", "notifications/screen_capture_notification_blocker.h", - "page_info/web_view_side_panel_throttle.cc", - "page_info/web_view_side_panel_throttle.h", "page_load_metrics/observers/initial_webui_page_load_metrics_observer.cc", "page_load_metrics/observers/initial_webui_page_load_metrics_observer.h", "page_load_metrics/observers/non_tab_webui_page_load_metrics_observer.cc", @@ -4126,6 +4100,7 @@ "//chrome/browser/importer:impl", "//chrome/browser/indigo", "//chrome/browser/indigo:impl", + "//chrome/browser/indigo/onboarding", "//chrome/browser/lens/region_search", "//chrome/browser/media/router:mojo_impl", "//chrome/browser/media/router:providers_wired_display_impl", @@ -4473,7 +4448,6 @@ # componentized: # - c/b/history_embeddings/history_embeddings_utils.h # - c/b/defaults.h" - # - c/b/page_info/merchant_trust_service_factory.h # - c/b/translate/translate_service.h "//chrome/browser/ui/views/location_bar:impl", @@ -4508,6 +4482,9 @@ # TODO(crbug.com/353332589): Remove this circular dependency when # c/b/net/system_network_context_manager.h gets componentized. "//chrome/browser/digital_credentials:impl", + + # Circular dependency on file_select_helper.h. + "//chrome/browser/indigo/onboarding", ] if (is_win || is_chromeos) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 27b39c8..dbce114 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2040,6 +2040,9 @@ const FeatureEntry::FeatureParam kOmniboxAimMultiContextCompactFusebox[] = { {"multi_context", "true"}, {"compact_fusebox", "true"}}; +const FeatureEntry::FeatureParam kOmniboxAimOriginalComposeplate[] = { + {"multi_context", "true"}, + {"redirect_composeplate_button", "false"}}; const FeatureEntry::FeatureParam kOmniboxAimModelPickerAndCanvas[] = { {"multi_context", "true"}, {"show_model_picker", "true"}}; @@ -2054,6 +2057,7 @@ {"MC Button with Hint", kOmniboxAimMultiContextDedicatedModeButtonWithHint, "3395755"}, {"MC Compact", kOmniboxAimMultiContextCompactFusebox, "3395755"}, + {"Original Composeplate", kOmniboxAimOriginalComposeplate, "3395755"}, {"Model Picker & Canvas", kOmniboxAimModelPickerAndCanvas, "3395755"}, }; @@ -7919,10 +7923,6 @@ #endif #if BUILDFLAG(IS_CHROMEOS) - {"disable-quick-answers-v2-translation", - flag_descriptions::kDisableQuickAnswersV2TranslationName, - flag_descriptions::kDisableQuickAnswersV2TranslationDescription, kOsCrOS, - FEATURE_VALUE_TYPE(chromeos::features::kDisableQuickAnswersV2Translation)}, {"quick-answers-rich-card", flag_descriptions::kQuickAnswersRichCardName, flag_descriptions::kQuickAnswersRichCardDescription, kOsCrOS, FEATURE_VALUE_TYPE(chromeos::features::kQuickAnswersRichCard)}, @@ -12299,13 +12299,6 @@ kOsDesktop, FEATURE_VALUE_TYPE(contextual_tasks::kContextualTasksContextLibrary)}, - {"contextual-tasks-expand-button", - contextual_tasks::flag_descriptions::kContextualTasksExpandButtonName, - contextual_tasks::flag_descriptions:: - kContextualTasksExpandButtonDescription, - kOsDesktop, - FEATURE_VALUE_TYPE(contextual_tasks::kContextualTasksExpandButton)}, - #if !BUILDFLAG(IS_ANDROID) {"create-new-tab-group-app-menu-top-level", flag_descriptions::kCreateNewTabGroupAppMenuTopLevelName,
diff --git a/chrome/browser/actor/action_tracker_for_metrics.cc b/chrome/browser/actor/action_tracker_for_metrics.cc index 1b2057e..61e2c67 100644 --- a/chrome/browser/actor/action_tracker_for_metrics.cc +++ b/chrome/browser/actor/action_tracker_for_metrics.cc
@@ -32,6 +32,9 @@ } } base::UmaHistogramCounts100("Actor.Task.SubsequentWaits", total_count); + + base::UmaHistogramCounts100("Autofill.Actor.AutofillAttentionDialogsPerTask", + autofill_attention_dialog_count_); } void ActionTrackerForMetrics::WillMoveToState(ActorTask::State state) { @@ -76,4 +79,8 @@ last_tool_name_in_current_sequence_.clear(); } +void ActionTrackerForMetrics::OnAutofillAttentionDialogPresented() { + autofill_attention_dialog_count_++; +} + } // namespace actor
diff --git a/chrome/browser/actor/action_tracker_for_metrics.h b/chrome/browser/actor/action_tracker_for_metrics.h index 8dd11043..057775e 100644 --- a/chrome/browser/actor/action_tracker_for_metrics.h +++ b/chrome/browser/actor/action_tracker_for_metrics.h
@@ -30,6 +30,8 @@ void OnFinishedAct(const mojom::ActionResult& result); + void OnAutofillAttentionDialogPresented(); + private: // Caches the last tool name found in the current sequence. This is only // committed to the persistent state if the sequence succeeded. @@ -44,6 +46,9 @@ // A map from the name of a tool request to the number of times a wait action // was executed immediately after it within this task's lifetime. std::map<std::string, int> subsequent_waits_per_tool_name_; + + // The number of autofill attention dialogs presented during the task. + int autofill_attention_dialog_count_ = 0; }; } // namespace actor
diff --git a/chrome/browser/actor/actor_task.h b/chrome/browser/actor/actor_task.h index 9905f535..fc74ca30 100644 --- a/chrome/browser/actor/actor_task.h +++ b/chrome/browser/actor/actor_task.h
@@ -222,6 +222,10 @@ Profile* GetProfile() const; + ActionTrackerForMetrics& action_tracker_for_metrics() const { + return *action_tracker_for_metrics_; + } + ActorKeyedService& actor_keyed_service() const { return service_.get(); } // These observations will be added to the final ActionsResult returned by the
diff --git a/chrome/browser/actor/execution_engine.cc b/chrome/browser/actor/execution_engine.cc index 8073d7ab..6e91861 100644 --- a/chrome/browser/actor/execution_engine.cc +++ b/chrome/browser/actor/execution_engine.cc
@@ -27,6 +27,7 @@ #include "base/types/id_type.h" #include "base/types/optional_ref.h" #include "base/types/pass_key.h" +#include "chrome/browser/actor/action_tracker_for_metrics.h" #include "chrome/browser/actor/actor_features.h" #include "chrome/browser/actor/actor_keyed_service.h" #include "chrome/browser/actor/actor_metrics.h" @@ -1179,6 +1180,7 @@ task_->delegate()->RequestToShowAutofillSuggestionsDialog( task_->id(), std::move(requests), std::move(event_handler), std::move(callback)); + task_->action_tracker_for_metrics().OnAutofillAttentionDialogPresented(); } void ExecutionEngine::InterruptFromTool() {
diff --git a/chrome/browser/actor/tools/action_tracker_for_metrics_browsertest.cc b/chrome/browser/actor/tools/action_tracker_for_metrics_browsertest.cc index 266ee62..cabf622 100644 --- a/chrome/browser/actor/tools/action_tracker_for_metrics_browsertest.cc +++ b/chrome/browser/actor/tools/action_tracker_for_metrics_browsertest.cc
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/actor/action_tracker_for_metrics.h" + #include <memory> #include <optional> #include <utility> @@ -359,5 +361,24 @@ /*expected_bucket_count=*/1); } +IN_PROC_BROWSER_TEST_F(ActionTrackerForMetricsTest, + AutofillAttentionDialog_Recorded) { + base::HistogramTester histogram_tester; + + actor_task() + .action_tracker_for_metrics() + .OnAutofillAttentionDialogPresented(); + actor_task() + .action_tracker_for_metrics() + .OnAutofillAttentionDialogPresented(); + + StopAllTasks(); + + histogram_tester.ExpectUniqueSample( + "Autofill.Actor.AutofillAttentionDialogsPerTask", + /*sample=*/2, + /*expected_bucket_count=*/1); +} + } // namespace } // namespace actor
diff --git a/chrome/browser/ash/app_list/BUILD.gn b/chrome/browser/ash/app_list/BUILD.gn index 0479e2f..f56034b 100644 --- a/chrome/browser/ash/app_list/BUILD.gn +++ b/chrome/browser/ash/app_list/BUILD.gn
@@ -86,12 +86,12 @@ "//chrome/browser/search_engines", "//chrome/browser/ui:browser_navigator_params_headers", "//chrome/browser/ui/ash/app_icon_color_cache", - "//chrome/browser/ui/webui/ash/settings/app_management", "//chrome/browser/web_applications", "//chrome/common", "//chromeos/ash/components/browser_context_helper", "//chromeos/ash/components/file_manager:constants", "//chromeos/ash/experiences/arc:arc_app_constants", + "//chromeos/ash/experiences/settings_ui", "//chromeos/ash/services/assistant/public/cpp", "//chromeos/constants", "//components/app_constants",
diff --git a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc index 7164c67b..17b2c0d 100644 --- a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc +++ b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
@@ -86,7 +86,6 @@ #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/browser_with_test_window_test.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/testing_profile.h"
diff --git a/chrome/browser/ash/app_list/app_list_controller_delegate.cc b/chrome/browser/ash/app_list/app_list_controller_delegate.cc index a07bd5c..15a60f8 100644 --- a/chrome/browser/ash/app_list/app_list_controller_delegate.cc +++ b/chrome/browser/ash/app_list/app_list_controller_delegate.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/check_deref.h" #include "base/metrics/histogram_macros.h" #include "build/build_config.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" @@ -15,9 +16,11 @@ #include "chrome/browser/extensions/launch_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/chrome_pages.h" -#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h" #include "chrome/common/extensions/extension_constants.h" +#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h" +#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" #include "components/services/app_service/public/cpp/app_types.h" +#include "components/user_manager/user.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" @@ -60,16 +63,24 @@ .GetAppType(app_id); DCHECK_NE(app_type, apps::AppType::kUnknown); + std::string sub_page; + std::optional<ash::SettingsAppManager::EntryPoint> entry_point; if (app_type == apps::AppType::kWeb || app_type == apps::AppType::kSystemWeb) { - chrome::ShowAppManagementPage(profile, app_id, - ash::settings::AppManagementEntryPoint:: - kAppListContextMenuAppInfoWebApp); + sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(app_id); + entry_point = + ash::SettingsAppManager::EntryPoint::kAppListContextMenuAppInfoWebApp; } else { - chrome::ShowAppManagementPage(profile, app_id, - ash::settings::AppManagementEntryPoint:: - kAppListContextMenuAppInfoChromeApp); + sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(app_id); + entry_point = ash::SettingsAppManager::EntryPoint:: + kAppListContextMenuAppInfoChromeApp; } + + const user_manager::User* user = + ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile); + ash::SettingsAppManager::Get()->Open( + CHECK_DEREF(user), ash::SettingsAppManager::OpenParams{ + .sub_page = sub_page, .entry_point = entry_point}); } void AppListControllerDelegate::UninstallApp(Profile* profile,
diff --git a/chrome/browser/ash/app_list/app_service/BUILD.gn b/chrome/browser/ash/app_list/app_service/BUILD.gn index 93eb0e9..5a46229c 100644 --- a/chrome/browser/ash/app_list/app_service/BUILD.gn +++ b/chrome/browser/ash/app_list/app_service/BUILD.gn
@@ -49,7 +49,8 @@ "//chrome/browser/ash/plugin_vm", "//chrome/browser/ash/remote_apps", "//chrome/browser/profiles:profile", - "//chrome/browser/ui/webui/ash/settings/app_management", + "//chromeos/ash/components/browser_context_helper", + "//chromeos/ash/experiences/settings_ui", "//chromeos/constants", "//components/app_constants", "//components/services/app_service",
diff --git a/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc b/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc index ec1f2e08..181abc7 100644 --- a/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc +++ b/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc
@@ -52,7 +52,6 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_constants.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h"
diff --git a/chrome/browser/ash/app_list/app_service/app_service_context_menu.cc b/chrome/browser/ash/app_list/app_service/app_service_context_menu.cc index 875dec00..cd0a604 100644 --- a/chrome/browser/ash/app_list/app_service/app_service_context_menu.cc +++ b/chrome/browser/ash/app_list/app_service/app_service_context_menu.cc
@@ -6,6 +6,7 @@ #include "ash/public/cpp/app_menu_constants.h" #include "ash/public/cpp/new_window_delegate.h" +#include "base/check_deref.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/functional/callback_helpers.h" @@ -28,9 +29,11 @@ #include "chrome/browser/extensions/menu_manager.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/chrome_pages.h" -#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h" +#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h" +#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" #include "components/app_constants/constants.h" #include "components/services/app_service/public/cpp/types_util.h" +#include "components/user_manager/user.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/context_menu_params.h" @@ -381,9 +384,15 @@ void AppServiceContextMenu::ShowAppInfo() { if (app_type_ == apps::AppType::kArc) { - chrome::ShowAppManagementPage( - profile(), app_id(), - ash::settings::AppManagementEntryPoint::kAppListContextMenuAppInfoArc); + const user_manager::User* user = + ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile()); + ash::SettingsAppManager::Get()->Open( + CHECK_DEREF(user), + ash::SettingsAppManager::OpenParams{ + .sub_page = + ash::SettingsAppManager::CreateAppManagementPagePath(app_id()), + .entry_point = ash::SettingsAppManager::EntryPoint:: + kAppListContextMenuAppInfoArc}); return; }
diff --git a/chrome/browser/ash/app_list/arc/arc_package_syncable_service.cc b/chrome/browser/ash/app_list/arc/arc_package_syncable_service.cc index f49bd51..a72e58c 100644 --- a/chrome/browser/ash/app_list/arc/arc_package_syncable_service.cc +++ b/chrome/browser/ash/app_list/arc/arc_package_syncable_service.cc
@@ -14,7 +14,6 @@ #include "chrome/browser/ash/app_list/arc/arc_package_syncable_service_factory.h" #include "chrome/browser/ash/arc/session/arc_session_manager.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/experiences/arc/arc_util.h" #include "chromeos/ash/experiences/arc/session/connection_holder.h" #include "components/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/ash/app_list/search/help_app_zero_state_provider.cc b/chrome/browser/ash/app_list/search/help_app_zero_state_provider.cc index c79c0f05..1f6565a 100644 --- a/chrome/browser/ash/app_list/search/help_app_zero_state_provider.cc +++ b/chrome/browser/ash/app_list/search/help_app_zero_state_provider.cc
@@ -18,7 +18,6 @@ #include "chrome/browser/ash/release_notes/release_notes_storage.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" -#include "chrome/common/pref_names.h" #include "chromeos/strings/grit/chromeos_strings.h" #include "components/prefs/pref_service.h" #include "components/services/app_service/public/cpp/app_launch_util.h"
diff --git a/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h b/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h index c44f94034..ef9fa86 100644 --- a/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h +++ b/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h
@@ -30,7 +30,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/prefs/pref_service.h" #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ash/app_mode/metrics/periodic_metrics_service_unittest.cc b/chrome/browser/ash/app_mode/metrics/periodic_metrics_service_unittest.cc index 067f573..853d3f1 100644 --- a/chrome/browser/ash/app_mode/metrics/periodic_metrics_service_unittest.cc +++ b/chrome/browser/ash/app_mode/metrics/periodic_metrics_service_unittest.cc
@@ -9,7 +9,6 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" #include "base/time/time.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chromeos/ash/components/network/network_handler_test_helper.h" #include "chromeos/ash/components/sync_wifi/network_test_helper.h"
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge.cc index 3572f794..9f0250a 100644 --- a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge.cc +++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge.cc
@@ -18,7 +18,6 @@ #include "chrome/browser/ash/arc/input_method_manager/arc_input_method_manager_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/api/accessibility_private.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/experiences/arc/arc_browser_context_keyed_service_factory_base.h" #include "chromeos/ash/experiences/arc/message_center/arc_notification_surface.h" #include "chromeos/ash/experiences/arc/session/arc_bridge_service.h"
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc index d86c22bc..fc5f13a 100644 --- a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc +++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
@@ -18,7 +18,6 @@ #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/extensions/api/accessibility_private.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/views/chrome_views_test_base.h" #include "chromeos/ash/experiences/arc/arc_util.h"
diff --git a/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc b/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc index 648e8d0..589de29 100644 --- a/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc +++ b/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc
@@ -38,7 +38,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/net/x509_certificate_model_nss.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/mixin_based_in_process_browser_test.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/ash/components/network/network_cert_loader.h"
diff --git a/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc b/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc index 6a966c2..d2acb17 100644 --- a/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc +++ b/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc
@@ -19,7 +19,6 @@ #include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h" #include "chrome/browser/ui/ash/wallpaper/test_wallpaper_controller.h" #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/ash/components/cryptohome/system_salt_getter.h"
diff --git a/chrome/browser/ash/attestation/platform_verification_flow_unittest.cc b/chrome/browser/ash/attestation/platform_verification_flow_unittest.cc index 5c3433ee..985a0eb6 100644 --- a/chrome/browser/ash/attestation/platform_verification_flow_unittest.cc +++ b/chrome/browser/ash/attestation/platform_verification_flow_unittest.cc
@@ -15,7 +15,6 @@ #include "chrome/browser/ash/attestation/platform_verification_flow.h" #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/attestation/fake_certificate.h" #include "chromeos/ash/components/attestation/mock_attestation_flow.h" #include "chromeos/ash/components/dbus/attestation/attestation.pb.h"
diff --git a/chrome/browser/ash/attestation/tpm_challenge_key.cc b/chrome/browser/ash/attestation/tpm_challenge_key.cc index ede98f2..5db868e 100644 --- a/chrome/browser/ash/attestation/tpm_challenge_key.cc +++ b/chrome/browser/ash/attestation/tpm_challenge_key.cc
@@ -13,7 +13,6 @@ #include "base/sequence_checker.h" #include "chrome/browser/ash/attestation/tpm_challenge_key_result.h" #include "chrome/browser/ash/attestation/tpm_challenge_key_subtle.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/dbus/attestation/attestation_ca.pb.h" #include "chromeos/ash/components/dbus/constants/attestation_constants.h"
diff --git a/chrome/browser/ash/attestation/tpm_challenge_key_subtle.cc b/chrome/browser/ash/attestation/tpm_challenge_key_subtle.cc index 57b4051..581af2bd 100644 --- a/chrome/browser/ash/attestation/tpm_challenge_key_subtle.cc +++ b/chrome/browser/ash/attestation/tpm_challenge_key_subtle.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/extensions/chrome_extension_function_details.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/attestation/attestation_flow_adaptive.h" #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h" #include "chromeos/ash/components/dbus/attestation/attestation_client.h"
diff --git a/chrome/browser/ash/attestation/tpm_challenge_key_subtle_unittest.cc b/chrome/browser/ash/attestation/tpm_challenge_key_subtle_unittest.cc index b47ced5..edf56ec 100644 --- a/chrome/browser/ash/attestation/tpm_challenge_key_subtle_unittest.cc +++ b/chrome/browser/ash/attestation/tpm_challenge_key_subtle_unittest.cc
@@ -24,7 +24,6 @@ #include "chrome/browser/ash/platform_keys/key_permissions/user_private_token_kpm_service_factory.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/common/chrome_constants.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ash/camera_mic/BUILD.gn b/chrome/browser/ash/camera_mic/BUILD.gn index 25fe6f67..0ec69492 100644 --- a/chrome/browser/ash/camera_mic/BUILD.gn +++ b/chrome/browser/ash/camera_mic/BUILD.gn
@@ -33,7 +33,6 @@ "//chrome/browser/ash/login/session", "//chrome/browser/ash/plugin_vm", "//chrome/browser/notifications", - "//chrome/browser/ui/webui/ash/settings/app_management", "//chromeos/ash/components/browser_context_helper", "//chromeos/ash/experiences/settings_ui", "//components/vector_icons",
diff --git a/chrome/browser/ash/camera_mic/vm_camera_mic_manager.cc b/chrome/browser/ash/camera_mic/vm_camera_mic_manager.cc index 6d437af..41d697d 100644 --- a/chrome/browser/ash/camera_mic/vm_camera_mic_manager.cc +++ b/chrome/browser/ash/camera_mic/vm_camera_mic_manager.cc
@@ -33,7 +33,6 @@ #include "chrome/browser/notifications/notification_display_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/chrome_pages.h" -#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h" #include "chrome/grit/generated_resources.h" #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h" #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" @@ -370,27 +369,35 @@ // Opens the settings page. void OpenSettings() const { + std::string sub_page; + std::optional<ash::SettingsAppManager::EntryPoint> entry_point; switch (vm_type_) { - case VmType::kCrostiniVm: { - auto* user = ash::BrowserContextHelper::Get()->GetUserByBrowserContext( - profile_.get()); - ash::SettingsAppManager::Get()->Open( - CHECK_DEREF(user), - {.sub_page = - chromeos::settings::mojom::kCrostiniDetailsSubpagePath}); + case VmType::kCrostiniVm: + sub_page = chromeos::settings::mojom::kCrostiniDetailsSubpagePath; break; - } case VmType::kPluginVm: - chrome::ShowAppManagementPage( - profile_, plugin_vm::kPluginVmShelfAppId, - settings::AppManagementEntryPoint::kNotificationPluginVm); + sub_page = ash::SettingsAppManager::CreateAppManagementPagePath( + plugin_vm::kPluginVmShelfAppId); + entry_point = + ash::SettingsAppManager::EntryPoint::kNotificationPluginVm; break; case VmType::kBorealis: - chrome::ShowAppManagementPage( - profile_, borealis::kClientAppId, - settings::AppManagementEntryPoint::kAppManagementMainViewBorealis); + sub_page = ash::SettingsAppManager::CreateAppManagementPagePath( + borealis::kClientAppId); + entry_point = + ash::SettingsAppManager::EntryPoint::kAppManagementMainViewBorealis; break; + default: + return; } + + const user_manager::User* user = + ash::BrowserContextHelper::Get()->GetUserByBrowserContext( + profile_.get()); + ash::SettingsAppManager::Get()->Open( + CHECK_DEREF(user), + ash::SettingsAppManager::OpenParams{.sub_page = sub_page, + .entry_point = entry_point}); } const raw_ptr<Profile, LeakedDanglingUntriaged> profile_;
diff --git a/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc b/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc index 80c373f..875c7c3 100644 --- a/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc +++ b/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc
@@ -29,7 +29,6 @@ #include "chrome/browser/ash/platform_keys/platform_keys_service_factory.h" #include "chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/network/network_handler.h" #include "chromeos/ash/components/network/network_state_handler.h" #include "chromeos/ash/components/platform_keys/platform_keys.h"
diff --git a/chrome/browser/ash/child_accounts/child_status_reporting_service.cc b/chrome/browser/ash/child_accounts/child_status_reporting_service.cc index 6800162..578804c9 100644 --- a/chrome/browser/ash/child_accounts/child_status_reporting_service.cc +++ b/chrome/browser/ash/child_accounts/child_status_reporting_service.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/child_accounts/child_status_reporting_service.h" +#include "ash/constants/ash_pref_names.h" #include "base/functional/bind.h" #include "base/logging.h" #include "base/task/single_thread_task_runner.h" @@ -13,7 +14,6 @@ #include "chrome/browser/ash/policy/status_collector/child_status_collector.h" #include "chrome/browser/ash/policy/uploading/status_uploader.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/system/statistics_provider.h" #include "components/policy/core/common/cloud/cloud_policy_client.h" #include "components/policy/core/common/cloud/cloud_policy_core.h" @@ -52,7 +52,7 @@ pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); pref_change_registrar_->Init(pref_service); pref_change_registrar_->Add( - prefs::kUsageTimeLimit, + ash::prefs::kUsageTimeLimit, base::BindRepeating( &ChildStatusReportingService::OnTimeLimitsPolicyChanged, base::Unretained(this))); @@ -66,7 +66,7 @@ void ChildStatusReportingService::CreateStatusUploaderIfNeeded( policy::CloudPolicyClient* client) { const base::DictValue& time_limit = - pref_change_registrar_->prefs()->GetDict(prefs::kUsageTimeLimit); + pref_change_registrar_->prefs()->GetDict(ash::prefs::kUsageTimeLimit); const base::TimeDelta new_day_reset_time = usage_time_limit::GetTimeUsageLimitResetTime(time_limit);
diff --git a/chrome/browser/ash/child_accounts/child_user_service.cc b/chrome/browser/ash/child_accounts/child_user_service.cc index 3be239a..81b975c 100644 --- a/chrome/browser/ash/child_accounts/child_user_service.cc +++ b/chrome/browser/ash/child_accounts/child_user_service.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/child_accounts/child_user_service.h" +#include "ash/constants/ash_pref_names.h" #include "base/check.h" #include "base/functional/bind.h" #include "base/metrics/histogram_functions.h" @@ -16,7 +17,6 @@ #include "chrome/browser/ash/child_accounts/time_limits/app_types.h" #include "chrome/browser/ash/child_accounts/usage_time_limit_processor.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "components/app_constants/constants.h" #include "components/prefs/pref_service.h" #include "content/public/browser/browser_context.h" @@ -83,7 +83,7 @@ pref_change_registrar_.Init(profile_->GetPrefs()); pref_change_registrar_.Add( - prefs::kUsageTimeLimit, + ash::prefs::kUsageTimeLimit, base::BindRepeating(&ChildUserService::ReportTimeLimitPolicy, base::Unretained(this))); } @@ -132,7 +132,7 @@ void ChildUserService ::ReportTimeLimitPolicy() const { const base::DictValue& time_limit_prefs = - profile_->GetPrefs()->GetDict(prefs::kUsageTimeLimit); + profile_->GetPrefs()->GetDict(ash::prefs::kUsageTimeLimit); std::set<usage_time_limit::PolicyType> enabled_policies = usage_time_limit::GetEnabledTimeLimitPolicies(time_limit_prefs); @@ -173,7 +173,7 @@ app_time_controller_->RecordMetricsOnShutdown(); app_time_controller_.reset(); } - pref_change_registrar_.Remove(prefs::kUsageTimeLimit); + pref_change_registrar_.Remove(ash::prefs::kUsageTimeLimit); } } // namespace ash
diff --git a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.cc b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.cc index 6d9fbec..e694e9e9 100644 --- a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.cc +++ b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.cc
@@ -4,13 +4,13 @@ #include "chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.h" +#include "ash/constants/ash_pref_names.h" #include "base/check.h" #include "base/metrics/histogram_functions.h" #include "base/unguessable_token.h" #include "chrome/browser/ash/child_accounts/time_limits/app_time_limit_utils.h" #include "chrome/browser/ash/child_accounts/time_limits/app_types.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" @@ -33,7 +33,7 @@ void FamilyUserChromeActivityMetrics::RegisterProfilePrefs( PrefRegistrySimple* registry) { registry->RegisterTimeDeltaPref( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration, + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration, base::TimeDelta()); } @@ -53,14 +53,14 @@ void FamilyUserChromeActivityMetrics::OnNewDay() { base::TimeDelta unreported_duration = pref_service_->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration); if (unreported_duration <= base::TimeDelta()) return; base::UmaHistogramCustomTimes(kChromeBrowserEngagementDurationHistogramName, unreported_duration, kMinChromeDuration, kMaxChromeDuration, kChromeDurationBuckets); pref_service_->ClearPref( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration); } void FamilyUserChromeActivityMetrics::SetActiveSessionStartForTesting( @@ -119,9 +119,9 @@ } else { if (base::Time() < active_duration_start_ && active_duration_start_ < now) { base::TimeDelta unreported_duration = pref_service_->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration); pref_service_->SetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration, + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration, unreported_duration + now - active_duration_start_); }
diff --git a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc index c93cf3c7..127e8299 100644 --- a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc +++ b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <vector> +#include "ash/constants/ash_pref_names.h" #include "base/command_line.h" #include "base/files/file_path.h" #include "base/logging.h" @@ -22,7 +23,6 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/test_extension_system.h" #include "chrome/browser/ui/browser_finder.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/test_browser_window_aura.h" #include "chromeos/dbus/power/fake_power_manager_client.h" @@ -175,7 +175,7 @@ EXPECT_EQ(kHalfHour, pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); // Test multiple browsers. std::unique_ptr<aura::Window> window = @@ -197,7 +197,7 @@ apps::InstanceState::kDestroyed); EXPECT_EQ(base::Hours(1), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); // Test date change. task_environment()->FastForwardBy(base::Days(1)); @@ -205,7 +205,7 @@ EXPECT_EQ(base::TimeDelta(), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); histogram_tester.ExpectTimeBucketCount( FamilyUserChromeActivityMetrics:: kChromeBrowserEngagementDurationHistogramName, @@ -229,7 +229,7 @@ 0); EXPECT_EQ(base::TimeDelta(), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); } // Tests destroying FamilyUserChromeActivityMetrics. OnAppInactive() will be @@ -252,7 +252,7 @@ 0); EXPECT_EQ(kHalfHour, pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); // Test restart. InitiateFamilyUserChromeActivityMetrics(); @@ -271,7 +271,7 @@ 0); EXPECT_EQ(base::Hours(1), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); } TEST_F(FamilyUserChromeActivityMetricsTest, ScreenStateChange) { @@ -290,7 +290,7 @@ kInactiveInstanceState); EXPECT_EQ(kOneMinute, pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); // Test the screen off for 1 day. SetScreenOff(true); @@ -300,7 +300,7 @@ EXPECT_EQ(base::TimeDelta(), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); histogram_tester.ExpectTimeBucketCount( FamilyUserChromeActivityMetrics:: kChromeBrowserEngagementDurationHistogramName, @@ -336,14 +336,14 @@ EXPECT_EQ(base::Minutes(2), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); task_environment()->FastForwardBy(base::Days(1)); OnNewDay(); EXPECT_EQ(base::TimeDelta(), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); + ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration)); histogram_tester.ExpectTimeBucketCount( FamilyUserChromeActivityMetrics:: kChromeBrowserEngagementDurationHistogramName,
diff --git a/chrome/browser/ash/child_accounts/family_user_metrics_service.cc b/chrome/browser/ash/child_accounts/family_user_metrics_service.cc index 55e35a6..4f9003a 100644 --- a/chrome/browser/ash/child_accounts/family_user_metrics_service.cc +++ b/chrome/browser/ash/child_accounts/family_user_metrics_service.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/child_accounts/family_user_metrics_service.h" +#include "ash/constants/ash_pref_names.h" #include "base/check.h" #include "base/time/time.h" #include "chrome/browser/ash/child_accounts/family_user_app_metrics.h" @@ -12,7 +13,6 @@ #include "chrome/browser/ash/child_accounts/family_user_parental_control_metrics.h" #include "chrome/browser/ash/child_accounts/family_user_session_metrics.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "content/public/browser/browser_context.h" @@ -33,7 +33,7 @@ // static void FamilyUserMetricsService::RegisterProfilePrefs( PrefRegistrySimple* registry) { - registry->RegisterIntegerPref(prefs::kFamilyUserMetricsDayId, 0); + registry->RegisterIntegerPref(ash::prefs::kFamilyUserMetricsDayId, 0); } // static @@ -86,14 +86,15 @@ } void FamilyUserMetricsService::CheckForNewDay() { - int day_id = pref_service_->GetInteger(prefs::kFamilyUserMetricsDayId); + int day_id = pref_service_->GetInteger(ash::prefs::kFamilyUserMetricsDayId); base::Time now = base::Time::Now(); // The OnNewDay() event can fire sooner or later than 24 hours due to clock or // time zone changes. if (day_id < GetDayId(now)) { for (Observer& observer : observers_) observer.OnNewDay(); - pref_service_->SetInteger(prefs::kFamilyUserMetricsDayId, GetDayId(now)); + pref_service_->SetInteger(ash::prefs::kFamilyUserMetricsDayId, + GetDayId(now)); } }
diff --git a/chrome/browser/ash/child_accounts/family_user_metrics_service_unittest.cc b/chrome/browser/ash/child_accounts/family_user_metrics_service_unittest.cc index cbd6733..388efa16 100644 --- a/chrome/browser/ash/child_accounts/family_user_metrics_service_unittest.cc +++ b/chrome/browser/ash/child_accounts/family_user_metrics_service_unittest.cc
@@ -6,8 +6,8 @@ #include <memory> +#include "ash/constants/ash_pref_names.h" #include "base/time/time.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power_manager/idle.pb.h" @@ -74,7 +74,7 @@ } int GetDayIdPref() { - return GetPrefService()->GetInteger(prefs::kFamilyUserMetricsDayId); + return GetPrefService()->GetInteger(ash::prefs::kFamilyUserMetricsDayId); } content::BrowserTaskEnvironment task_environment_{
diff --git a/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc index 28b9a2f..ec32509 100644 --- a/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc +++ b/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <string> +#include "ash/constants/ash_pref_names.h" #include "base/containers/flat_map.h" #include "base/run_loop.h" #include "base/strings/strcat.h" @@ -27,7 +28,6 @@ #include "chrome/browser/ash/child_accounts/time_limits/app_types.h" #include "chrome/browser/prefs/browser_prefs.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/ash/experiences/arc/test/fake_app_instance.h" #include "components/prefs/scoped_user_pref_update.h" @@ -119,7 +119,7 @@ /*quota*/ kOneHour, /*last_updated=*/base::Time::Now()); - GetPrefs()->SetDict(prefs::kUsageTimeLimit, policy_content.Clone()); + GetPrefs()->SetDict(ash::prefs::kUsageTimeLimit, policy_content.Clone()); histogram_tester_.ExpectBucketCount( ChildUserService::GetTimeLimitPolicyTypesHistogramNameForTest(), @@ -167,7 +167,7 @@ /*action=*/usage_time_limit::TimeLimitOverride::Action::kLock, /*created_at=*/base::Time::Now() - kOneDay, /*duration=*/base::Hours(2)); - GetPrefs()->SetDict(prefs::kUsageTimeLimit, policy_content.Clone()); + GetPrefs()->SetDict(ash::prefs::kUsageTimeLimit, policy_content.Clone()); // The override time limit policy would not get reported since the difference // between reported and created time are greater than 1 day. @@ -188,7 +188,7 @@ /*action=*/usage_time_limit::TimeLimitOverride::Action::kLock, /*created_at=*/base::Time::Now() - base::Hours(23), /*duration=*/base::Hours(2)); - GetPrefs()->SetDict(prefs::kUsageTimeLimit, policy_content.Clone()); + GetPrefs()->SetDict(ash::prefs::kUsageTimeLimit, policy_content.Clone()); // The override time limit policy would get reported since the created // time and reported time are within 1 day. @@ -245,7 +245,7 @@ base::Hours(1), base::Time::Now())); builder.SetResetTime(6, 0); - GetPrefs()->SetDict(prefs::kPerAppTimeLimitsPolicy, + GetPrefs()->SetDict(ash::prefs::kPerAppTimeLimitsPolicy, builder.value().Clone()); }
diff --git a/chrome/browser/ash/child_accounts/family_user_session_metrics.cc b/chrome/browser/ash/child_accounts/family_user_session_metrics.cc index 0ce6b2a..a24e0c9c 100644 --- a/chrome/browser/ash/child_accounts/family_user_session_metrics.cc +++ b/chrome/browser/ash/child_accounts/family_user_session_metrics.cc
@@ -7,10 +7,10 @@ #include <string> #include <utility> +#include "ash/constants/ash_pref_names.h" #include "base/logging.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/user_metrics.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" @@ -90,7 +90,8 @@ void FamilyUserSessionMetrics::RegisterProfilePrefs( PrefRegistrySimple* registry) { registry->RegisterTimeDeltaPref( - prefs::kFamilyUserMetricsSessionEngagementDuration, base::TimeDelta()); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration, + base::TimeDelta()); } FamilyUserSessionMetrics::FamilyUserSessionMetrics(PrefService* pref_service) @@ -112,13 +113,14 @@ void FamilyUserSessionMetrics::OnNewDay() { base::TimeDelta unreported_duration = pref_service_->GetTimeDelta( - prefs::kFamilyUserMetricsSessionEngagementDuration); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration); if (unreported_duration <= base::TimeDelta()) return; base::UmaHistogramCustomTimes(kSessionEngagementDurationHistogramName, unreported_duration, kMinSessionDuration, kMaxSessionDuration, kSessionDurationBuckets); - pref_service_->ClearPref(prefs::kFamilyUserMetricsSessionEngagementDuration); + pref_service_->ClearPref( + ash::prefs::kFamilyUserMetricsSessionEngagementDuration); } void FamilyUserSessionMetrics::SetActiveSessionStartForTesting( @@ -144,9 +146,9 @@ /*end=*/now); if (now > active_session_start_) { base::TimeDelta unreported_duration = pref_service_->GetTimeDelta( - prefs::kFamilyUserMetricsSessionEngagementDuration); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration); pref_service_->SetTimeDelta( - prefs::kFamilyUserMetricsSessionEngagementDuration, + ash::prefs::kFamilyUserMetricsSessionEngagementDuration, unreported_duration + now - active_session_start_); }
diff --git a/chrome/browser/ash/child_accounts/family_user_session_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_session_metrics_unittest.cc index 3e5c91e7..facf215 100644 --- a/chrome/browser/ash/child_accounts/family_user_session_metrics_unittest.cc +++ b/chrome/browser/ash/child_accounts/family_user_session_metrics_unittest.cc
@@ -6,12 +6,12 @@ #include <memory> +#include "ash/constants/ash_pref_names.h" #include "base/logging.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/user_action_tester.h" #include "base/test/task_environment.h" #include "base/time/time.h" -#include "chrome/common/pref_names.h" #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power_manager/idle.pb.h" #include "chromeos/dbus/power_manager/suspend.pb.h" @@ -161,8 +161,9 @@ histogram_tester.ExpectUniqueTimeSample( FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName, kTenMinutes, 1); - EXPECT_EQ(kOneHour, pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsSessionEngagementDuration)); + EXPECT_EQ(kOneHour, + pref_service()->GetTimeDelta( + ash::prefs::kFamilyUserMetricsSessionEngagementDuration)); } TEST_F(FamilyUserSessionMetricsTest, ScreenStateChange) { @@ -198,7 +199,7 @@ kOneHour, 1); EXPECT_EQ(base::TimeDelta(), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsSessionEngagementDuration)); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration)); // Test screen on on 4 Jan 2020 0:10:00. SetScreenOff(false); @@ -258,7 +259,7 @@ FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName, 2); EXPECT_EQ(base::TimeDelta(), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsSessionEngagementDuration)); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration)); } TEST_F(FamilyUserSessionMetricsTest, SuspendStateChange) { @@ -302,7 +303,7 @@ FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName, 0); EXPECT_EQ(base::Minutes(20), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsSessionEngagementDuration)); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration)); } TEST_F(FamilyUserSessionMetricsTest, ClockBackward) { @@ -340,7 +341,7 @@ FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName, 0); EXPECT_EQ(base::TimeDelta(), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsSessionEngagementDuration)); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration)); } // Tests destroying FamilyUserSessionMetrics without invoking @@ -370,10 +371,10 @@ // Duration metric result: histogram_tester.ExpectTotalCount( - prefs::kFamilyUserMetricsSessionEngagementDuration, 0); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration, 0); EXPECT_EQ(kTenMinutes, pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsSessionEngagementDuration)); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration)); // Test restart. InitiateFamilyUserSessionMetrics(); @@ -396,10 +397,10 @@ // Duration metric result: histogram_tester.ExpectTotalCount( - prefs::kFamilyUserMetricsSessionEngagementDuration, 0); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration, 0); EXPECT_EQ(base::Minutes(20), pref_service()->GetTimeDelta( - prefs::kFamilyUserMetricsSessionEngagementDuration)); + ash::prefs::kFamilyUserMetricsSessionEngagementDuration)); } } // namespace ash
diff --git a/chrome/browser/ash/child_accounts/parent_access_code/config_source.cc b/chrome/browser/ash/child_accounts/parent_access_code/config_source.cc index c5aa4e5..4386dcc 100644 --- a/chrome/browser/ash/child_accounts/parent_access_code/config_source.cc +++ b/chrome/browser/ash/child_accounts/parent_access_code/config_source.cc
@@ -7,11 +7,11 @@ #include <optional> #include <utility> +#include "ash/constants/ash_pref_names.h" #include "base/check.h" #include "base/check_deref.h" #include "base/logging.h" #include "base/values.h" -#include "chrome/common/pref_names.h" #include "components/account_id/account_id.h" #include "components/user_manager/known_user.h" #include "components/user_manager/user.h" @@ -41,7 +41,7 @@ } const base::Value* dictionary = known_user.FindPath( - user->GetAccountId(), prefs::kKnownUserParentAccessCodeConfig); + user->GetAccountId(), ash::prefs::kKnownUserParentAccessCodeConfig); if (dictionary) { LoadConfigForUser(user->GetAccountId(), dictionary->GetDict()); } @@ -60,18 +60,18 @@ #endif // DCHECK_IS_ON() user_manager::KnownUser known_user(&local_state_.get()); - known_user.SetPath(account_id, ::prefs::kKnownUserParentAccessCodeConfig, + known_user.SetPath(account_id, ash::prefs::kKnownUserParentAccessCodeConfig, base::Value(std::move(config))); - const base::Value* dictionary = - known_user.FindPath(account_id, prefs::kKnownUserParentAccessCodeConfig); + const base::Value* dictionary = known_user.FindPath( + account_id, ash::prefs::kKnownUserParentAccessCodeConfig); CHECK(dictionary); LoadConfigForUser(account_id, dictionary->GetDict()); } void ConfigSource::RemoveConfigForUser(const AccountId& account_id) { user_manager::KnownUser(&local_state_.get()) - .RemovePref(account_id, ::prefs::kKnownUserParentAccessCodeConfig); + .RemovePref(account_id, ash::prefs::kKnownUserParentAccessCodeConfig); } void ConfigSource::LoadConfigForUser(const AccountId& account_id,
diff --git a/chrome/browser/ash/child_accounts/parent_access_code/parent_access_service.cc b/chrome/browser/ash/child_accounts/parent_access_code/parent_access_service.cc index b39c1937..ecc23b9 100644 --- a/chrome/browser/ash/child_accounts/parent_access_code/parent_access_service.cc +++ b/chrome/browser/ash/child_accounts/parent_access_code/parent_access_service.cc
@@ -7,6 +7,7 @@ #include <string> #include <utility> +#include "ash/constants/ash_pref_names.h" #include "ash/public/cpp/child_accounts/parent_access_controller.h" #include "base/check.h" #include "base/check_deref.h" @@ -15,7 +16,6 @@ #include "base/no_destructor.h" #include "base/timer/timer.h" #include "base/values.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_registry_simple.h" #include "components/user_manager/user.h" #include "components/user_manager/user_manager.h" @@ -57,7 +57,7 @@ // static void ParentAccessService::RegisterProfilePrefs(PrefRegistrySimple* registry) { - registry->RegisterDictionaryPref(prefs::kParentAccessCodeConfig); + registry->RegisterDictionaryPref(ash::prefs::kParentAccessCodeConfig); } // static
diff --git a/chrome/browser/ash/child_accounts/screen_time_controller.cc b/chrome/browser/ash/child_accounts/screen_time_controller.cc index af1a50d..84605ae 100644 --- a/chrome/browser/ash/child_accounts/screen_time_controller.cc +++ b/chrome/browser/ash/child_accounts/screen_time_controller.cc
@@ -8,6 +8,7 @@ #include <string> #include <utility> +#include "ash/constants/ash_pref_names.h" #include "ash/public/cpp/child_accounts/parent_access_controller.h" #include "ash/public/cpp/login_screen.h" #include "base/functional/bind.h" @@ -22,7 +23,6 @@ #include "chrome/browser/ash/login/lock/screen_locker.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" @@ -39,7 +39,7 @@ // Otherwise, requesting lock does not work. constexpr base::TimeDelta kCheckLoginDelay = base::Seconds(15); -// Dictionary keys for prefs::kScreenTimeLastState. +// Dictionary keys for ash::prefs::kScreenTimeLastState. constexpr char kScreenStateLocked[] = "locked"; constexpr char kScreenStateCurrentPolicyType[] = "active_policy"; constexpr char kScreenStateTimeUsageLimitEnabled[] = "time_usage_limit_enabled"; @@ -68,9 +68,9 @@ // static void ScreenTimeController::RegisterProfilePrefs(PrefRegistrySimple* registry) { - registry->RegisterDictionaryPref(prefs::kScreenTimeLastState); - registry->RegisterDictionaryPref(prefs::kTimeLimitLocalOverride); - registry->RegisterDictionaryPref(prefs::kUsageTimeLimit); + registry->RegisterDictionaryPref(ash::prefs::kScreenTimeLastState); + registry->RegisterDictionaryPref(ash::prefs::kTimeLimitLocalOverride); + registry->RegisterDictionaryPref(ash::prefs::kUsageTimeLimit); } ScreenTimeController::ScreenTimeController(content::BrowserContext* context) @@ -79,7 +79,7 @@ clock_(base::DefaultClock::GetInstance()), next_state_timer_(std::make_unique<base::OneShotTimer>()), usage_time_limit_warning_timer_(std::make_unique<base::OneShotTimer>()), - last_policy_(pref_service_->GetDict(prefs::kUsageTimeLimit).Clone()), + last_policy_(pref_service_->GetDict(ash::prefs::kUsageTimeLimit).Clone()), time_limit_notifier_(context) { session_manager::SessionManager::Get()->AddObserver(this); UsageTimeStateNotifier::GetInstance()->AddObserver(this); @@ -88,7 +88,7 @@ SystemClockClient::Get()->AddObserver(this); pref_change_registrar_.Init(pref_service_); pref_change_registrar_.Add( - prefs::kUsageTimeLimit, + ash::prefs::kUsageTimeLimit, base::BindRepeating(&ScreenTimeController::OnPolicyChanged, base::Unretained(this))); @@ -150,9 +150,9 @@ system::TimezoneSettings::GetInstance()->GetTimezone(); std::optional<usage_time_limit::State> last_state = GetLastStateFromPref(); const base::DictValue& time_limit = - pref_service_->GetDict(prefs::kUsageTimeLimit); + pref_service_->GetDict(ash::prefs::kUsageTimeLimit); const base::DictValue& local_override = - pref_service_->GetDict(prefs::kTimeLimitLocalOverride); + pref_service_->GetDict(ash::prefs::kTimeLimitLocalOverride); // TODO(agawronska): Usage timestamp should be passed instead of second |now|. usage_time_limit::State state = usage_time_limit::GetState( @@ -246,7 +246,7 @@ std::nullopt); // Replace previous local override stored in pref, because PAC can only be // entered if previous override is not active anymore. - pref_service_->SetDict(prefs::kTimeLimitLocalOverride, + pref_service_->SetDict(ash::prefs::kTimeLimitLocalOverride, local_override.ToDictionary()); pref_service_->CommitPendingWrite(); @@ -350,14 +350,15 @@ state_dict.Set(kScreenStateNextUnlockTime, state.next_unlock_time.InSecondsFSinceUnixEpoch()); - pref_service_->SetDict(prefs::kScreenTimeLastState, std::move(state_dict)); + pref_service_->SetDict(ash::prefs::kScreenTimeLastState, + std::move(state_dict)); pref_service_->CommitPendingWrite(); } std::optional<usage_time_limit::State> ScreenTimeController::GetLastStateFromPref() { const base::DictValue& last_state = - pref_service_->GetDict(prefs::kScreenTimeLastState); + pref_service_->GetDict(ash::prefs::kScreenTimeLastState); usage_time_limit::State result; if (last_state.empty()) return std::nullopt; @@ -439,9 +440,9 @@ const icu::TimeZone& time_zone = system::TimezoneSettings::GetInstance()->GetTimezone(); const base::DictValue& time_limit = - pref_service_->GetDict(prefs::kUsageTimeLimit); + pref_service_->GetDict(ash::prefs::kUsageTimeLimit); const base::DictValue& local_override = - pref_service_->GetDict(prefs::kTimeLimitLocalOverride); + pref_service_->GetDict(ash::prefs::kTimeLimitLocalOverride); std::optional<base::TimeDelta> remaining_usage = usage_time_limit::GetRemainingTimeUsage(time_limit, &local_override, now,
diff --git a/chrome/browser/ash/child_accounts/screen_time_controller.h b/chrome/browser/ash/child_accounts/screen_time_controller.h index 400a4f28..6ebefff 100644 --- a/chrome/browser/ash/child_accounts/screen_time_controller.h +++ b/chrome/browser/ash/child_accounts/screen_time_controller.h
@@ -9,6 +9,7 @@ #include <optional> #include <string> +#include "ash/constants/ash_pref_names.h" #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" #include "base/observer_list.h" @@ -118,11 +119,11 @@ // Schedule a call for UsageTimeLimitWarning. void ScheduleUsageTimeLimitWarning(const usage_time_limit::State& state); - // Save the |state| to |prefs::kScreenTimeLastState|. + // Save the |state| to |ash::prefs::kScreenTimeLastState|. void SaveCurrentStateToPref(const usage_time_limit::State& state); - // Get the last calculated |state| from |prefs::kScreenTimeLastState|, if it - // exists. + // Get the last calculated |state| from |ash::prefs::kScreenTimeLastState|, if + // it exists. std::optional<usage_time_limit::State> GetLastStateFromPref(); // Called when the usage time limit is |kUsageTimeLimitWarningTime| or less to
diff --git a/chrome/browser/ash/child_accounts/screen_time_controller_browsertest.cc b/chrome/browser/ash/child_accounts/screen_time_controller_browsertest.cc index e170f5e..e0c089c 100644 --- a/chrome/browser/ash/child_accounts/screen_time_controller_browsertest.cc +++ b/chrome/browser/ash/child_accounts/screen_time_controller_browsertest.cc
@@ -24,7 +24,6 @@ #include "chrome/browser/ash/policy/core/user_policy_test_helper.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/mixin_based_in_process_browser_test.h" #include "components/prefs/pref_service.h" #include "components/session_manager/core/session_manager.h" @@ -122,7 +121,7 @@ void MockChildScreenTime(base::TimeDelta used_time) { child_profile_->GetPrefs()->SetInteger( - ::prefs::kChildScreenTimeMilliseconds, used_time.InMilliseconds()); + ash::prefs::kChildScreenTimeMilliseconds, used_time.InMilliseconds()); } bool IsLocked() {
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc b/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc index d349a11..b414579 100644 --- a/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc +++ b/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc
@@ -6,6 +6,7 @@ #include <algorithm> +#include "ash/constants/ash_pref_names.h" #include "base/logging.h" #include "base/time/default_tick_clock.h" #include "base/unguessable_token.h" @@ -15,7 +16,6 @@ #include "chrome/browser/ash/child_accounts/time_limits/app_time_notification_delegate.h" #include "chrome/browser/ash/child_accounts/time_limits/app_time_policy_helpers.h" #include "chrome/browser/ash/child_accounts/time_limits/persisted_app_info.h" -#include "chrome/common/pref_names.h" #include "components/policy/proto/device_management_backend.pb.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" @@ -133,10 +133,11 @@ // static void AppActivityRegistry::RegisterProfilePrefs(PrefRegistrySimple* registry) { - registry->RegisterListPref(prefs::kPerAppTimeLimitsAppActivities); - registry->RegisterInt64Pref(prefs::kPerAppTimeLimitsLastSuccessfulReportTime, - 0); - registry->RegisterInt64Pref(prefs::kPerAppTimeLimitsLatestLimitUpdateTime, 0); + registry->RegisterListPref(ash::prefs::kPerAppTimeLimitsAppActivities); + registry->RegisterInt64Pref( + ash::prefs::kPerAppTimeLimitsLastSuccessfulReportTime, 0); + registry->RegisterInt64Pref( + ash::prefs::kPerAppTimeLimitsLatestLimitUpdateTime, 0); } AppActivityRegistry::AppActivityRegistry( @@ -445,7 +446,7 @@ } const base::ListValue& list = - pref_service_->GetList(prefs::kPerAppTimeLimitsAppActivities); + pref_service_->GetList(ash::prefs::kPerAppTimeLimitsAppActivities); const std::vector<PersistedAppInfo> applications_info = PersistedAppInfo::PersistedAppInfosFromList( @@ -497,7 +498,7 @@ // Update last successful report time. pref_service_->SetInt64( - prefs::kPerAppTimeLimitsLastSuccessfulReportTime, + ash::prefs::kPerAppTimeLimitsLastSuccessfulReportTime, timestamp.ToDeltaSinceWindowsEpoch().InMicroseconds()); } @@ -529,7 +530,7 @@ // Update the latest app limit update. pref_service_->SetInt64( - prefs::kPerAppTimeLimitsLatestLimitUpdateTime, + ash::prefs::kPerAppTimeLimitsLatestLimitUpdateTime, latest_app_limit_update_.ToDeltaSinceWindowsEpoch().InMicroseconds()); return policy_updated; @@ -671,7 +672,7 @@ void AppActivityRegistry::SaveAppActivity() { { ScopedListPrefUpdate update(pref_service_, - prefs::kPerAppTimeLimitsAppActivities); + ash::prefs::kPerAppTimeLimitsAppActivities); base::ListValue& list = update.Get(); const base::Time now = base::Time::Now(); @@ -745,7 +746,7 @@ void AppActivityRegistry::CleanRegistry(base::Time timestamp) { ScopedListPrefUpdate update(pref_service_, - prefs::kPerAppTimeLimitsAppActivities); + ash::prefs::kPerAppTimeLimitsAppActivities); base::ListValue& list = update.Get(); @@ -1122,8 +1123,8 @@ void AppActivityRegistry::InitializeRegistryFromPref() { DCHECK(pref_service_); - int64_t last_limits_updates = - pref_service_->GetInt64(prefs::kPerAppTimeLimitsLatestLimitUpdateTime); + int64_t last_limits_updates = pref_service_->GetInt64( + ash::prefs::kPerAppTimeLimitsLatestLimitUpdateTime); latest_app_limit_update_ = base::Time::FromDeltaSinceWindowsEpoch( base::Microseconds(last_limits_updates)); @@ -1133,7 +1134,7 @@ void AppActivityRegistry::InitializeAppActivities() { const base::ListValue& list = - pref_service_->GetList(prefs::kPerAppTimeLimitsAppActivities); + pref_service_->GetList(ash::prefs::kPerAppTimeLimitsAppActivities); const std::vector<PersistedAppInfo> applications_info = PersistedAppInfo::PersistedAppInfosFromList( @@ -1183,8 +1184,8 @@ } bool AppActivityRegistry::ShouldCleanUpStoredPref() { - int64_t last_time = - pref_service_->GetInt64(prefs::kPerAppTimeLimitsLastSuccessfulReportTime); + int64_t last_time = pref_service_->GetInt64( + ash::prefs::kPerAppTimeLimitsLastSuccessfulReportTime); if (last_time == 0) { return false;
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_activity_registry_unittest.cc b/chrome/browser/ash/child_accounts/time_limits/app_activity_registry_unittest.cc index 4f2c134..ea44c6c 100644 --- a/chrome/browser/ash/child_accounts/time_limits/app_activity_registry_unittest.cc +++ b/chrome/browser/ash/child_accounts/time_limits/app_activity_registry_unittest.cc
@@ -9,6 +9,7 @@ #include <optional> #include <vector> +#include "ash/constants/ash_pref_names.h" #include "base/test/task_environment.h" #include "base/time/time.h" #include "base/unguessable_token.h" @@ -19,7 +20,6 @@ #include "chrome/browser/ash/child_accounts/time_limits/app_time_notification_delegate.h" #include "chrome/browser/ash/child_accounts/time_limits/app_types.h" #include "chrome/browser/ash/child_accounts/time_limits/persisted_app_info.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/views/chrome_views_test_base.h" #include "components/prefs/pref_service.h" @@ -636,7 +636,7 @@ // Now let's test that the app activity are stored appropriately. const base::ListValue& list = - prefs()->GetList(prefs::kPerAppTimeLimitsAppActivities); + prefs()->GetList(ash::prefs::kPerAppTimeLimitsAppActivities); const std::vector<PersistedAppInfo> app_infos = PersistedAppInfo::PersistedAppInfosFromList( @@ -678,7 +678,7 @@ // Now let's test that the app activity are stored appropriately. const base::ListValue& list = - prefs()->GetList(prefs::kPerAppTimeLimitsAppActivities); + prefs()->GetList(ash::prefs::kPerAppTimeLimitsAppActivities); const std::vector<PersistedAppInfo> app_infos = PersistedAppInfo::PersistedAppInfosFromList( @@ -696,7 +696,7 @@ registry().OnSuccessfullyReported(base::Time::Now()); const base::ListValue& new_list = - prefs()->GetList(prefs::kPerAppTimeLimitsAppActivities); + prefs()->GetList(ash::prefs::kPerAppTimeLimitsAppActivities); const std::vector<PersistedAppInfo> final_app_infos = PersistedAppInfo::PersistedAppInfosFromList( @@ -716,7 +716,7 @@ CreateAppActivityForApp(kApp1, base::Hours(1)); CreateAppActivityForApp(kApp2, base::Hours(1)); - prefs()->SetInt64(prefs::kPerAppTimeLimitsLastSuccessfulReportTime, + prefs()->SetInt64(ash::prefs::kPerAppTimeLimitsLastSuccessfulReportTime, start_time.ToDeltaSinceWindowsEpoch().InMicroseconds()); task_environment()->AdvanceClock(base::Days(30)); @@ -727,7 +727,7 @@ // Now let's test that the app activity are stored appropriately. const base::ListValue& list = - prefs()->GetList(prefs::kPerAppTimeLimitsAppActivities); + prefs()->GetList(ash::prefs::kPerAppTimeLimitsAppActivities); const std::vector<PersistedAppInfo> app_infos = PersistedAppInfo::PersistedAppInfosFromList(
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc b/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc index 079c557..47b3473c 100644 --- a/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc +++ b/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc
@@ -7,6 +7,7 @@ #include <algorithm> #include <string> +#include "ash/constants/ash_pref_names.h" #include "ash/constants/notifier_catalogs.h" #include "ash/public/cpp/notification_utils.h" #include "base/functional/bind.h" @@ -33,7 +34,6 @@ #include "chrome/browser/notifications/notification_handler.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_features.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "chromeos/ash/experiences/arc/app/arc_app_constants.h" #include "chromeos/ui/vector_icons/vector_icons.h" @@ -208,9 +208,10 @@ // static void AppTimeController::RegisterProfilePrefs(PrefRegistrySimple* registry) { - registry->RegisterInt64Pref(prefs::kPerAppTimeLimitsLastResetTime, 0); - registry->RegisterDictionaryPref(prefs::kPerAppTimeLimitsPolicy); - registry->RegisterDictionaryPref(prefs::kPerAppTimeLimitsAllowlistPolicy); + registry->RegisterInt64Pref(ash::prefs::kPerAppTimeLimitsLastResetTime, 0); + registry->RegisterDictionaryPref(ash::prefs::kPerAppTimeLimitsPolicy); + registry->RegisterDictionaryPref( + ash::prefs::kPerAppTimeLimitsAllowlistPolicy); } AppTimeController::AppTimeController( @@ -247,8 +248,9 @@ void AppTimeController::Init() { PrefService* pref_service = profile_->GetPrefs(); RegisterProfilePrefObservers(pref_service); - TimeLimitsAllowlistPolicyUpdated(prefs::kPerAppTimeLimitsAllowlistPolicy); - TimeLimitsPolicyUpdated(prefs::kPerAppTimeLimitsPolicy); + TimeLimitsAllowlistPolicyUpdated( + ash::prefs::kPerAppTimeLimitsAllowlistPolicy); + TimeLimitsPolicyUpdated(ash::prefs::kPerAppTimeLimitsPolicy); // Restore the last reset time. If reset time has have been crossed, triggers // AppActivityRegistry to clear up the running active times of applications. @@ -319,20 +321,20 @@ // Using base::Unretained(this) is safe here because when |pref_registrar_| // gets destroyed, it will remove the observers from PrefService. pref_registrar_->Add( - prefs::kPerAppTimeLimitsPolicy, + ash::prefs::kPerAppTimeLimitsPolicy, base::BindRepeating(&AppTimeController::TimeLimitsPolicyUpdated, base::Unretained(this))); pref_registrar_->Add( - prefs::kPerAppTimeLimitsAllowlistPolicy, + ash::prefs::kPerAppTimeLimitsAllowlistPolicy, base::BindRepeating(&AppTimeController::TimeLimitsAllowlistPolicyUpdated, base::Unretained(this))); } void AppTimeController::TimeLimitsPolicyUpdated(const std::string& pref_name) { - DCHECK_EQ(pref_name, prefs::kPerAppTimeLimitsPolicy); + DCHECK_EQ(pref_name, ash::prefs::kPerAppTimeLimitsPolicy); const base::DictValue& policy = - pref_registrar_->prefs()->GetDict(prefs::kPerAppTimeLimitsPolicy); + pref_registrar_->prefs()->GetDict(ash::prefs::kPerAppTimeLimitsPolicy); std::map<AppId, AppLimit> app_limits = policy::AppLimitsFromDict(policy); @@ -369,10 +371,10 @@ void AppTimeController::TimeLimitsAllowlistPolicyUpdated( const std::string& pref_name) { - DCHECK_EQ(pref_name, prefs::kPerAppTimeLimitsAllowlistPolicy); + DCHECK_EQ(pref_name, ash::prefs::kPerAppTimeLimitsAllowlistPolicy); const base::DictValue& policy = pref_registrar_->prefs()->GetDict( - prefs::kPerAppTimeLimitsAllowlistPolicy); + ash::prefs::kPerAppTimeLimitsAllowlistPolicy); // Figure out a way to avoid cloning AppTimeLimitsAllowlistPolicyWrapper wrapper(&policy); @@ -420,14 +422,14 @@ } const base::DictValue& allowlist_policy = pref_registrar_->prefs()->GetDict( - prefs::kPerAppTimeLimitsAllowlistPolicy); + ash::prefs::kPerAppTimeLimitsAllowlistPolicy); AppTimeLimitsAllowlistPolicyWrapper wrapper(&allowlist_policy); if (std::ranges::contains(wrapper.GetAllowlistAppList(), app_id)) { app_registry_->SetAppAllowlisted(app_id); } const base::DictValue& policy = - pref_registrar_->prefs()->GetDict(prefs::kPerAppTimeLimitsPolicy); + pref_registrar_->prefs()->GetDict(ash::prefs::kPerAppTimeLimitsPolicy); // Update the application's time limit. const std::map<AppId, AppLimit> limits = policy::AppLimitsFromDict(policy); @@ -488,7 +490,7 @@ void AppTimeController::RestoreLastResetTime() { PrefService* pref_service = profile_->GetPrefs(); int64_t reset_time = - pref_service->GetInt64(prefs::kPerAppTimeLimitsLastResetTime); + pref_service->GetInt64(ash::prefs::kPerAppTimeLimitsLastResetTime); if (reset_time == 0) { SetLastResetTime(base::Time::Now()); @@ -525,7 +527,7 @@ PrefService* service = profile_->GetPrefs(); DCHECK(service); service->SetInt64( - prefs::kPerAppTimeLimitsLastResetTime, + ash::prefs::kPerAppTimeLimitsLastResetTime, last_limits_reset_time_.ToDeltaSinceWindowsEpoch().InMicroseconds()); service->CommitPendingWrite(); }
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc b/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc index c899267..d25c8de 100644 --- a/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc +++ b/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc
@@ -6,6 +6,7 @@ #include <optional> +#include "ash/constants/ash_pref_names.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/strings/strcat.h" @@ -25,7 +26,6 @@ #include "chrome/browser/ash/child_accounts/time_limits/app_time_limits_policy_builder.h" #include "chrome/browser/ash/child_accounts/time_limits/app_types.h" #include "chrome/browser/notifications/notification_display_service_tester.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/ash/components/dbus/system_clock/system_clock_client.h" #include "chromeos/ash/components/settings/timezone_settings.h" @@ -453,7 +453,7 @@ builder.AddAppLimit(kApp2, AppLimit(AppRestriction::kTimeLimit, kOneHour / 2, base::Time::Now())); builder.SetResetTime(6, 0); - profile().GetPrefs()->SetDict(prefs::kPerAppTimeLimitsPolicy, + profile().GetPrefs()->SetDict(ash::prefs::kPerAppTimeLimitsPolicy, builder.value().Clone()); } @@ -502,7 +502,7 @@ // |last_reset_time|. last_reset_time = last_reset_time - kDay; profile().GetPrefs()->SetInt64( - prefs::kPerAppTimeLimitsLastResetTime, + ash::prefs::kPerAppTimeLimitsLastResetTime, last_reset_time.ToDeltaSinceWindowsEpoch().InMicroseconds()); InstantiateController(); @@ -535,7 +535,7 @@ builder.AddAppLimit(absent_app, app_limit); builder.AddAppLimit(kApp2, blocked_app); builder.SetResetTime(6, 0); - profile().GetPrefs()->SetDict(prefs::kPerAppTimeLimitsPolicy, + profile().GetPrefs()->SetDict(ash::prefs::kPerAppTimeLimitsPolicy, builder.value().Clone()); }
diff --git a/chrome/browser/ash/child_accounts/usage_time_limit_processor.h b/chrome/browser/ash/child_accounts/usage_time_limit_processor.h index 2f2126a..1cc558cd 100644 --- a/chrome/browser/ash/child_accounts/usage_time_limit_processor.h +++ b/chrome/browser/ash/child_accounts/usage_time_limit_processor.h
@@ -14,6 +14,7 @@ #include <set> #include <unordered_map> +#include "ash/constants/ash_pref_names.h" #include "base/time/time.h" #include "base/values.h" #include "chromeos/ash/components/settings/timezone_settings.h" @@ -198,8 +199,8 @@ const base::DictValue& new_policy); // Returns the active time limit polices in `time_limit_prefs`. -// `time_limit_prefs` is the value of prefs::kUsageTimeLimit which stores the -// usage time limit preference of a user. +// `time_limit_prefs` is the value of ash::prefs::kUsageTimeLimit which stores +// the usage time limit preference of a user. std::set<PolicyType> GetEnabledTimeLimitPolicies( const base::DictValue& time_limit_prefs);
diff --git a/chrome/browser/ash/dbus/vm/BUILD.gn b/chrome/browser/ash/dbus/vm/BUILD.gn index 6a5d81a..0949e7ca 100644 --- a/chrome/browser/ash/dbus/vm/BUILD.gn +++ b/chrome/browser/ash/dbus/vm/BUILD.gn
@@ -48,7 +48,6 @@ "//chrome/browser/chromeos/policy/dlp", "//chrome/browser/profiles:profile", "//chrome/browser/ui/views/select_file_dialog_extension", - "//chrome/browser/ui/webui/ash/settings/app_management", "//chrome/common", "//chrome/common:constants", "//chromeos/ash/components/browser_context_helper",
diff --git a/chrome/browser/ash/dbus/vm/plugin_vm_service_provider.cc b/chrome/browser/ash/dbus/vm/plugin_vm_service_provider.cc index 4b81282d..b91ab8a9 100644 --- a/chrome/browser/ash/dbus/vm/plugin_vm_service_provider.cc +++ b/chrome/browser/ash/dbus/vm/plugin_vm_service_provider.cc
@@ -16,7 +16,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/chrome_pages.h" -#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h" +#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h" #include "chromeos/ash/components/dbus/plugin_vm_service/plugin_vm_service.pb.h" #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" #include "components/prefs/pref_service.h" @@ -116,20 +116,15 @@ return; } + std::string sub_page; + std::optional<ash::SettingsAppManager::EntryPoint> entry_point; + if (request.subpage_path() == kShowSettingsPageDetails) { - Profile* primary_profile = ProfileManager::GetPrimaryUserProfile(); - chrome::ShowAppManagementPage( - primary_profile, plugin_vm::kPluginVmShelfAppId, - settings::AppManagementEntryPoint::kDBusServicePluginVm); + sub_page = ash::SettingsAppManager::CreateAppManagementPagePath( + plugin_vm::kPluginVmShelfAppId); + entry_point = ash::SettingsAppManager::EntryPoint::kDBusServicePluginVm; } else if (request.subpage_path() == kShowSettingsPageSharedPaths) { - if (auto* session = - session_manager::SessionManager::Get()->GetPrimarySession()) { - ash::SettingsAppManager::Get()->Open( - CHECK_DEREF(user_manager::UserManager::Get()->FindUser( - session->account_id())), - {.sub_page = - chromeos::settings::mojom::kPluginVmSharedPathsSubpagePath}); - } + sub_page = chromeos::settings::mojom::kPluginVmSharedPathsSubpagePath; } else { constexpr char error_message[] = "Invalid subpage_path"; LOG(ERROR) << error_message; @@ -139,6 +134,16 @@ return; } + if (auto* session = + session_manager::SessionManager::Get()->GetPrimarySession()) { + const user_manager::User* user = + user_manager::UserManager::Get()->FindUser(session->account_id()); + ash::SettingsAppManager::Get()->Open( + CHECK_DEREF(user), + ash::SettingsAppManager::OpenParams{.sub_page = sub_page, + .entry_point = entry_point}); + } + std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call)); }
diff --git a/chrome/browser/ash/events/event_rewriter_delegate_impl.cc b/chrome/browser/ash/events/event_rewriter_delegate_impl.cc index 43e964b..8c451052 100644 --- a/chrome/browser/ash/events/event_rewriter_delegate_impl.cc +++ b/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
@@ -18,7 +18,6 @@ #include "chrome/browser/extensions/extension_commands_global_registry.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/ash/login/login_display_host.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" #include "components/user_manager/user_manager.h" #include "ui/aura/client/aura_constants.h"
diff --git a/chrome/browser/ash/events/event_rewriter_unittest.cc b/chrome/browser/ash/events/event_rewriter_unittest.cc index 9c05af6..7f1280e 100644 --- a/chrome/browser/ash/events/event_rewriter_unittest.cc +++ b/chrome/browser/ash/events/event_rewriter_unittest.cc
@@ -38,7 +38,6 @@ #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" #include "chrome/browser/ash/notifications/deprecation_notification_controller.h" #include "chrome/browser/ash/preferences/preferences.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/chrome_ash_test_base.h" #include "components/prefs/pref_member.h" #include "components/sync_preferences/testing_pref_service_syncable.h"
diff --git a/chrome/browser/ash/extensions/gfx_utils.cc b/chrome/browser/ash/extensions/gfx_utils.cc index 8ed86443..dc120330 100644 --- a/chrome/browser/ash/extensions/gfx_utils.cc +++ b/chrome/browser/ash/extensions/gfx_utils.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" #include "chrome/common/extensions/extension_constants.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/chromeos_app_icon_resources.h" #include "components/prefs/pref_service.h" #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc b/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc index 8da11ec..11f45e1 100644 --- a/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc +++ b/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc
@@ -30,7 +30,6 @@ #include "chrome/browser/ash/policy/dlp/dlp_files_controller_ash.h" #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h" #include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_rules_manager.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/ash/components/file_manager/app_id.h" #include "components/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc b/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc index 7a15eb9..051fdf81 100644 --- a/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc +++ b/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
@@ -43,7 +43,6 @@ #include "chrome/browser/ui/ash/session/session_controller_client_impl.h" #include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h" #include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ash/hats/hats_dialog.cc b/chrome/browser/ash/hats/hats_dialog.cc index 0cba83b..ef1cead 100644 --- a/chrome/browser/ash/hats/hats_dialog.cc +++ b/chrome/browser/ash/hats/hats_dialog.cc
@@ -20,7 +20,6 @@ #include "chrome/browser/profiles/profile_destroyer.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/dialogs/browser_dialogs.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/browser_resources.h" #include "chrome/grit/generated_resources.h" #include "chromeos/version/version_loader.h"
diff --git a/chrome/browser/ash/input_method/assistive_suggester.cc b/chrome/browser/ash/input_method/assistive_suggester.cc index 2a1feb1..bcd6624 100644 --- a/chrome/browser/ash/input_method/assistive_suggester.cc +++ b/chrome/browser/ash/input_method/assistive_suggester.cc
@@ -21,7 +21,6 @@ #include "chrome/browser/ash/input_method/assistive_prefs.h" #include "chrome/browser/ash/input_method/assistive_suggester_switch.h" #include "chrome/browser/ash/input_method/suggestion_handler_interface.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/services/ime/public/cpp/assistive_suggestions.h" #include "components/exo/wm_helper.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/input_method/autocorrect_manager.cc b/chrome/browser/ash/input_method/autocorrect_manager.cc index 351288c4..17b277b 100644 --- a/chrome/browser/ash/input_method/autocorrect_manager.cc +++ b/chrome/browser/ash/input_method/autocorrect_manager.cc
@@ -23,7 +23,6 @@ #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "chromeos/components/kiosk/kiosk_utils.h" #include "components/strings/grit/components_strings.h"
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc index bffaaf3ae..350e4c7 100644 --- a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc +++ b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
@@ -35,7 +35,6 @@ #include "chrome/browser/ash/lobster/lobster_event_sink.h" #include "chrome/browser/ui/ash/input_method/input_method_menu_manager.h" #include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" #include "chromeos/ash/services/ime/public/cpp/autocorrect.h" #include "chromeos/ash/services/ime/public/mojom/input_method.mojom.h"
diff --git a/chrome/browser/ash/login/existing_user_controller.cc b/chrome/browser/ash/login/existing_user_controller.cc index 1811ebc2..9978acf 100644 --- a/chrome/browser/ash/login/existing_user_controller.cc +++ b/chrome/browser/ash/login/existing_user_controller.cc
@@ -84,7 +84,6 @@ #include "chrome/browser/ui/webui/ash/login/tpm_error_screen_handler.h" #include "chrome/browser/ui/webui/ash/login/update_required_screen_handler.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h" #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
diff --git a/chrome/browser/ash/login/lock/views_screen_locker.cc b/chrome/browser/ash/login/lock/views_screen_locker.cc index 8bc93520..d3a2330 100644 --- a/chrome/browser/ash/login/lock/views_screen_locker.cc +++ b/chrome/browser/ash/login/lock/views_screen_locker.cc
@@ -34,7 +34,6 @@ #include "chrome/browser/global_features.h" #include "chrome/browser/ui/ash/session/session_controller_client_impl.h" #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/install_attributes/install_attributes.h" #include "components/user_manager/known_user.h" #include "components/user_manager/user.h"
diff --git a/chrome/browser/ash/login/lock_screen_utils.cc b/chrome/browser/ash/login/lock_screen_utils.cc index 8bfe14b6..076f0f4 100644 --- a/chrome/browser/ash/login/lock_screen_utils.cc +++ b/chrome/browser/ash/login/lock_screen_utils.cc
@@ -14,7 +14,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ash/input_method/ime_controller_client_impl.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/settings/cros_settings.h" #include "components/account_id/account_id.h" #include "components/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc b/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc index b776740..3deaaf96 100644 --- a/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc +++ b/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
@@ -32,7 +32,6 @@ #include "chrome/browser/ui/ash/login/login_display_host.h" #include "chrome/browser/ui/ash/login/user_adding_screen.h" #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/language_preferences/language_preferences.h" #include "chromeos/ash/components/login/auth/public/user_context.h" #include "chromeos/ash/components/policy/device_policy/device_policy_builder.h"
diff --git a/chrome/browser/ash/login/oobe_browsertest.cc b/chrome/browser/ash/login/oobe_browsertest.cc index f4e8daaa..52d7643 100644 --- a/chrome/browser/ash/login/oobe_browsertest.cc +++ b/chrome/browser/ash/login/oobe_browsertest.cc
@@ -28,7 +28,6 @@ #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h" #include "chrome/browser/ui/webui/signin/signin_utils.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/fake_gaia_mixin.h" #include "chrome/test/base/in_process_browser_test.h" #include "chromeos/ash/components/dbus/cryptohome/key.pb.h"
diff --git a/chrome/browser/ash/login/oobe_localization_browsertest.cc b/chrome/browser/ash/login/oobe_localization_browsertest.cc index 1db45afc..a7b17687 100644 --- a/chrome/browser/ash/login/oobe_localization_browsertest.cc +++ b/chrome/browser/ash/login/oobe_localization_browsertest.cc
@@ -23,7 +23,6 @@ #include "chrome/browser/ui/ash/login/login_display_host.h" #include "chrome/browser/ui/webui/ash/login/oobe_ui.h" #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" #include "chromeos/ash/components/system/fake_statistics_provider.h" #include "chromeos/ash/components/system/statistics_provider.h"
diff --git a/chrome/browser/ash/login/screens/consumer_update_screen.cc b/chrome/browser/ash/login/screens/consumer_update_screen.cc index 372f387..ff9d1a2 100644 --- a/chrome/browser/ash/login/screens/consumer_update_screen.cc +++ b/chrome/browser/ash/login/screens/consumer_update_screen.cc
@@ -26,7 +26,6 @@ #include "chrome/browser/ash/system/timezone_util.h" #include "chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h" #include "chrome/browser/ui/webui/ash/login/mojom/screens_oobe.mojom.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/branded_strings.h" #include "chrome/grit/generated_resources.h" #include "chromeos/ash/components/install_attributes/install_attributes.h"
diff --git a/chrome/browser/ash/login/screens/marketing_opt_in_screen.cc b/chrome/browser/ash/login/screens/marketing_opt_in_screen.cc index 2f2e685f..44d96435 100644 --- a/chrome/browser/ash/login/screens/marketing_opt_in_screen.cc +++ b/chrome/browser/ash/login/screens/marketing_opt_in_screen.cc
@@ -33,7 +33,6 @@ #include "chrome/browser/ui/webui/ash/login/gesture_navigation_screen_handler.h" #include "chrome/browser/ui/webui/ash/login/marketing_opt_in_screen_handler.h" #include "chrome/common/chrome_features.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "chromeos/constants/chromeos_features.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc b/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc index 08f7dcee..bc55767 100644 --- a/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc +++ b/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc
@@ -36,7 +36,6 @@ #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h" #include "chrome/browser/ui/webui/ash/login/marketing_opt_in_screen_handler.h" #include "chrome/common/chrome_features.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/fake_gaia_mixin.h" #include "chromeos/constants/chromeos_features.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/login/screens/update_screen.cc b/chrome/browser/ash/login/screens/update_screen.cc index 513d6854..b7fe9bd4 100644 --- a/chrome/browser/ash/login/screens/update_screen.cc +++ b/chrome/browser/ash/login/screens/update_screen.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/ash/login/wizard_controller.h" #include "chrome/browser/ash/system/timezone_util.h" #include "chrome/browser/ui/webui/ash/login/update_screen_handler.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/branded_strings.h" #include "chrome/grit/generated_resources.h" #include "chromeos/ash/components/network/network_state.h"
diff --git a/chrome/browser/ash/login/smart_lock/smart_lock_notification_controller_unittest.cc b/chrome/browser/ash/login/smart_lock/smart_lock_notification_controller_unittest.cc index afaf1a4b..191a5fe 100644 --- a/chrome/browser/ash/login/smart_lock/smart_lock_notification_controller_unittest.cc +++ b/chrome/browser/ash/login/smart_lock/smart_lock_notification_controller_unittest.cc
@@ -7,7 +7,6 @@ #include "ash/constants/ash_features.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/notifications/notification_display_service_tester.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/browser_with_test_window_test.h" #include "chromeos/ash/components/proximity_auth/proximity_auth_pref_names.h" #include "components/sync_preferences/testing_pref_service_syncable.h"
diff --git a/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc b/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc index 9ff7e660c..b98c4e4 100644 --- a/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc +++ b/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc
@@ -14,7 +14,6 @@ #include "chrome/browser/ash/login/users/default_user_image/default_user_images.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/prefs/pref_service_syncable_util.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/sync_preferences/pref_service_syncable.h"
diff --git a/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc b/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc index 584c2097..bcda6af 100644 --- a/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc +++ b/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc
@@ -25,7 +25,6 @@ #include "chrome/browser/policy/networking/policy_cert_service.h" #include "chrome/browser/policy/networking/policy_cert_service_factory.h" #include "chrome/browser/prefs/browser_prefs.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ash/login/users/user_manager_unittest.cc b/chrome/browser/ash/login/users/user_manager_unittest.cc index bed4bd0..5559c3a5 100644 --- a/chrome/browser/ash/login/users/user_manager_unittest.cc +++ b/chrome/browser/ash/login/users/user_manager_unittest.cc
@@ -31,7 +31,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_test_util.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/fake_profile_manager.h" #include "chrome/test/base/testing_browser_process.h" #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
diff --git a/chrome/browser/ash/note_taking/note_taking_helper_unittest.cc b/chrome/browser/ash/note_taking/note_taking_helper_unittest.cc index 263a8fd..e36c36e5 100644 --- a/chrome/browser/ash/note_taking/note_taking_helper_unittest.cc +++ b/chrome/browser/ash/note_taking/note_taking_helper_unittest.cc
@@ -42,7 +42,6 @@ #include "chrome/browser/web_applications/web_app_install_info.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_registrar.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/browser_with_test_window_test.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_impl.cc b/chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_impl.cc index 9618b00..2883c32 100644 --- a/chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_impl.cc +++ b/chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_impl.cc
@@ -20,7 +20,6 @@ #include "base/values.h" #include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_manager_impl.h" #include "chrome/browser/ash/platform_keys/platform_keys_service.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/platform_keys/platform_keys.h" #include "components/policy/core/common/policy_map.h" #include "components/policy/core/common/policy_namespace.h"
diff --git a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_unittest.cc b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_unittest.cc index 853e0665..cf185e5 100644 --- a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_unittest.cc +++ b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_unittest.cc
@@ -31,7 +31,6 @@ #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" #include "chrome/common/chrome_constants.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ash/policy/handlers/app_launch_automation_policy_handler.cc b/chrome/browser/ash/policy/handlers/app_launch_automation_policy_handler.cc index 268c2cb..168506c3 100644 --- a/chrome/browser/ash/policy/handlers/app_launch_automation_policy_handler.cc +++ b/chrome/browser/ash/policy/handlers/app_launch_automation_policy_handler.cc
@@ -10,7 +10,6 @@ #include "ash/shell.h" #include "base/functional/callback_helpers.h" #include "base/values.h" -#include "chrome/common/pref_names.h" #include "components/desks_storage/core/admin_template_service.h" #include "components/desks_storage/core/desk_model.h" #include "components/desks_storage/core/desk_template_conversion.h"
diff --git a/chrome/browser/ash/policy/handlers/configuration_policy_handler_ash_unittest.cc b/chrome/browser/ash/policy/handlers/configuration_policy_handler_ash_unittest.cc index 98f8b4a..a47a5d2 100644 --- a/chrome/browser/ash/policy/handlers/configuration_policy_handler_ash_unittest.cc +++ b/chrome/browser/ash/policy/handlers/configuration_policy_handler_ash_unittest.cc
@@ -12,7 +12,6 @@ #include "base/json/json_reader.h" #include "base/values.h" #include "chrome/browser/ui/ash/shelf/chrome_shelf_prefs.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/experiences/arc/arc_prefs.h" #include "chromeos/dbus/power/power_policy_controller.h" #include "components/policy/core/browser/configuration_policy_handler.h"
diff --git a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_unittest.cc b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_unittest.cc index 7b688fd..9c62570 100644 --- a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_unittest.cc +++ b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_unittest.cc
@@ -18,7 +18,6 @@ #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h" #include "chrome/browser/browser_process_platform_part.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chromeos/ash/components/dbus/shill/shill_service_client.h" #include "chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h"
diff --git a/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc b/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc index 81ff1a8..0b219a89 100644 --- a/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc +++ b/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/ui/ash/login/login_display_host.h" #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h" #include "chrome/browser/ui/webui/ash/login/reset_screen_handler.h" -#include "chrome/common/pref_names.h" #include "components/policy/core/common/policy_types.h" #include "components/policy/proto/chrome_device_policy.pb.h" #include "components/prefs/pref_change_registrar.h"
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_encrypted_event_reporter_unittest.cc b/chrome/browser/ash/policy/reporting/arc_app_install_encrypted_event_reporter_unittest.cc index 64aa8637..092ee039 100644 --- a/chrome/browser/ash/policy/reporting/arc_app_install_encrypted_event_reporter_unittest.cc +++ b/chrome/browser/ash/policy/reporting/arc_app_install_encrypted_event_reporter_unittest.cc
@@ -19,7 +19,6 @@ #include "chrome/browser/ash/policy/reporting/install_event_log_util.h" #include "chrome/browser/prefs/browser_prefs.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/ash/components/network/network_handler_test_helper.h"
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector.cc index 87977a0e..5e4e78f5 100644 --- a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector.cc +++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector.cc
@@ -6,7 +6,6 @@ #include "base/command_line.h" #include "base/time/time.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/dbus/dbus_thread_manager.h" #include "chromeos/ash/components/network/network_handler.h" #include "chromeos/ash/components/network/network_state.h"
diff --git a/chrome/browser/ash/policy/status_collector/activity_storage.cc b/chrome/browser/ash/policy/status_collector/activity_storage.cc index cfbe444..68dd01e 100644 --- a/chrome/browser/ash/policy/status_collector/activity_storage.cc +++ b/chrome/browser/ash/policy/status_collector/activity_storage.cc
@@ -15,7 +15,6 @@ #include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" #include "base/values.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/ash/policy/status_collector/child_activity_storage.cc b/chrome/browser/ash/policy/status_collector/child_activity_storage.cc index f1ed39e..8278ae1 100644 --- a/chrome/browser/ash/policy/status_collector/child_activity_storage.cc +++ b/chrome/browser/ash/policy/status_collector/child_activity_storage.cc
@@ -6,6 +6,7 @@ #include <algorithm> +#include "ash/constants/ash_pref_names.h" #include "base/strings/string_number_conversions.h" #include "base/values.h" #include "chrome/common/pref_names.h" @@ -71,12 +72,12 @@ DCHECK(activity_day_start <= today_start); base::TimeDelta previous_activity = base::Milliseconds( - pref_service_->GetInteger(prefs::kChildScreenTimeMilliseconds)); + pref_service_->GetInteger(ash::prefs::kChildScreenTimeMilliseconds)); // If this activity window belongs to the current day, the screen time pref // should be updated. if (activity_day_start == today_start) { - pref_service_->SetInteger(prefs::kChildScreenTimeMilliseconds, + pref_service_->SetInteger(ash::prefs::kChildScreenTimeMilliseconds, (previous_activity + activity).InMilliseconds()); pref_service_->SetTime(prefs::kLastChildScreenTimeSaved, now); pref_service_->CommitPendingWrite();
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector.cc b/chrome/browser/ash/policy/status_collector/child_status_collector.cc index a5bca7f..ead1466 100644 --- a/chrome/browser/ash/policy/status_collector/child_status_collector.cc +++ b/chrome/browser/ash/policy/status_collector/child_status_collector.cc
@@ -15,6 +15,7 @@ #include <sstream> #include <utility> +#include "ash/constants/ash_pref_names.h" #include "base/base64.h" #include "base/check.h" #include "base/feature_list.h" @@ -177,7 +178,7 @@ base::TimeDelta ChildStatusCollector::GetActiveChildScreenTime() { UpdateChildUsageTime(); return base::Milliseconds( - pref_service_->GetInteger(prefs::kChildScreenTimeMilliseconds)); + pref_service_->GetInteger(ash::prefs::kChildScreenTimeMilliseconds)); } // static @@ -237,7 +238,7 @@ // Reset screen time if it has not been reset today. if (reset_time > pref_service_->GetTime(prefs::kLastChildScreenTimeReset)) { pref_service_->SetTime(prefs::kLastChildScreenTimeReset, now); - pref_service_->SetInteger(prefs::kChildScreenTimeMilliseconds, 0); + pref_service_->SetInteger(ash::prefs::kChildScreenTimeMilliseconds, 0); pref_service_->CommitPendingWrite(); } @@ -304,7 +305,7 @@ base::UmaHistogramMemoryKB(kReportSizeHistogramName, size_in_bytes / 1024); int64_t last_successful_report_time_int = pref_service_->GetInt64( - prefs::kPerAppTimeLimitsLastSuccessfulReportTime); + ash::prefs::kPerAppTimeLimitsLastSuccessfulReportTime); if (last_successful_report_time_int > 0) { base::Time last_successful_report_time = base::Time::FromDeltaSinceWindowsEpoch(
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc b/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc index 24ba5659..450d68f 100644 --- a/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc +++ b/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc
@@ -12,6 +12,7 @@ #include <utility> #include <vector> +#include "ash/constants/ash_pref_names.h" #include "base/compiler_specific.h" #include "base/environment.h" #include "base/functional/bind.h" @@ -367,8 +368,9 @@ base::OnceClosure(), base::BindOnce( [](int64_t duration, PrefService* profile_pref_service_) { - EXPECT_EQ(duration, profile_pref_service_->GetInteger( - prefs::kChildScreenTimeMilliseconds)); + EXPECT_EQ(duration, + profile_pref_service_->GetInteger( + ash::prefs::kChildScreenTimeMilliseconds)); }, duration, pref_service())); } @@ -755,7 +757,7 @@ { ash::app_time::AppTimeLimitsPolicyBuilder builder; builder.SetAppActivityReportingEnabled(/* enabled */ false); - testing_profile()->GetPrefs()->SetDict(prefs::kPerAppTimeLimitsPolicy, + testing_profile()->GetPrefs()->SetDict(ash::prefs::kPerAppTimeLimitsPolicy, builder.value().Clone()); }
diff --git a/chrome/browser/ash/policy/status_collector/status_collector.cc b/chrome/browser/ash/policy/status_collector/status_collector.cc index 81f24fd..665753cc 100644 --- a/chrome/browser/ash/policy/status_collector/status_collector.cc +++ b/chrome/browser/ash/policy/status_collector/status_collector.cc
@@ -11,6 +11,7 @@ #include <utility> #include <vector> +#include "ash/constants/ash_pref_names.h" #include "base/check.h" #include "base/logging.h" #include "base/time/clock.h" @@ -87,7 +88,7 @@ registry->RegisterDictionaryPref(prefs::kUserActivityTimes); registry->RegisterTimePref(prefs::kLastChildScreenTimeReset, base::Time()); registry->RegisterTimePref(prefs::kLastChildScreenTimeSaved, base::Time()); - registry->RegisterIntegerPref(prefs::kChildScreenTimeMilliseconds, 0); + registry->RegisterIntegerPref(ash::prefs::kChildScreenTimeMilliseconds, 0); AppInfoGenerator::RegisterProfilePrefs(registry); }
diff --git a/chrome/browser/ash/preferences/preferences.cc b/chrome/browser/ash/preferences/preferences.cc index 41804171..d9fa8f2 100644 --- a/chrome/browser/ash/preferences/preferences.cc +++ b/chrome/browser/ash/preferences/preferences.cc
@@ -767,7 +767,7 @@ pref_change_registrar_.Add(ash::prefs::kUserTimezone, callback); pref_change_registrar_.Add(ash::prefs::kResolveTimezoneByGeolocationMethod, callback); - pref_change_registrar_.Add(::prefs::kParentAccessCodeConfig, callback); + pref_change_registrar_.Add(ash::prefs::kParentAccessCodeConfig, callback); for (auto* copy_pref : kCopyToKnownUserPrefs) { pref_change_registrar_.Add(copy_pref, callback); } @@ -1330,12 +1330,12 @@ } } - if (pref_name == ::prefs::kParentAccessCodeConfig || + if (pref_name == ash::prefs::kParentAccessCodeConfig || reason != REASON_PREF_CHANGED) { - if (prefs_->IsManagedPreference(::prefs::kParentAccessCodeConfig) && + if (prefs_->IsManagedPreference(ash::prefs::kParentAccessCodeConfig) && user_->IsChild()) { const base::DictValue& value = - prefs_->GetDict(::prefs::kParentAccessCodeConfig); + prefs_->GetDict(ash::prefs::kParentAccessCodeConfig); parent_access::ParentAccessService::Get().UpdateConfigForUser( user_->GetAccountId(), value.Clone()); } else {
diff --git a/chrome/browser/ash/printing/cups_print_job_notification.cc b/chrome/browser/ash/printing/cups_print_job_notification.cc index 819172e..94d9a69 100644 --- a/chrome/browser/ash/printing/cups_print_job_notification.cc +++ b/chrome/browser/ash/printing/cups_print_job_notification.cc
@@ -20,7 +20,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_utils.h" #include "chrome/browser/ui/chrome_pages.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "components/prefs/pref_service.h" #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ash/printing/enterprise/print_servers_provider_unittest.cc b/chrome/browser/ash/printing/enterprise/print_servers_provider_unittest.cc index 4e412cb9..0b38f94 100644 --- a/chrome/browser/ash/printing/enterprise/print_servers_provider_unittest.cc +++ b/chrome/browser/ash/printing/enterprise/print_servers_provider_unittest.cc
@@ -8,7 +8,6 @@ #include <vector> #include "chrome/browser/ash/printing/print_server.h" -#include "chrome/common/pref_names.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "content/public/test/browser_task_environment.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/ash/printing/print_servers_manager.cc b/chrome/browser/ash/printing/print_servers_manager.cc index 91288f9a..e3929d5 100644 --- a/chrome/browser/ash/printing/print_servers_manager.cc +++ b/chrome/browser/ash/printing/print_servers_manager.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/ash/printing/synced_printers_manager_factory.h" #include "chrome/browser/ash/printing/usb_printer_notification_controller.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chromeos/printing/cups_printer_status.h" #include "chromeos/printing/printing_constants.h" #include "chromeos/printing/uri.h"
diff --git a/chrome/browser/ash/printing/print_servers_manager_unittest.cc b/chrome/browser/ash/printing/print_servers_manager_unittest.cc index be27abc..97c7a082 100644 --- a/chrome/browser/ash/printing/print_servers_manager_unittest.cc +++ b/chrome/browser/ash/printing/print_servers_manager_unittest.cc
@@ -17,7 +17,6 @@ #include "base/test/task_environment.h" #include "chrome/browser/ash/printing/enterprise/print_servers_provider.h" #include "chrome/browser/ash/printing/server_printers_provider.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "components/prefs/pref_service.h" #include "components/sync_preferences/testing_pref_service_syncable.h"
diff --git a/chrome/browser/ash/printing/server_printers_provider.cc b/chrome/browser/ash/printing/server_printers_provider.cc index dec0c842..2d973ac0 100644 --- a/chrome/browser/ash/printing/server_printers_provider.cc +++ b/chrome/browser/ash/printing/server_printers_provider.cc
@@ -15,7 +15,6 @@ #include "base/task/sequenced_task_runner.h" #include "chrome/browser/ash/printing/server_printers_fetcher.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "components/device_event_log/device_event_log.h" #include "url/gurl.h"
diff --git a/chrome/browser/ash/printing/synced_printers_manager.cc b/chrome/browser/ash/printing/synced_printers_manager.cc index 65eec4f..8a5a2bad 100644 --- a/chrome/browser/ash/printing/synced_printers_manager.cc +++ b/chrome/browser/ash/printing/synced_printers_manager.cc
@@ -14,7 +14,6 @@ #include "base/values.h" #include "chrome/browser/ash/printing/printers_sync_bridge.h" #include "chrome/browser/ash/printing/specifics_translation.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/settings/cros_settings_names.h" #include "chromeos/printing/printer_configuration.h" #include "chromeos/printing/printer_translator.h"
diff --git a/chrome/browser/ash/printing/synced_printers_manager_unittest.cc b/chrome/browser/ash/printing/synced_printers_manager_unittest.cc index 5a02eed..858019ac 100644 --- a/chrome/browser/ash/printing/synced_printers_manager_unittest.cc +++ b/chrome/browser/ash/printing/synced_printers_manager_unittest.cc
@@ -18,7 +18,6 @@ #include "base/time/time.h" #include "chrome/browser/ash/printing/printers_sync_bridge.h" #include "chrome/browser/ash/printing/synced_printers_manager_factory.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "components/sync/model/data_type_store.h" #include "components/sync/test/data_type_store_test_util.h"
diff --git a/chrome/browser/ash/system/timezone_resolver_manager.cc b/chrome/browser/ash/system/timezone_resolver_manager.cc index 47c32f7d..020ffe6e 100644 --- a/chrome/browser/ash/system/timezone_resolver_manager.cc +++ b/chrome/browser/ash/system/timezone_resolver_manager.cc
@@ -21,7 +21,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/geolocation/system_location_provider.h" #include "chromeos/ash/components/install_attributes/install_attributes.h" #include "components/policy/proto/chrome_device_policy.pb.h"
diff --git a/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc b/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc index f8ab6b2f..1f0ca2a1 100644 --- a/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc +++ b/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc
@@ -30,7 +30,6 @@ #include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h" #include "chrome/browser/web_applications/web_app_command_scheduler.h" #include "chrome/browser/web_applications/web_app_provider.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h" #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java index 0205e00d..03f7b2be 100644 --- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java +++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java
@@ -72,7 +72,9 @@ */ public void setClickCallback(@Nullable ClickWithMetaStateCallback callback) { setOnClickListener( - callback != null ? (v) -> callback.onClickWithMeta(mLastEventMetaState) : null); + callback != null + ? (v) -> callback.onClickWithMeta(mLastEventMetaState, /* buttonState= */ 0) + : null); } /**
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java index 33cfdafb..307d153d 100644 --- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java +++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java
@@ -289,7 +289,7 @@ // Private methods. // TODO(crbug.com/394614779): Open in popup window instead of bookmark manager. - private void onAllBookmarksButtonClick(int metaState) { + private void onAllBookmarksButtonClick(int metaState, int buttonState) { // Open the manager iff the active profile and model are unchanged to prevent accidentally // opening the manager for the wrong profile/model. We will only record the click event if // this guard passes, so the data shows only actions that resulted in a change. @@ -1047,7 +1047,7 @@ new PropertyModel.Builder(BookmarkBarButtonProperties.ALL_KEYS) .with( BookmarkBarButtonProperties.CLICK_CALLBACK, - (metaState) -> clickCallback.accept(item, metaState)) + (metaState, buttonState) -> clickCallback.accept(item, metaState)) .with(BookmarkBarButtonProperties.KEY_LISTENER, keyListener) .with( BookmarkBarButtonProperties.ICON_TINT_LIST_ID,
diff --git a/chrome/browser/chromeos/extensions/wm/wm_desks_private_events_unittest.cc b/chrome/browser/chromeos/extensions/wm/wm_desks_private_events_unittest.cc index 7b8220b..36921c69 100644 --- a/chrome/browser/chromeos/extensions/wm/wm_desks_private_events_unittest.cc +++ b/chrome/browser/chromeos/extensions/wm/wm_desks_private_events_unittest.cc
@@ -8,7 +8,6 @@ #include "base/uuid.h" #include "chrome/browser/extensions/extension_service_test_with_install.h" #include "chrome/common/extensions/api/wm_desks_private.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/test_browser_window.h" #include "chrome/test/base/testing_profile.h" #include "components/crx_file/id_util.h"
diff --git a/chrome/browser/chromeos/network/network_portal_signin_window.cc b/chrome/browser/chromeos/network/network_portal_signin_window.cc index 16ea581..da1f3e8 100644 --- a/chrome/browser/chromeos/network/network_portal_signin_window.cc +++ b/chrome/browser/chromeos/network/network_portal_signin_window.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/dialogs/browser_dialogs.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/network/network_handler.h" #include "chromeos/ash/components/network/network_state_handler.h" #include "components/device_event_log/device_event_log.h"
diff --git a/chrome/browser/contextual_search/contextual_search_web_contents_helper.cc b/chrome/browser/contextual_search/contextual_search_web_contents_helper.cc index e6ebd4e..0549c6c6 100644 --- a/chrome/browser/contextual_search/contextual_search_web_contents_helper.cc +++ b/chrome/browser/contextual_search/contextual_search_web_contents_helper.cc
@@ -19,4 +19,9 @@ return std::move(input_state_model_); } +std::unique_ptr<contextual_search::ContextualSearchSessionHandle> +ContextualSearchWebContentsHelper::TakeSessionHandle() { + return std::move(session_handle_); +} + WEB_CONTENTS_USER_DATA_KEY_IMPL(ContextualSearchWebContentsHelper);
diff --git a/chrome/browser/contextual_search/contextual_search_web_contents_helper.h b/chrome/browser/contextual_search/contextual_search_web_contents_helper.h index 3c0bad4f..9c78958 100644 --- a/chrome/browser/contextual_search/contextual_search_web_contents_helper.h +++ b/chrome/browser/contextual_search/contextual_search_web_contents_helper.h
@@ -47,9 +47,15 @@ return session_handle_.get(); } - // Returns the input state model. May return nullptr. + // Returns the input state model. May return nullptr. This transfers + // ownership to the caller. std::unique_ptr<contextual_search::InputStateModel> TakeInputStateModel(); + // Returns the session handle. May return nullptr. This transfers ownership + // to the caller. + std::unique_ptr<contextual_search::ContextualSearchSessionHandle> + TakeSessionHandle(); + // Returns the task ID associated with the current contextual search session. // std::nullopt if the web_contents isn't showing a contextual task. const std::optional<base::Uuid>& task_id() const { return task_id_; }
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc index f1bc1d333..ac18d82b 100644 --- a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc +++ b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc
@@ -160,6 +160,7 @@ mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_searchbox_handler, GetSessionHandleCallback get_session_callback, + ClearSessionHandleCallback clear_session_callback, TakeInputStateModelCallback take_input_model_callback) : ComposeboxHandler( std::move(pending_handler), @@ -171,7 +172,8 @@ std::make_unique<ContextualTasksOmniboxClient>(profile, web_contents, this)), - std::move(get_session_callback)), + std::move(get_session_callback), + std::move(clear_session_callback)), take_input_model_callback_(std::move(take_input_model_callback)), web_ui_interface_(web_ui_interface), contextual_tasks_service_(
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h index a9ee937..1c51522 100644 --- a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h +++ b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h
@@ -62,6 +62,7 @@ mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_searchbox_handler, GetSessionHandleCallback get_session_callback, + ClearSessionHandleCallback clear_session_callback, TakeInputStateModelCallback take_input_model_callback); ~ContextualTasksComposeboxHandler() override;
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc index b4bc4f62..e0a973b 100644 --- a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc +++ b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc
@@ -290,6 +290,8 @@ base::BindRepeating( &ContextualTasksUI::GetOrCreateContextualSessionHandle, base::Unretained(mock_ui_.get())), + base::BindRepeating(&ContextualTasksUI::ClearContextualSessionHandle, + base::Unretained(mock_ui_.get())), base::BindRepeating(&ContextualTasksUI::TakeInputStateModel, base::Unretained(mock_ui_.get()))); handler_->SetMockContextualTasksService(mock_contextual_tasks_service_ptr_); @@ -2742,6 +2744,7 @@ []() -> contextual_search::ContextualSearchSessionHandle* { return nullptr; }), + base::DoNothing(), base::BindRepeating(&ContextualTasksUI::TakeInputStateModel, base::Unretained(mock_ui_.get()))); auto file_info = searchbox::mojom::SelectedFileInfo::New(); @@ -2812,6 +2815,8 @@ base::BindRepeating( &ContextualTasksUI::GetOrCreateContextualSessionHandle, base::Unretained(mock_ui_.get())), + base::BindRepeating(&ContextualTasksUI::ClearContextualSessionHandle, + base::Unretained(mock_ui_.get())), std::move(mock_callback)); // 2. Act: Trigger the handler to fetch the model via the callback.
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc b/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc index 33b49472..a4c5286 100644 --- a/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc +++ b/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc
@@ -53,6 +53,7 @@ mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_searchbox_handler, GetSessionHandleCallback get_session_callback, + ClearSessionHandleCallback clear_session_callback, TakeInputStateModelCallback get_inputstatemodel_callback) : ContextualTasksComposeboxHandler( ui_controller, @@ -62,6 +63,7 @@ std::move(pending_page), std::move(pending_searchbox_handler), std::move(get_session_callback), + std::move(clear_session_callback), std::move(get_inputstatemodel_callback)) {} ~MockContextualTasksComposeboxHandler() override = default; @@ -682,6 +684,7 @@ base::BindRepeating( &ContextualTasksUI::GetOrCreateContextualSessionHandle, base::Unretained(ui)), + base::DoNothing(), base::BindRepeating(&ContextualTasksUI::TakeInputStateModel, base::Unretained(ui))); MockContextualTasksComposeboxHandler* mock_handler =
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_ui.cc b/chrome/browser/contextual_tasks/contextual_tasks_ui.cc index a45e5e9..2ba71264 100644 --- a/chrome/browser/contextual_tasks/contextual_tasks_ui.cc +++ b/chrome/browser/contextual_tasks/contextual_tasks_ui.cc
@@ -677,6 +677,8 @@ base::BindRepeating( &ContextualTasksUI::GetOrCreateContextualSessionHandle, base::Unretained(this)), + base::BindRepeating(&ContextualTasksUI::ClearContextualSessionHandle, + base::Unretained(this)), base::BindRepeating(&ContextualTasksUI::TakeInputStateModel, base::Unretained(this))); composebox_handler_->SetPage(std::move(pending_searchbox_page)); @@ -733,6 +735,11 @@ return helper->session_handle(); } +// Empty implementation, does not need to be cleared in contextual tasks. Only +// needs to be cleared when transferring ownership to a new web contents / UI +// controller which never happens for contextual tasks. +void ContextualTasksUI::ClearContextualSessionHandle() {} + std::unique_ptr<contextual_search::InputStateModel> ContextualTasksUI::TakeInputStateModel() { if (!task_id_.has_value()) {
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_ui.h b/chrome/browser/contextual_tasks/contextual_tasks_ui.h index 62b42a6..bfaa38e 100644 --- a/chrome/browser/contextual_tasks/contextual_tasks_ui.h +++ b/chrome/browser/contextual_tasks/contextual_tasks_ui.h
@@ -155,6 +155,9 @@ void PostMessageToWebview(const lens::ClientToAimMessage& message) override; contextual_search::ContextualSearchSessionHandle* GetOrCreateContextualSessionHandle() override; + + void ClearContextualSessionHandle(); + std::unique_ptr<contextual_search::InputStateModel> TakeInputStateModel() override; mojo::Remote<contextual_tasks::mojom::Page>& GetPageRemote() override;
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc b/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc index a05b0a1..7012796 100644 --- a/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc +++ b/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc
@@ -213,31 +213,8 @@ auto* helper = ContextualSearchWebContentsHelper::FromWebContents( source_tab->GetContents()); if (helper && helper->session_handle()) { - source = helper->session_handle()->GetMetricsRecorder()->source(); - - auto* service = - ContextualSearchServiceFactory::GetForProfile(profile_.get()); - if (service) { - // Create a new handle for existing session. The session handle should - // not be moved because there can be cases where a user opens the WebUI - // in a new tab (and would therefore leave the source tab without a - // handle). - session_handle = - service->GetSession(helper->session_handle()->session_id(), - helper->session_handle()->invocation_source()); - if (session_handle) { - session_handle->set_submitted_context_tokens( - helper->session_handle()->GetSubmittedContextTokens()); - // TODO(crbug.com/469877869): Determine what to do with the return - // value of this call, or move this call to a different location. - session_handle->CheckSearchContentSharingSettings( - profile_->GetPrefs()); - // Now that fulfillment has been passed to contextual tasks, the - // original session handle should clear its submitted context. Not - // clearing leaves it in a faulty state for future queries. - helper->session_handle()->ClearSubmittedContextTokens(); - } - } + session_handle = helper->TakeSessionHandle(); + source = session_handle->GetMetricsRecorder()->source(); } } base::UmaHistogramEnumeration(
diff --git a/chrome/browser/data_sharing/BUILD.gn b/chrome/browser/data_sharing/BUILD.gn index a41a4c9..8edccb83 100644 --- a/chrome/browser/data_sharing/BUILD.gn +++ b/chrome/browser/data_sharing/BUILD.gn
@@ -460,6 +460,132 @@ } source_set("data_sharing") { - sources = [ "data_sharing_service_factory.h" ] - public_deps = [ "//chrome/browser/profiles:profile" ] + sources = [ + "data_sharing_navigation_throttle.h", + "data_sharing_navigation_utils.h", + "data_sharing_service_factory.h", + "migration/migratable_sync_service_coordinator_factory.h", + "personal_collaboration_data/personal_collaboration_data_service_factory.h", + ] + public_deps = [ + "//chrome/browser/profiles:profile", + "//components/collaboration/public", + "//content/public/browser", + ] + + if (is_android) { + sources += [ + "android/data_sharing_ui_delegate_android.h", + "data_sharing_service_factory_bridge.h", + ] + } else { + sources += [ + "desktop/data_sharing_conversion_utils.h", + "desktop/data_sharing_sdk_delegate_desktop.h", + "desktop/data_sharing_ui_delegate_desktop.h", + ] + + public_deps += [ + "//chrome/browser/ui/webui/data_sharing", + "//components/data_sharing/public/protocol:mojo_bindings", + ] + } +} + +# The reason why `impl` target can't be merged into `data_sharing` target +# above is that data_sharing_ui_delegate_desktop.cc includes headers from +# //c/b/ui:ui that are not componentized yet, namely: +# +# - c/b/ui/webui/data_sharing/data_sharing_page_handler.h +# - c/b/ui/views/data_sharing/collaboration_controller_delegate_desktop.h +# - c/b/ui/browser_finder.h +# +# Hence, depending on //c/b/ui:ui isn't itself a problem. However, in this case, +# if `data_sharing` depends on it, there will be non-trivial circular dependency errors. +source_set("impl") { + sources = [ + "data_sharing_navigation_throttle.cc", + "data_sharing_navigation_utils.cc", + "data_sharing_service_factory.cc", + "migration/migratable_sync_service_coordinator_factory.cc", + "personal_collaboration_data/personal_collaboration_data_service_factory.cc", + ] + deps = [ + ":data_sharing", + "//chrome/browser/collaboration", + "//chrome/browser/profiles:profile", + "//chrome/browser/signin", + "//chrome/browser/sync", + "//chrome/browser/sync:factories", + "//chrome/common:channel_info", + "//components/data_sharing/internal", + "//components/data_sharing/internal:personal_collaboration_data_internal", + "//components/data_sharing/migration/internal", + "//components/data_sharing/public:personal_collaboration_data", + "//content/public/browser", + ] + + if (is_android) { + sources += [ + "android/data_sharing_service_factory_android.cc", + "android/data_sharing_ui_delegate_android.cc", + "data_sharing_service_factory_bridge.cc", + ] + deps += [ ":jni_headers" ] + } else { + sources += [ + "desktop/data_sharing_conversion_utils.cc", + "desktop/data_sharing_sdk_delegate_desktop.cc", + "desktop/data_sharing_ui_delegate_desktop.cc", + ] + deps += [ + "//chrome/browser/ui", + "//chrome/browser/ui/browser_window", + ] + } + + public_deps = [ "//chrome/browser:browser_public_dependencies" ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "data_sharing_navigation_throttle_unittest.cc", + "data_sharing_service_factory_unittest.cc", + "personal_collaboration_data/personal_collaboration_data_service_factory_unittest.cc", + ] + deps = [ + ":data_sharing", + "//base/test:test_support", + "//chrome/browser/collaboration", + "//chrome/test:test_support", + "//components/collaboration:test_support", + "//components/data_sharing/internal", + "//components/data_sharing/public:personal_collaboration_data", + "//content/test:test_support", + ] + + if (!is_android) { + sources += [ "desktop/data_sharing_conversion_utils_unittest.cc" ] + } +} + +if (!is_android) { + source_set("browser_tests") { + testonly = true + defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] + sources = [ + "desktop/data_sharing_sdk_delegate_desktop_browsertest.cc", + "desktop/data_sharing_service_browsertest.cc", + ] + deps = [ + ":data_sharing", + "//base", + "//base/test:test_support", + "//chrome/browser/ui", + "//chrome/common", + "//chrome/test:test_support_ui", + "//components/data_sharing/public", + ] + } }
diff --git a/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc b/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc index 5638dd2..bc51231 100644 --- a/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc +++ b/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc
@@ -10,7 +10,6 @@ #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h" -#include "chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.h" #include "components/collaboration/public/collaboration_service.h" #include "components/data_sharing/public/data_sharing_service.h" #include "url/gurl.h"
diff --git a/chrome/browser/desktop_to_mobile_promos/promos_utils.cc b/chrome/browser/desktop_to_mobile_promos/promos_utils.cc index 8cff2ba..ead0cad 100644 --- a/chrome/browser/desktop_to_mobile_promos/promos_utils.cc +++ b/chrome/browser/desktop_to_mobile_promos/promos_utils.cc
@@ -233,7 +233,7 @@ promo_impression = DesktopIOSPromoImpression::kThirdImpression; break; default: - NOTREACHED(); + return; } base::UmaHistogramEnumeration( "IOS.Desktop." + promo_histogram_type + ".Shown", promo_impression);
diff --git a/chrome/browser/educational_tip/BUILD.gn b/chrome/browser/educational_tip/BUILD.gn index 2f55a82..0495f812 100644 --- a/chrome/browser/educational_tip/BUILD.gn +++ b/chrome/browser/educational_tip/BUILD.gn
@@ -32,18 +32,18 @@ "java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupPromoCoordinator.java", "java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupSyncPromoCoordinator.java", "java/src/org/chromium/chrome/browser/educational_tip/cards/TipsNotificationsPromoCoordinator.java", - "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetContent.java", - "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinator.java", - "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetItem.java", - "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerView.java", - "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetProperties.java", - "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetViewBinder.java", "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellBuilder.java", "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinator.java", "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellProperties.java", "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellView.java", "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellViewBinder.java", - "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipSetupListBottomSheetListItemView.java", + "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetContent.java", + "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinator.java", + "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetItem.java", + "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerView.java", + "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListItemView.java", + "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetProperties.java", + "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetViewBinder.java", "java/src/org/chromium/chrome/browser/setup_list/SetupListCompletable.java", "java/src/org/chromium/chrome/browser/setup_list/SetupListManager.java", "java/src/org/chromium/chrome/browser/setup_list/SetupListModuleUtils.java", @@ -157,12 +157,12 @@ "junit/src/org/chromium/chrome/browser/educational_tip/TabGroupPromoCoordinatorUnitTest.java", "junit/src/org/chromium/chrome/browser/educational_tip/TabGroupSyncPromoCoordinatorUnitTest.java", "junit/src/org/chromium/chrome/browser/educational_tip/cards/SavePasswordsPromoCoordinatorUnitTest.java", - "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinatorUnitTest.java", - "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerViewUnitTest.java", "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellBuilderUnitTest.java", "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinatorUnitTest.java", "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellViewBinderUnitTest.java", "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellViewUnitTest.java", + "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinatorUnitTest.java", + "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerViewUnitTest.java", "junit/src/org/chromium/chrome/browser/setup_list/SetupListCompletableUnitTest.java", "junit/src/org/chromium/chrome/browser/setup_list/SetupListManagerUnitTest.java", ]
diff --git a/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_bottom_sheet_list_item_view.xml b/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_bottom_sheet_list_item_view.xml index 020bf1c..430c156 100644 --- a/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_bottom_sheet_list_item_view.xml +++ b/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_bottom_sheet_list_item_view.xml
@@ -5,7 +5,7 @@ found in the LICENSE file. --> -<org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipSetupListBottomSheetListItemView +<org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetListItemView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" @@ -62,4 +62,4 @@ app:layout_constraintStart_toEndOf="@id/list_item_description" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"/> -</org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipSetupListBottomSheetListItemView> +</org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetListItemView>
diff --git a/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_see_more_bottom_sheet_layout.xml b/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_see_more_bottom_sheet_layout.xml index e1b96f6..25116bf 100644 --- a/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_see_more_bottom_sheet_layout.xml +++ b/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_see_more_bottom_sheet_layout.xml
@@ -59,7 +59,7 @@ android:gravity="center_horizontal" android:textAppearance="@style/TextAppearance.TextMedium.Secondary"/> - <org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetListContainerView + <org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetListContainerView android:id="@+id/setup_list_bottom_sheet_container_view" android:layout_width="match_parent" android:layout_height="wrap_content"
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinator.java index e945903..80419b35 100644 --- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinator.java +++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinator.java
@@ -31,6 +31,8 @@ import org.chromium.chrome.browser.educational_tip.EducationalTipCardProviderFactory; import org.chromium.chrome.browser.educational_tip.EducationalTipModuleUtils; import org.chromium.chrome.browser.educational_tip.R; +import org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetCoordinator; +import org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetItem; import org.chromium.chrome.browser.magic_stack.ModuleDelegate; import org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType; import org.chromium.chrome.browser.magic_stack.ModuleProvider; @@ -118,9 +120,12 @@ EducationalTipCardProvider item1Provider = mProviders.get(mItem1Type); EducationalTipCardProvider item2Provider = mProviders.get(mItem2Type); - EducationalTipBottomSheetCoordinator educationalTipBottomSheetCoordinator = - getEducationalTipBottomSheetCoordinator(); - mModel.set(SEE_MORE_CLICK_HANDLER, educationalTipBottomSheetCoordinator::showBottomSheet); + EducationalTipSetupListBottomSheetCoordinator + educationalTipSetupListBottomSheetCoordinator = + getEducationalTipSetupListBottomSheetCoordinator(); + mModel.set( + SEE_MORE_CLICK_HANDLER, + educationalTipSetupListBottomSheetCoordinator::showBottomSheet); // Refresh Slot 1 if (item1Provider != null) { @@ -160,23 +165,26 @@ } @NonNull - private EducationalTipBottomSheetCoordinator getEducationalTipBottomSheetCoordinator() { - Supplier<List<EducationalTipBottomSheetItem>> bottomSheetSupplier = + private EducationalTipSetupListBottomSheetCoordinator + getEducationalTipSetupListBottomSheetCoordinator() { + Supplier<List<EducationalTipSetupListBottomSheetItem>> bottomSheetSupplier = () -> { - List<EducationalTipBottomSheetItem> output = new ArrayList<>(); + List<EducationalTipSetupListBottomSheetItem> output = new ArrayList<>(); for (@ModuleType int type : mCurrentRankedModuleTypes) { EducationalTipCardProvider provider = mProviders.get(type); if (provider != null) { SetupListCompletable.CompletionState completionState = SetupListCompletable.getCompletionState(provider, type); output.add( - new EducationalTipBottomSheetItem(provider, completionState)); + new EducationalTipSetupListBottomSheetItem( + provider, completionState)); } } return output; }; - return new EducationalTipBottomSheetCoordinator(mActionDelegate, bottomSheetSupplier); + return new EducationalTipSetupListBottomSheetCoordinator( + mActionDelegate, bottomSheetSupplier); } /**
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetContent.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetContent.java similarity index 87% rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetContent.java rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetContent.java index a540afd0..b347a8e 100644 --- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetContent.java +++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetContent.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.educational_tip.two_cell; +package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet; import android.content.Context; import android.view.View; @@ -14,8 +14,7 @@ /** Bottom sheet content of the educational tip two-cell layout. */ @NullMarked -public class EducationalTipBottomSheetContent implements BottomSheetContent { - // TODO(crbug.com/479597724): Implement BottomSheetContent and add relevant tests. +public class EducationalTipSetupListBottomSheetContent implements BottomSheetContent { private final View mContentView; private final Context mContext; @@ -23,7 +22,7 @@ * @param context Context of the bottom sheet. * @param contentView Main view for the bottom sheet. */ - public EducationalTipBottomSheetContent(Context context, View contentView) { + public EducationalTipSetupListBottomSheetContent(Context context, View contentView) { mContext = context; mContentView = contentView; }
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinator.java similarity index 72% rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinator.java rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinator.java index 5570aec..8002e34 100644 --- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinator.java +++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinator.java
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.educational_tip.two_cell; +package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_TITLE; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_TITLE; import android.content.Context; import android.view.LayoutInflater; @@ -29,21 +29,22 @@ * fetching and preparing the content for the bottom sheet. */ @NullMarked -public class EducationalTipBottomSheetCoordinator { +public class EducationalTipSetupListBottomSheetCoordinator { private final Context mContext; private final BottomSheetContent mBottomSheetContent; private final BottomSheetController mBottomSheetController; private final PropertyModel mModel; - private final Supplier<List<EducationalTipBottomSheetItem>> + private final Supplier<List<EducationalTipSetupListBottomSheetItem>> mRankedEducationalTipProviderSupplier; /** * @param actionDelegate The instance of {@link EducationTipModuleActionDelegate}. */ - public EducationalTipBottomSheetCoordinator( + public EducationalTipSetupListBottomSheetCoordinator( EducationTipModuleActionDelegate actionDelegate, - Supplier<List<EducationalTipBottomSheetItem>> rankedEducationalTipProviderSupplier) { + Supplier<List<EducationalTipSetupListBottomSheetItem>> + rankedEducationalTipProviderSupplier) { mRankedEducationalTipProviderSupplier = rankedEducationalTipProviderSupplier; mContext = actionDelegate.getContext(); mBottomSheetController = actionDelegate.getBottomSheetController(); @@ -52,11 +53,13 @@ .inflate( R.layout.educational_tip_setup_list_see_more_bottom_sheet_layout, /* root= */ null); - mBottomSheetContent = new EducationalTipBottomSheetContent(mContext, contentView); + mBottomSheetContent = new EducationalTipSetupListBottomSheetContent(mContext, contentView); - mModel = new PropertyModel.Builder(EducationalTipBottomSheetProperties.ALL_KEYS).build(); + mModel = + new PropertyModel.Builder(EducationalTipSetupListBottomSheetProperties.ALL_KEYS) + .build(); PropertyModelChangeProcessor.create( - mModel, contentView, EducationalTipBottomSheetViewBinder::bind); + mModel, contentView, EducationalTipSetupListBottomSheetViewBinder::bind); mModel.set( BOTTOM_SHEET_LIST_ITEMS_ON_CLICK, () -> dismissBottomSheet(/* animate= */ false));
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetItem.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetItem.java similarity index 81% rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetItem.java rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetItem.java index 8a06633..0aa22626 100644 --- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetItem.java +++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetItem.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.educational_tip.two_cell; +package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; @@ -11,11 +11,11 @@ /** Data holder for items displayed in the educational tip bottom sheet. */ @NullMarked -public class EducationalTipBottomSheetItem { +public class EducationalTipSetupListBottomSheetItem { public final EducationalTipCardProvider provider; public final SetupListCompletable.@Nullable CompletionState completionState; - public EducationalTipBottomSheetItem( + public EducationalTipSetupListBottomSheetItem( EducationalTipCardProvider provider, SetupListCompletable.@Nullable CompletionState completionState) { this.provider = provider;
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerView.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerView.java similarity index 90% rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerView.java rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerView.java index 7cb41a8..2c7d4df 100644 --- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerView.java +++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerView.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.educational_tip.two_cell; +package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet; import android.content.Context; import android.util.AttributeSet; @@ -23,12 +23,10 @@ * bottom sheet. */ @NullMarked -public class EducationalTipBottomSheetListContainerView extends LinearLayout { - // TODO(crbug.com/479597724): Implement container view. - +public class EducationalTipSetupListBottomSheetListContainerView extends LinearLayout { @Nullable private Runnable mDismissBottomSheetRunnable; - public EducationalTipBottomSheetListContainerView( + public EducationalTipSetupListBottomSheetListContainerView( Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @@ -40,12 +38,13 @@ * @param rankedEducationalTips list of {@link EducationalTipCardProvider} that will be * displayed in the container. */ - public void renderSetUpList(List<EducationalTipBottomSheetItem> rankedEducationalTips) { + public void renderSetUpList( + List<EducationalTipSetupListBottomSheetItem> rankedEducationalTips) { // Clears all previous list items. destroy(); for (int i = 0; i < rankedEducationalTips.size(); i++) { - EducationalTipBottomSheetItem item = rankedEducationalTips.get(i); + EducationalTipSetupListBottomSheetItem item = rankedEducationalTips.get(i); EducationalTipCardProvider educationalTip = item.provider; EducationalTipSetupListBottomSheetListItemView listItemView = (EducationalTipSetupListBottomSheetListItemView) createListItemView();
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipSetupListBottomSheetListItemView.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListItemView.java similarity index 86% rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipSetupListBottomSheetListItemView.java rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListItemView.java index 685d5f80..3e9a8e8 100644 --- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipSetupListBottomSheetListItemView.java +++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListItemView.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.educational_tip.two_cell; +package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet; import android.content.Context; import android.graphics.Paint; @@ -18,8 +18,8 @@ import org.chromium.chrome.browser.setup_list.SetupListModuleUtils; /** - * The list item view within a {@link EducationalTipBottomSheetListContainerView} that is in a - * bottom sheet. + * The list item view within a {@link EducationalTipSetupListBottomSheetListContainerView} that is + * in a bottom sheet. */ @NullMarked public class EducationalTipSetupListBottomSheetListItemView extends ConstraintLayout { @@ -27,7 +27,8 @@ private TextView mTitle; private TextView mDescription; - public EducationalTipSetupListBottomSheetListItemView(Context context, @Nullable AttributeSet attrs) { + public EducationalTipSetupListBottomSheetListItemView( + Context context, @Nullable AttributeSet attrs) { super(context, attrs); }
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetProperties.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetProperties.java similarity index 86% rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetProperties.java rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetProperties.java index c93ba7e3..a936408b 100644 --- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetProperties.java +++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetProperties.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.educational_tip.two_cell; +package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet; import org.chromium.build.annotations.NullMarked; import org.chromium.ui.modelutil.PropertyKey; @@ -12,12 +12,12 @@ /** The properties associated with rendering the educational tip bottom sheet. */ @NullMarked -public class EducationalTipBottomSheetProperties { +public class EducationalTipSetupListBottomSheetProperties { public static final WritableObjectPropertyKey<String> BOTTOM_SHEET_TITLE = new WritableObjectPropertyKey<>(); public static final WritableObjectPropertyKey<String> BOTTOM_SHEET_DESCRIPTION = new WritableObjectPropertyKey<>(); - public static final WritableObjectPropertyKey<List<EducationalTipBottomSheetItem>> + public static final WritableObjectPropertyKey<List<EducationalTipSetupListBottomSheetItem>> BOTTOM_SHEET_LIST_ITEMS = new WritableObjectPropertyKey<>(); public static final WritableObjectPropertyKey<Runnable> BOTTOM_SHEET_LIST_ITEMS_ON_CLICK = new WritableObjectPropertyKey<>();
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetViewBinder.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetViewBinder.java similarity index 71% rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetViewBinder.java rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetViewBinder.java index e69285c..175be99 100644 --- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetViewBinder.java +++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetViewBinder.java
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.educational_tip.two_cell; +package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_TITLE; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_TITLE; import android.view.View; import android.widget.TextView; @@ -19,7 +19,7 @@ /** ViewBinder for a generic two-cell educational tip bottom sheet. */ @NullMarked -public class EducationalTipBottomSheetViewBinder { +public class EducationalTipSetupListBottomSheetViewBinder { public static void bind(PropertyModel model, View view, PropertyKey propertyKey) { if (propertyKey == BOTTOM_SHEET_TITLE) { TextView title = view.findViewById(R.id.setup_list_bottom_sheet_title); @@ -28,11 +28,11 @@ TextView description = view.findViewById(R.id.setup_list_bottom_sheet_description); description.setText(model.get(BOTTOM_SHEET_DESCRIPTION)); } else if (propertyKey == BOTTOM_SHEET_LIST_ITEMS) { - EducationalTipBottomSheetListContainerView listContainerView = + EducationalTipSetupListBottomSheetListContainerView listContainerView = view.findViewById(R.id.setup_list_bottom_sheet_container_view); listContainerView.renderSetUpList(model.get(BOTTOM_SHEET_LIST_ITEMS)); } else if (propertyKey == BOTTOM_SHEET_LIST_ITEMS_ON_CLICK) { - EducationalTipBottomSheetListContainerView listContainerView = + EducationalTipSetupListBottomSheetListContainerView listContainerView = view.findViewById(R.id.setup_list_bottom_sheet_container_view); listContainerView.setDismissBottomSheet(model.get(BOTTOM_SHEET_LIST_ITEMS_ON_CLICK)); }
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinatorUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinatorUnitTest.java similarity index 65% rename from chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinatorUnitTest.java rename to chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinatorUnitTest.java index 459c4e2..b89b726 100644 --- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinatorUnitTest.java +++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinatorUnitTest.java
@@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.educational_tip.two_cell; +package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK; -import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_TITLE; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK; +import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_TITLE; import android.content.Context; @@ -44,29 +44,33 @@ import java.util.List; import java.util.function.Supplier; -/** Unit tests for {@link EducationalTipBottomSheetCoordinator} */ +/** Unit tests for {@link EducationalTipSetupListBottomSheetCoordinator} */ @RunWith(BaseRobolectricTestRunner.class) @Config( manifest = Config.NONE, shadows = {ShadowAppCompatResources.class}) -public class EducationalTipBottomSheetCoordinatorUnitTest { +public class EducationalTipSetupListBottomSheetCoordinatorUnitTest { @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Mock private BottomSheetController mBottomSheetController; @Mock private EducationTipModuleActionDelegate mActionDelegate; - @Mock private Supplier<List<EducationalTipBottomSheetItem>> mEducationalTipCardProviderSupplier; + + @Mock + private Supplier<List<EducationalTipSetupListBottomSheetItem>> + mEducationalTipCardProviderSupplier; private Context mContext; - private List<EducationalTipBottomSheetItem> mListOfEducationalTipBottomSheetItem; + private List<EducationalTipSetupListBottomSheetItem> + mListOfEducationalTipSetupListBottomSheetItem; @Before public void setUp() { mContext = ApplicationProvider.getApplicationContext(); when(mActionDelegate.getContext()).thenReturn(ApplicationProvider.getApplicationContext()); when(mActionDelegate.getBottomSheetController()).thenReturn(mBottomSheetController); - mListOfEducationalTipBottomSheetItem = createListOfEducationalTipBottomSheetItem(); + mListOfEducationalTipSetupListBottomSheetItem = createListOfEducationalTipBottomSheetItem(); when(mEducationalTipCardProviderSupplier.get()) - .thenReturn(mListOfEducationalTipBottomSheetItem); + .thenReturn(mListOfEducationalTipSetupListBottomSheetItem); List<Integer> moduleTypeList = new ArrayList<>(); for (int i = 0; i < 5; i++) { @@ -78,11 +82,12 @@ @Test @SmallTest public void testShowBottomSheet() { - EducationalTipBottomSheetCoordinator educationalTipBottomSheetCoordinator = - new EducationalTipBottomSheetCoordinator( - mActionDelegate, mEducationalTipCardProviderSupplier); - PropertyModel model = educationalTipBottomSheetCoordinator.getModelForTesting(); - educationalTipBottomSheetCoordinator.showBottomSheet(); + EducationalTipSetupListBottomSheetCoordinator + educationalTipSetupListBottomSheetCoordinator = + new EducationalTipSetupListBottomSheetCoordinator( + mActionDelegate, mEducationalTipCardProviderSupplier); + PropertyModel model = educationalTipSetupListBottomSheetCoordinator.getModelForTesting(); + educationalTipSetupListBottomSheetCoordinator.showBottomSheet(); Assert.assertEquals( "Bottom sheet title should be default", @@ -95,20 +100,21 @@ Assert.assertEquals( "Bottom sheet list items should be set", model.get(BOTTOM_SHEET_LIST_ITEMS), - mListOfEducationalTipBottomSheetItem); + mListOfEducationalTipSetupListBottomSheetItem); verify(mBottomSheetController).requestShowContent(any(), /* animate= */ eq(true)); } @Test @SmallTest public void testDismissBottomSheet() { - EducationalTipBottomSheetCoordinator educationalTipBottomSheetCoordinator = - new EducationalTipBottomSheetCoordinator( - mActionDelegate, mEducationalTipCardProviderSupplier); - PropertyModel model = educationalTipBottomSheetCoordinator.getModelForTesting(); + EducationalTipSetupListBottomSheetCoordinator + educationalTipSetupListBottomSheetCoordinator = + new EducationalTipSetupListBottomSheetCoordinator( + mActionDelegate, mEducationalTipCardProviderSupplier); + PropertyModel model = educationalTipSetupListBottomSheetCoordinator.getModelForTesting(); // 1. Verify dismissal with animation. - educationalTipBottomSheetCoordinator.dismissBottomSheet(true); + educationalTipSetupListBottomSheetCoordinator.dismissBottomSheet(true); verify(mBottomSheetController).hideContent(any(), eq(true)); // 2. Verify dismissal without animation (triggered by item click). @@ -118,8 +124,9 @@ verify(mBottomSheetController).hideContent(any(), eq(false)); } - private List<EducationalTipBottomSheetItem> createListOfEducationalTipBottomSheetItem() { - List<EducationalTipBottomSheetItem> output = new ArrayList<>(); + private List<EducationalTipSetupListBottomSheetItem> + createListOfEducationalTipBottomSheetItem() { + List<EducationalTipSetupListBottomSheetItem> output = new ArrayList<>(); for (int i = 0; i < 5; i++) { EducationalTipCardProvider provider = EducationalTipCardProviderFactory.createInstance( @@ -128,7 +135,7 @@ null, mActionDelegate, () -> {}); - output.add(new EducationalTipBottomSheetItem(provider, null)); + output.add(new EducationalTipSetupListBottomSheetItem(provider, null)); } return output; }
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerViewUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerViewUnitTest.java similarity index 69% rename from chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerViewUnitTest.java rename to chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerViewUnitTest.java index ebfa27c..6479d97 100644 --- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerViewUnitTest.java +++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerViewUnitTest.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.educational_tip.two_cell; +package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -31,26 +31,27 @@ import java.util.ArrayList; import java.util.List; -/** Unit tests for {@link EducationalTipBottomSheetListContainerView} */ +/** Unit tests for {@link EducationalTipSetupListBottomSheetListContainerView} */ @RunWith(BaseRobolectricTestRunner.class) @Config( manifest = Config.NONE, shadows = {ShadowAppCompatResources.class}) -public class EducationalTipBottomSheetListContainerViewUnitTest { +public class EducationalTipSetupListBottomSheetListContainerViewUnitTest { private static final int EDUCATIONAL_TIP_MODULES_SIZE = 5; @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Mock private EducationalTipCardProvider mEducationalTipCardProvider; - private EducationalTipBottomSheetListContainerView mContainerView; - private List<EducationalTipBottomSheetItem> mListOfEducationalTipBottomSheetItem; + private EducationalTipSetupListBottomSheetListContainerView mContainerView; + private List<EducationalTipSetupListBottomSheetItem> + mListOfEducationalTipSetupListBottomSheetItem; @Before public void setUp() { Context context = ApplicationProvider.getApplicationContext(); - mContainerView = new EducationalTipBottomSheetListContainerView(context, null); - mListOfEducationalTipBottomSheetItem = createListOfEducationalTipBottomSheetItem(); + mContainerView = new EducationalTipSetupListBottomSheetListContainerView(context, null); + mListOfEducationalTipSetupListBottomSheetItem = createListOfEducationalTipBottomSheetItem(); List<Integer> moduleTypeList = new ArrayList<>(); for (int i = 0; i < 5; i++) { @@ -61,13 +62,13 @@ @Test public void testRenderSetUpList_numberOfListItemsCreated() { - mContainerView.renderSetUpList(mListOfEducationalTipBottomSheetItem); + mContainerView.renderSetUpList(mListOfEducationalTipSetupListBottomSheetItem); Assert.assertEquals( "All educational tip modules should be added", EDUCATIONAL_TIP_MODULES_SIZE, mContainerView.getChildCount()); - mContainerView.renderSetUpList(mListOfEducationalTipBottomSheetItem); + mContainerView.renderSetUpList(mListOfEducationalTipSetupListBottomSheetItem); Assert.assertEquals( "Previous educational tip list items should be destroyed", EDUCATIONAL_TIP_MODULES_SIZE, @@ -77,7 +78,9 @@ @Test public void testEducationalTipCardProviderInRenderSetUpList() { mContainerView.renderSetUpList( - List.of(new EducationalTipBottomSheetItem(mEducationalTipCardProvider, null))); + List.of( + new EducationalTipSetupListBottomSheetItem( + mEducationalTipCardProvider, null))); verify(mEducationalTipCardProvider, times(1)).getCardImage(); verify(mEducationalTipCardProvider, times(1)).getCardTitle(); verify(mEducationalTipCardProvider, times(1)).getCardDescription(); @@ -88,7 +91,9 @@ Runnable mockDismissRunnable = mock(Runnable.class); mContainerView.setDismissBottomSheet(mockDismissRunnable); mContainerView.renderSetUpList( - List.of(new EducationalTipBottomSheetItem(mEducationalTipCardProvider, null))); + List.of( + new EducationalTipSetupListBottomSheetItem( + mEducationalTipCardProvider, null))); mContainerView.getChildAt(0).performClick(); @@ -96,11 +101,12 @@ verify(mockDismissRunnable, times(1)).run(); } - private List<EducationalTipBottomSheetItem> createListOfEducationalTipBottomSheetItem() { - List<EducationalTipBottomSheetItem> output = new ArrayList<>(); + private List<EducationalTipSetupListBottomSheetItem> + createListOfEducationalTipBottomSheetItem() { + List<EducationalTipSetupListBottomSheetItem> output = new ArrayList<>(); for (int i = 0; i < EDUCATIONAL_TIP_MODULES_SIZE; i++) { output.add( - new EducationalTipBottomSheetItem( + new EducationalTipSetupListBottomSheetItem( mock(EducationalTipCardProvider.class), null)); } return output;
diff --git a/chrome/browser/extensions/api/history/history_apitest.cc b/chrome/browser/extensions/api/history/history_apitest.cc index fe3951e..7c6257a 100644 --- a/chrome/browser/extensions/api/history/history_apitest.cc +++ b/chrome/browser/extensions/api/history/history_apitest.cc
@@ -66,12 +66,9 @@ namespace extensions { -using ContextType = extensions::browser_test_util::ContextType; - -class HistoryApiTest : public ExtensionApiTest, - public testing::WithParamInterface<ContextType> { +class HistoryApiTest : public ExtensionApiTest { public: - HistoryApiTest() : ExtensionApiTest(GetParam()) {} + HistoryApiTest() = default; void SetUpOnMainThread() override { ExtensionApiTest::SetUpOnMainThread(); @@ -91,18 +88,6 @@ } }; -// Android only supports MV3 and later, therefore don't need to test for -// persistent background context. -#if !BUILDFLAG(IS_ANDROID) -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - HistoryApiTest, - ::testing::Values(ContextType::kPersistentBackground)); -#endif // !BUILDFLAG(IS_ANDROID) - -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - HistoryApiTest, - ::testing::Values(ContextType::kServiceWorker)); - class HistoryApi404Test : public HistoryApiTest { public: HistoryApi404Test() { @@ -114,18 +99,6 @@ base::test::ScopedFeatureList scoped_feature_list_; }; -// Android only supports Manifest V3 and later, and persistent background -// context is removed in MV3. -#if !BUILDFLAG(IS_ANDROID) -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - HistoryApi404Test, - ::testing::Values(ContextType::kPersistentBackground)); -#endif // !BUILDFLAG(IS_ANDROID) - -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - HistoryApi404Test, - ::testing::Values(ContextType::kServiceWorker)); - class SyncEnabledHistoryApiTest : public HistoryApiTest { public: void SetUpBrowserContextKeyedServices( @@ -163,41 +136,34 @@ const history::HistoryAddPageArgs args_; }; -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - SyncEnabledHistoryApiTest, - ::testing::Values(ContextType::kPersistentBackground)); -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - SyncEnabledHistoryApiTest, - ::testing::Values(ContextType::kServiceWorker)); - -IN_PROC_BROWSER_TEST_P(HistoryApiTest, MiscSearch) { +IN_PROC_BROWSER_TEST_F(HistoryApiTest, MiscSearch) { ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(RunExtensionTest("history/regular/misc_search")) << message_; } -IN_PROC_BROWSER_TEST_P(HistoryApiTest, TimedSearch) { +IN_PROC_BROWSER_TEST_F(HistoryApiTest, TimedSearch) { ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(RunExtensionTest("history/regular/timed_search")) << message_; } -IN_PROC_BROWSER_TEST_P(HistoryApiTest, Delete) { +IN_PROC_BROWSER_TEST_F(HistoryApiTest, Delete) { ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(RunExtensionTest("history/regular/delete")) << message_; } -IN_PROC_BROWSER_TEST_P(HistoryApiTest, DeleteProhibited) { +IN_PROC_BROWSER_TEST_F(HistoryApiTest, DeleteProhibited) { profile()->GetPrefs()->SetBoolean(prefs::kAllowDeletingBrowserHistory, false); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(RunExtensionTest("history/regular/delete_prohibited")) << message_; } -IN_PROC_BROWSER_TEST_P(HistoryApiTest, GetVisits) { +IN_PROC_BROWSER_TEST_F(HistoryApiTest, GetVisits) { ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(RunExtensionTest("history/regular/get_visits")) << message_; } -IN_PROC_BROWSER_TEST_P(HistoryApi404Test, GetVisits_Excludes404Visits) { +IN_PROC_BROWSER_TEST_F(HistoryApi404Test, GetVisits_Excludes404Visits) { ASSERT_TRUE(StartEmbeddedTestServer()); history::HistoryService* history_service = HistoryServiceFactory::GetForProfile(profile(), @@ -237,11 +203,10 @@ R"({ "name": "chrome.history", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "permissions": ["history"], "background": { - "scripts": ["get_visits_404.js"], - "persistent": true + "service_worker": "get_visits_404.js" } })"; static constexpr char kBackgroundJs[] = @@ -271,7 +236,7 @@ ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_; } -IN_PROC_BROWSER_TEST_P(SyncEnabledHistoryApiTest, GetVisits_Foreign) { +IN_PROC_BROWSER_TEST_F(SyncEnabledHistoryApiTest, GetVisits_Foreign) { ASSERT_TRUE(StartEmbeddedTestServer()); // Setup: Add a foreign (aka synced) history entry to the DB. @@ -299,11 +264,10 @@ R"({ "name": "chrome.history", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "permissions": ["history"], "background": { - "scripts": ["get_visits_foreign.js"], - "persistent": true + "service_worker": "get_visits_foreign.js" } })"; static constexpr char kBackgroundJs[] = @@ -334,7 +298,7 @@ ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_; } -IN_PROC_BROWSER_TEST_P(SyncEnabledHistoryApiTest, SearchIncludesActorVisits) { +IN_PROC_BROWSER_TEST_F(SyncEnabledHistoryApiTest, SearchIncludesActorVisits) { ASSERT_TRUE(StartEmbeddedTestServer()); history::HistoryService* history_service = @@ -363,11 +327,10 @@ R"({ "name": "chrome.history.actor", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "permissions": ["history"], "background": { - "scripts": ["search_actor.js"], - "persistent": true + "service_worker": "search_actor.js" } })"; static constexpr char kBackgroundJs[] = @@ -389,14 +352,14 @@ ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_; } -IN_PROC_BROWSER_TEST_P(HistoryApiTest, SearchAfterAdd) { +IN_PROC_BROWSER_TEST_F(HistoryApiTest, SearchAfterAdd) { ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(RunExtensionTest("history/regular/search_after_add")) << message_; } // Test when History API is used from incognito mode, it has access to the // regular mode history and actual incognito navigation has no effect on it. -IN_PROC_BROWSER_TEST_P(HistoryApiTest, Incognito) { +IN_PROC_BROWSER_TEST_F(HistoryApiTest, Incognito) { // TODO(crbug.com/40937027): Convert test to use HTTPS and then remove. ScopedAllowHttpForHostnamesForTesting allow_http({"www.b.com"}, profile()->GetPrefs());
diff --git a/chrome/browser/extensions/api/management/management_api_browsertest.cc b/chrome/browser/extensions/api/management/management_api_browsertest.cc index 642056d..739603f 100644 --- a/chrome/browser/extensions/api/management/management_api_browsertest.cc +++ b/chrome/browser/extensions/api/management/management_api_browsertest.cc
@@ -68,9 +68,7 @@ class ExtensionManagementApiBrowserTest : public ExtensionBrowserTest { public: - explicit ExtensionManagementApiBrowserTest( - ContextType context_type = ContextType::kNone) - : ExtensionBrowserTest(context_type) {} + ExtensionManagementApiBrowserTest() = default; ~ExtensionManagementApiBrowserTest() override = default; ExtensionManagementApiBrowserTest(const ExtensionManagementApiBrowserTest&) = delete; @@ -93,14 +91,10 @@ ScopedInstallVerifierBypassForTest install_verifier_bypass_; }; -using ContextType = extensions::browser_test_util::ContextType; - class ExtensionManagementApiTestWithBackgroundType - : public ExtensionManagementApiBrowserTest, - public ::testing::WithParamInterface<ContextType> { + : public ExtensionManagementApiBrowserTest { public: - ExtensionManagementApiTestWithBackgroundType() - : ExtensionManagementApiBrowserTest(GetParam()) { + ExtensionManagementApiTestWithBackgroundType() { #if !BUILDFLAG(IS_ANDROID) // Android does not support Chrome apps and does not have access to the // variable g_enable_chrome_apps_for_testing. @@ -125,20 +119,9 @@ #endif }; -#if !BUILDFLAG(IS_ANDROID) -// Android does not support persistent background pages. -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - ExtensionManagementApiTestWithBackgroundType, - ::testing::Values(ContextType::kPersistentBackground)); -#endif // !BUILDFLAG(IS_ANDROID) - -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - ExtensionManagementApiTestWithBackgroundType, - ::testing::Values(ContextType::kServiceWorker)); - // We test this here instead of in an ExtensionApiTest because normal extensions // are not allowed to call the install function. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType, +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType, InstallEvent) { ExtensionTestMessageListener listener1("ready"); ASSERT_TRUE( @@ -154,7 +137,7 @@ #if !BUILDFLAG(IS_ANDROID) // Android does not support Chrome apps. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType, +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType, LaunchApp) { ExtensionTestMessageListener listener1("app_launched"); ExtensionTestMessageListener listener2("got_expected_error"); @@ -171,7 +154,7 @@ } // Android does not support Chrome apps. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType, +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType, NoLaunchAppDeprecated) { extensions::testing::g_enable_chrome_apps_for_testing = false; const Extension* packaged_app = @@ -194,7 +177,7 @@ } // Android does not support Chrome apps. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType, +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType, LaunchAppFromBackground) { ExtensionTestMessageListener listener1("success"); ASSERT_TRUE( @@ -206,7 +189,7 @@ } // Android does not support Chrome apps. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType, +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType, NoLaunchAppFromBackgroundDeprecated) { extensions::testing::g_enable_chrome_apps_for_testing = false; const Extension* packaged_app = @@ -234,7 +217,7 @@ } #endif // !BUILDFLAG(IS_ANDROID) -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType, +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType, SelfUninstall) { // Wait for the helper script to finish before loading the primary // extension. This ensures that the onUninstall event listener is @@ -249,7 +232,7 @@ ASSERT_TRUE(listener2.WaitUntilSatisfied()); } -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType, +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType, SelfUninstallNoPermissions) { // Wait for the helper script to finish before loading the primary // extension. This ensures that the onUninstall event listener is @@ -264,7 +247,7 @@ ASSERT_TRUE(listener2.WaitUntilSatisfied()); } -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType, Get) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType, Get) { ExtensionTestMessageListener listener("success"); ASSERT_TRUE( LoadExtension(test_data_dir_.AppendASCII("management/simple_extension"), @@ -273,7 +256,7 @@ ASSERT_TRUE(listener.WaitUntilSatisfied()); } -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType, +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType, GetSelfNoPermissions) { ExtensionTestMessageListener listener1("success"); ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("management/get_self")));
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc index dae1080..d2f8009d 100644 --- a/chrome/browser/extensions/api/management/management_apitest.cc +++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -72,10 +72,9 @@ R"({ "name": "Management API Test", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "background": { - "scripts": ["background.js"], - "persistent": true + "service_worker": "background.js" }, "replacement_web_app": "%s" })"; @@ -91,13 +90,9 @@ } // namespace -using ContextType = extensions::browser_test_util::ContextType; - -class ExtensionManagementApiTest - : public extensions::ExtensionApiTest, - public testing::WithParamInterface<ContextType> { +class ExtensionManagementApiTest : public extensions::ExtensionApiTest { public: - ExtensionManagementApiTest() : ExtensionApiTest(GetParam()) { + ExtensionManagementApiTest() { #if !BUILDFLAG(IS_ANDROID) enable_chrome_apps_ = std::make_unique<base::AutoReset<bool>>( &extensions::testing::g_enable_chrome_apps_for_testing, true); @@ -176,18 +171,7 @@ #endif }; -#if !BUILDFLAG(IS_ANDROID) -// Android does not support persistent background pages. -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - ExtensionManagementApiTest, - ::testing::Values(ContextType::kPersistentBackground)); -#endif // !BUILDFLAG(IS_ANDROID) - -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - ExtensionManagementApiTest, - ::testing::Values(ContextType::kServiceWorker)); - -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, Basics) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, Basics) { #if !BUILDFLAG(IS_ANDROID) // Android does not provide the XmlUnitTestResultPrinter this method needs. base::AddFeatureIdTagToTestResult( @@ -214,12 +198,12 @@ #else #define MAYBE_NoPermission NoPermission #endif -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, MAYBE_NoPermission) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, MAYBE_NoPermission) { LoadExtensions(); ASSERT_TRUE(RunExtensionTest("management/no_permission")); } -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, Uninstall) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, Uninstall) { LoadExtensions(); // Confirmation dialog will be shown for uninstallations except for self. extensions::ScopedTestDialogAutoConfirm auto_confirm( @@ -229,7 +213,7 @@ #if !BUILDFLAG(IS_ANDROID) // Skipped on Android because it does not support Chrome apps. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, CreateAppShortcut) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, CreateAppShortcut) { LoadExtensions(); base::FilePath basedir = test_data_dir_.AppendASCII("management"); LoadNamedExtension(basedir, "packaged_app"); @@ -239,7 +223,7 @@ } // Skipped on Android because it does not support Chrome apps. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, GenerateAppForLink) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, GenerateAppForLink) { web_app::test::WaitUntilReady(web_app::WebAppProvider::GetForTest(profile())); ASSERT_TRUE(RunExtensionTest("management/generate_app_for_link")); } @@ -331,14 +315,7 @@ net::EmbeddedTestServer https_test_server_; }; -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - InstallReplacementWebAppApiTest, - ::testing::Values(ContextType::kPersistentBackground)); -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - InstallReplacementWebAppApiTest, - ::testing::Values(ContextType::kServiceWorker)); - -IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, NotWebstore) { +IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, NotWebstore) { static constexpr char kBackground[] = R"( chrome.management.installReplacementWebApp(function() { chrome.test.assertLastError( @@ -352,7 +329,7 @@ kBackground, false /* from_webstore */); } -IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, NoGesture) { +IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, NoGesture) { static constexpr char kBackground[] = R"( chrome.management.installReplacementWebApp(function() { chrome.test.assertLastError( @@ -366,7 +343,7 @@ kBackground, true /* from_webstore */); } -IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, NotInstallableWebApp) { +IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, NotInstallableWebApp) { static constexpr char kBackground[] = R"(chrome.test.runWithUserGesture(function() { chrome.management.installReplacementWebApp(function() { @@ -381,7 +358,7 @@ kBackground, true /* from_webstore */); } -IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, InstallableWebApp) { +IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, InstallableWebApp) { static constexpr char kGoodWebAppURL[] = "/management/install_replacement_web_app/acceptable_web_app/index.html"; @@ -390,7 +367,7 @@ // Check that web app still installs and launches correctly when start_url does // not match replacement_web_app_url. -IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, +IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, InstallableWebAppWithStartUrl) { static constexpr char kGoodWebAppUrl[] = "/management/install_replacement_web_app/" @@ -404,7 +381,7 @@ RunInstallableWebAppTest(kManifest, kGoodWebAppUrl, kGoodWebAppStartUrl); } -IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, +IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, InstallableWebAppInPlatformApp) { static constexpr char kAppManifest[] = R"({ @@ -424,7 +401,7 @@ #endif // BUILDFLAG(ENABLE_EXTENSIONS) // Tests actions on extensions when no management policy is in place. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, ManagementPolicyAllowed) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, ManagementPolicyAllowed) { LoadExtensions(); extensions::ScopedTestDialogAutoConfirm auto_confirm( extensions::ScopedTestDialogAutoConfirm::ACCEPT); @@ -447,7 +424,7 @@ } // Tests actions on extensions when management policy prohibits those actions. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, ManagementPolicyProhibited) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, ManagementPolicyProhibited) { LoadExtensions(); extensions::ExtensionRegistry* registry = extensions::ExtensionRegistry::Get(profile()); @@ -469,7 +446,7 @@ #if !BUILDFLAG(IS_ANDROID) // Skipped on Android because it does not support Chrome apps. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, LaunchPanelApp) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, LaunchPanelApp) { // Load an extension that calls launchApp() on any app that gets // installed. ExtensionTestMessageListener launcher_loaded("launcher loaded"); @@ -525,7 +502,7 @@ } // Skipped on Android because it does not support Chrome apps. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, LaunchTabApp) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, LaunchTabApp) { // Load an extension that calls launchApp() on any app that gets // installed. ExtensionTestMessageListener launcher_loaded("launcher loaded"); @@ -579,7 +556,7 @@ } // Skipped on Android because it does not support Chrome apps. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, NoLaunchPanelAppsDeprecated) { extensions::testing::g_enable_chrome_apps_for_testing = false; // Load an extension that calls launchApp() on any app that gets @@ -609,7 +586,7 @@ } // Skipped on Android because it does not support Chrome apps. -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, NoLaunchTabAppDeprecated) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, NoLaunchTabAppDeprecated) { extensions::testing::g_enable_chrome_apps_for_testing = false; // Load an extension that calls launchApp() on any app that gets // installed. @@ -649,7 +626,7 @@ #else #define MAYBE_LaunchType LaunchType #endif -IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, MAYBE_LaunchType) { +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, MAYBE_LaunchType) { LoadExtensions(); base::FilePath basedir = test_data_dir_.AppendASCII("management"); LoadNamedExtension(basedir, "packaged_app");
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc b/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc index 7487061..739f849e 100644 --- a/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc +++ b/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
@@ -43,17 +43,12 @@ namespace extensions { namespace { -using ContextType = extensions::browser_test_util::ContextType; - -class NativeMessagingApiTestBase : public ExtensionApiTest { +class NativeMessagingApiTest : public ExtensionApiTest { public: - explicit NativeMessagingApiTestBase( - ContextType context_type = ContextType::kNone) - : ExtensionApiTest(context_type) {} - ~NativeMessagingApiTestBase() override = default; - NativeMessagingApiTestBase(const NativeMessagingApiTestBase&) = delete; - NativeMessagingApiTestBase& operator=(const NativeMessagingApiTestBase&) = - delete; + NativeMessagingApiTest() = default; + ~NativeMessagingApiTest() override = default; + NativeMessagingApiTest(const NativeMessagingApiTest&) = delete; + NativeMessagingApiTest& operator=(const NativeMessagingApiTest&) = delete; protected: size_t GetTotalTabCount() const { @@ -69,13 +64,13 @@ // Tests basic functionality of chrome.runtime.sendNativeMessage in an MV3 // extension. -IN_PROC_BROWSER_TEST_F(NativeMessagingApiTestBase, SendNativeMessage) { +IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, SendNativeMessage) { constexpr bool kUserLevel = false; ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(kUserLevel)); ASSERT_TRUE(RunExtensionTest("native_messaging_send_native_message")); } -IN_PROC_BROWSER_TEST_F(NativeMessagingApiTestBase, UserLevelSendNativeMessage) { +IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, UserLevelSendNativeMessage) { constexpr bool kUserLevel = true; ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(kUserLevel)); ASSERT_TRUE(RunExtensionTest("native_messaging_send_native_message")); @@ -85,7 +80,7 @@ // On Windows, a new codepath is used to directly launch .EXE-based Native // Hosts. This codepath allows launching of Native Hosts even when cmd.exe is // disabled or misconfigured. -class NativeMessagingLaunchExeTest : public NativeMessagingApiTestBase, +class NativeMessagingLaunchExeTest : public NativeMessagingApiTest, public testing::WithParamInterface<bool> { public: NativeMessagingLaunchExeTest() { @@ -134,55 +129,27 @@ } #endif -class NativeMessagingApiTest : public NativeMessagingApiTestBase, - public testing::WithParamInterface<ContextType> { - public: - NativeMessagingApiTest() : NativeMessagingApiTestBase(GetParam()) {} - ~NativeMessagingApiTest() override = default; - NativeMessagingApiTest(const NativeMessagingApiTest&) = delete; - NativeMessagingApiTest& operator=(const NativeMessagingApiTest&) = delete; - - protected: - bool RunTest(const char* extension_name) { - if (GetParam() == ContextType::kPersistentBackground) { - return RunExtensionTest(extension_name); - } - std::string lazy_extension_name = base::StrCat({extension_name, "/lazy"}); - return RunExtensionTest(lazy_extension_name.c_str()); - } -}; - -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - NativeMessagingApiTest, - ::testing::Values(ContextType::kPersistentBackground)); -INSTANTIATE_TEST_SUITE_P(EventPage, - NativeMessagingApiTest, - ::testing::Values(ContextType::kEventPage)); -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - NativeMessagingApiTest, - ::testing::Values(ContextType::kServiceWorker)); - // Tests chrome.runtime.sendNativeMessage to a native messaging host. -IN_PROC_BROWSER_TEST_P(NativeMessagingApiTest, NativeMessagingBasic) { +IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, NativeMessagingBasic) { ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(false)); - ASSERT_TRUE(RunTest("native_messaging")) << message_; + ASSERT_TRUE(RunExtensionTest("native_messaging")) << message_; } -IN_PROC_BROWSER_TEST_P(NativeMessagingApiTest, UserLevelNativeMessaging) { +IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, UserLevelNativeMessaging) { ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(true)); - ASSERT_TRUE(RunTest("native_messaging")) << message_; + ASSERT_TRUE(RunExtensionTest("native_messaging")) << message_; } // Tests chrome.runtime.connectNative to a native messaging host. -IN_PROC_BROWSER_TEST_P(NativeMessagingApiTest, ConnectNative) { +IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, ConnectNative) { ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(false)); - ASSERT_TRUE(RunTest("native_messaging_connect")) << message_; + ASSERT_TRUE(RunExtensionTest("native_messaging_connect")) << message_; } -IN_PROC_BROWSER_TEST_P(NativeMessagingApiTest, +IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, UserLevelNativeMessagingConnectNative) { ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(true)); - ASSERT_TRUE(RunTest("native_messaging_connect")) << message_; + ASSERT_TRUE(RunExtensionTest("native_messaging_connect")) << message_; } #if !BUILDFLAG(IS_CHROMEOS) @@ -204,7 +171,7 @@ return command_line; } -class NativeMessagingLaunchApiTest : public NativeMessagingApiTestBase { +class NativeMessagingLaunchApiTest : public NativeMessagingApiTest { public: NativeMessagingLaunchApiTest() { feature_list_.InitAndEnableFeature(features::kOnConnectNative); @@ -494,7 +461,7 @@ #if BUILDFLAG(IS_WIN) class NativeHostExecutablesLaunchDirectlyPolicyTest - : public extensions::NativeMessagingApiTestBase, + : public extensions::NativeMessagingApiTest, public testing::WithParamInterface<bool> { public: NativeHostExecutablesLaunchDirectlyPolicyTest() {
diff --git a/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc b/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc index 25c6d24..05a906fd 100644 --- a/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc +++ b/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc
@@ -149,27 +149,15 @@ } // namespace -using ContextType = extensions::browser_test_util::ContextType; - -class ExtensionMetricsApiTest - : public ExtensionApiTest, - public testing::WithParamInterface<ContextType> { +class ExtensionMetricsApiTest : public ExtensionApiTest { public: - ExtensionMetricsApiTest() : ExtensionApiTest(GetParam()) {} + ExtensionMetricsApiTest() = default; ~ExtensionMetricsApiTest() override = default; ExtensionMetricsApiTest(const ExtensionMetricsApiTest&) = delete; ExtensionMetricsApiTest& operator=(const ExtensionMetricsApiTest&) = delete; }; -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - ExtensionMetricsApiTest, - ::testing::Values(ContextType::kPersistentBackground)); - -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - ExtensionMetricsApiTest, - ::testing::Values(ContextType::kServiceWorker)); - -IN_PROC_BROWSER_TEST_P(ExtensionMetricsApiTest, Metrics) { +IN_PROC_BROWSER_TEST_F(ExtensionMetricsApiTest, Metrics) { ukm::TestAutoSetUkmRecorder ukm_recorder; base::UserActionTester user_action_tester;
diff --git a/chrome/browser/extensions/api/module/module_apitest.cc b/chrome/browser/extensions/api/module/module_apitest.cc index 18c9158..63d3f1f 100644 --- a/chrome/browser/extensions/api/module/module_apitest.cc +++ b/chrome/browser/extensions/api/module/module_apitest.cc
@@ -14,47 +14,34 @@ namespace { -using ContextType = extensions::browser_test_util::ContextType; - -class ExtensionModuleApiTest : public ExtensionApiTest, - public testing::WithParamInterface<ContextType> { +class ExtensionModuleApiTest : public ExtensionApiTest { public: - ExtensionModuleApiTest() : ExtensionApiTest(GetParam()) {} + ExtensionModuleApiTest() = default; ~ExtensionModuleApiTest() override = default; ExtensionModuleApiTest(const ExtensionModuleApiTest&) = delete; ExtensionModuleApiTest& operator=(const ExtensionModuleApiTest&) = delete; }; -// Android only supports service worker. -#if !BUILDFLAG(IS_ANDROID) -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - ExtensionModuleApiTest, - ::testing::Values(ContextType::kPersistentBackground)); -#endif -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - ExtensionModuleApiTest, - ::testing::Values(ContextType::kServiceWorker)); - } // namespace -IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, CognitoFile) { +IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, CognitoFile) { ASSERT_TRUE(RunExtensionTest("extension_module/cognito_file", {}, {.allow_file_access = true})) << message_; } -IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, IncognitoFile) { +IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, IncognitoFile) { ASSERT_TRUE( RunExtensionTest("extension_module/incognito_file", {}, {.allow_in_incognito = true, .allow_file_access = true})) << message_; } -IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, CognitoNoFile) { +IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, CognitoNoFile) { ASSERT_TRUE(RunExtensionTest("extension_module/cognito_nofile")) << message_; } -IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, IncognitoNoFile) { +IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, IncognitoNoFile) { ASSERT_TRUE(RunExtensionTest("extension_module/incognito_nofile", {}, {.allow_in_incognito = true})) << message_;
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc index f7b1754..783a107 100644 --- a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc +++ b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
@@ -123,13 +123,10 @@ } } -using ContextType = browser_test_util::ContextType; - -class OmniboxApiTestBase : public ExtensionApiTest { +class OmniboxApiTest : public ExtensionApiTest { public: - explicit OmniboxApiTestBase(ContextType context_type = ContextType::kNone) - : ExtensionApiTest(context_type) {} - ~OmniboxApiTestBase() override = default; + OmniboxApiTest() = default; + ~OmniboxApiTest() override = default; void SetUpOnMainThread() override { ExtensionApiTest::SetUpOnMainThread(); @@ -172,30 +169,6 @@ #endif // BUILDFLAG(IS_ANDROID) }; -class OmniboxApiTest : public OmniboxApiTestBase, - public testing::WithParamInterface<ContextType> { - public: - OmniboxApiTest() : OmniboxApiTestBase(GetParam()) {} - ~OmniboxApiTest() override = default; -}; - -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - OmniboxApiTest, - testing::Values(ContextType::kServiceWorker)); - -// Desktop Android only supports service worker. -#if !BUILDFLAG(IS_ANDROID) -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - OmniboxApiTest, - testing::Values(ContextType::kPersistentBackground)); - -using OmniboxApiBackgroundPageTest = OmniboxApiTest; - -INSTANTIATE_TEST_SUITE_P(All, - OmniboxApiBackgroundPageTest, - testing::Values(ContextType::kNone)); -#endif // !BUILDFLAG(IS_ANDROID) - } // namespace // TODO(crbug.com/326903502): Flaky on TSan. @@ -204,14 +177,14 @@ #else #define MAYBE_SendSuggestions SendSuggestions #endif -IN_PROC_BROWSER_TEST_P(OmniboxApiTest, MAYBE_SendSuggestions) { +IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_SendSuggestions) { constexpr char kManifest[] = R"({ "name": "Basic Send Suggestions", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "omnibox": { "keyword": "alpha" }, - "background": { "scripts": [ "background.js" ], "persistent": true } + "background": { "service_worker": "background.js" } })"; constexpr char kBackground[] = R"(chrome.omnibox.onInputChanged.addListener((text, suggest) => { @@ -329,14 +302,14 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) // TODO(crbug.com/405219624): Port these tests to desktop Android. Most require // access to the Views location bar, which is not available on Android. -IN_PROC_BROWSER_TEST_P(OmniboxApiTest, OnInputEntered) { +IN_PROC_BROWSER_TEST_F(OmniboxApiTest, OnInputEntered) { constexpr char kManifest[] = R"({ "name": "Basic Send Suggestions", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "omnibox": { "keyword": "alpha" }, - "background": { "scripts": [ "background.js" ], "persistent": true } + "background": { "service_worker": "background.js" } })"; // This extension will collect input entered into the omnibox and pass it // to the browser when instructed. @@ -388,15 +361,15 @@ // Tests receiving suggestions from and sending input to the incognito context // of an incognito split mode extension. // Regression test for https://crbug.com/40100987. -IN_PROC_BROWSER_TEST_P(OmniboxApiTest, IncognitoSplitMode) { +IN_PROC_BROWSER_TEST_F(OmniboxApiTest, IncognitoSplitMode) { static constexpr char kManifest[] = R"({ "name": "SetDefaultSuggestion", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "omnibox": { "keyword": "alpha" }, "incognito": "split", - "background": { "scripts": [ "background.js" ], "persistent": true } + "background": { "service_worker": "background.js" } })"; static constexpr char kBackground[] = R"(let suggestionSuffix = @@ -506,7 +479,7 @@ // Tests that the autocomplete popup doesn't reopen after accepting input for // a given query. // http://crbug.com/88552 -IN_PROC_BROWSER_TEST_P(OmniboxApiBackgroundPageTest, MAYBE_PopupStaysClosed) { +IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_PopupStaysClosed) { ASSERT_TRUE(RunExtensionTest("omnibox")) << message_; LocationBar* location_bar = GetLocationBar(browser()); @@ -550,14 +523,14 @@ #else #define MAYBE_DeleteOmniboxSuggestionResult DeleteOmniboxSuggestionResult #endif -IN_PROC_BROWSER_TEST_P(OmniboxApiTest, MAYBE_DeleteOmniboxSuggestionResult) { +IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_DeleteOmniboxSuggestionResult) { static constexpr char kManifest[] = R"({ "name": "Basic Send Suggestions", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "omnibox": { "keyword": "alpha" }, - "background": { "scripts": [ "background.js" ], "persistent": true } + "background": { "service_worker": "background.js" } })"; static constexpr char kBackground[] = R"(chrome.omnibox.onInputChanged.addListener((text, suggest) => { @@ -659,15 +632,15 @@ // Tests that if the user hits "backspace" (leaving the extension keyword mode), // the extension suggestions are not sent. // TODO(crbug.com/40839815): Flaky. -IN_PROC_BROWSER_TEST_P(OmniboxApiTest, +IN_PROC_BROWSER_TEST_F(OmniboxApiTest, DISABLED_ExtensionSuggestionsOnlyInKeywordMode) { static constexpr char kManifest[] = R"({ "name": "Basic Send Suggestions", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "omnibox": { "keyword": "kw" }, - "background": { "scripts": [ "background.js" ], "persistent": true } + "background": { "service_worker": "background.js" } })"; static constexpr char kBackground[] = R"(chrome.omnibox.onInputChanged.addListener((text, suggest) => { @@ -751,14 +724,14 @@ } #endif // BUILDFLAG(ENABLE_EXTENSIONS) -IN_PROC_BROWSER_TEST_P(OmniboxApiTest, SetDefaultSuggestionFailures) { +IN_PROC_BROWSER_TEST_F(OmniboxApiTest, SetDefaultSuggestionFailures) { constexpr char kManifest[] = R"({ "name": "SetDefaultSuggestion", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "omnibox": { "keyword": "word" }, - "background": { "scripts": [ "background.js" ], "persistent": true } + "background": { "service_worker": "background.js" } })"; constexpr char kBackground[] = R"(chrome.test.runTests([ @@ -820,14 +793,14 @@ #else #define MAYBE_SetDefaultSuggestion SetDefaultSuggestion #endif -IN_PROC_BROWSER_TEST_P(OmniboxApiTest, MAYBE_SetDefaultSuggestion) { +IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_SetDefaultSuggestion) { constexpr char kManifest[] = R"({ "name": "SetDefaultSuggestion", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "omnibox": { "keyword": "word" }, - "background": { "scripts": [ "background.js" ], "persistent": true } + "background": { "service_worker": "background.js" } })"; constexpr char kBackground[] = R"(chrome.test.runTests([ @@ -888,14 +861,14 @@ #else #define MAYBE_PassEmptySuggestions PassEmptySuggestions #endif -IN_PROC_BROWSER_TEST_P(OmniboxApiTest, MAYBE_PassEmptySuggestions) { +IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_PassEmptySuggestions) { static constexpr char kManifest[] = R"({ "name": "Basic Send Suggestions", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "omnibox": { "keyword": "alpha" }, - "background": { "scripts": [ "background.js" ], "persistent": true } + "background": { "service_worker": "background.js" } })"; // Register a listener that passes back empty suggestions if there is no // text content. @@ -965,7 +938,7 @@ } #endif // BUILDFLAG(ENABLE_EXTENSIONS) -class UnscopedOmniboxApiTest : public OmniboxApiTestBase { +class UnscopedOmniboxApiTest : public OmniboxApiTest { public: UnscopedOmniboxApiTest() { // TODO(crbug.com/441102004): Update UnscopedExtensionZeroSuggest to support @@ -983,7 +956,7 @@ private: void SetUpOnMainThread() override { - OmniboxApiTestBase::SetUpOnMainThread(); + OmniboxApiTest::SetUpOnMainThread(); // Prevent the stop timer from killing the hints fetch early, which might // cause test flakiness due to timeout. SetStopTimerDuration(base::Seconds(30));
diff --git a/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc b/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc index dd5a498..a6dcdf3 100644 --- a/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc +++ b/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc
@@ -74,15 +74,14 @@ } // Tests for chrome.printerProvider API. -class PrinterProviderApiTest : public ExtensionApiTest, - public testing::WithParamInterface<ContextType> { +class PrinterProviderApiTest : public ExtensionApiTest { public: enum PrintRequestDataType { PRINT_REQUEST_DATA_TYPE_NOT_SET, PRINT_REQUEST_DATA_TYPE_BYTES }; - PrinterProviderApiTest() : ExtensionApiTest(GetParam()) {} + PrinterProviderApiTest() = default; ~PrinterProviderApiTest() override = default; PrinterProviderApiTest(const PrinterProviderApiTest&) = delete; PrinterProviderApiTest& operator=(const PrinterProviderApiTest&) = delete; @@ -254,44 +253,37 @@ } }; -INSTANTIATE_TEST_SUITE_P(EventPage, - PrinterProviderApiTest, - ::testing::Values(ContextType::kEventPage)); -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - PrinterProviderApiTest, - ::testing::Values(ContextType::kServiceWorker)); - -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, PrintJobSuccess) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintJobSuccess) { RunPrintRequestTestExtension("OK", PRINT_REQUEST_DATA_TYPE_BYTES, "OK"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, PrintJobAsyncSuccess) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintJobAsyncSuccess) { RunPrintRequestTestExtension("ASYNC_RESPONSE", PRINT_REQUEST_DATA_TYPE_BYTES, "OK"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, PrintJobFailed) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintJobFailed) { RunPrintRequestTestExtension("INVALID_TICKET", PRINT_REQUEST_DATA_TYPE_BYTES, "INVALID_TICKET"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, NoPrintEventListener) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, NoPrintEventListener) { RunPrintRequestTestExtension("NO_LISTENER", PRINT_REQUEST_DATA_TYPE_BYTES, "FAILED"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintRequestInvalidCallbackParam) { RunPrintRequestTestExtension("INVALID_VALUE", PRINT_REQUEST_DATA_TYPE_BYTES, "FAILED"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, PrintRequestDataNotSet) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintRequestDataNotSet) { RunPrintRequestTestExtension("IGNORE_CALLBACK", PRINT_REQUEST_DATA_TYPE_NOT_SET, "FAILED"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, PrintRequestExtensionUnloaded) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintRequestExtensionUnloaded) { ResultCatcher catcher; ExtensionId extension_id; @@ -317,28 +309,28 @@ EXPECT_EQ("FAILED", status); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetCapabilitySuccess) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetCapabilitySuccess) { RunPrinterCapabilitiesRequestTest("OK", "{\"capability\":\"value\"}"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetCapabilityAsyncSuccess) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetCapabilityAsyncSuccess) { RunPrinterCapabilitiesRequestTest("ASYNC_RESPONSE", "{\"capability\":\"value\"}"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, EmptyCapability) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, EmptyCapability) { RunPrinterCapabilitiesRequestTest("EMPTY", "{}"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, NoCapabilityEventListener) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, NoCapabilityEventListener) { RunPrinterCapabilitiesRequestTest("NO_LISTENER", "{}"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, CapabilityInvalidValue) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, CapabilityInvalidValue) { RunPrinterCapabilitiesRequestTest("INVALID_VALUE", "{}"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetCapabilityExtensionUnloaded) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetCapabilityExtensionUnloaded) { ResultCatcher catcher; ExtensionId extension_id; @@ -356,7 +348,7 @@ EXPECT_EQ("{}", *result); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersSuccess) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersSuccess) { ResultCatcher catcher; ExtensionId extension_id; @@ -391,7 +383,7 @@ ValidatePrinterListValue(printers, expected_printers); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersAsyncSuccess) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersAsyncSuccess) { ResultCatcher catcher; ExtensionId extension_id; @@ -419,7 +411,7 @@ ValidatePrinterListValue(printers, expected_printers); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersTwoExtensions) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersTwoExtensions) { ResultCatcher catcher; ExtensionId extension_id_1; @@ -474,7 +466,7 @@ ValidatePrinterListValue(printers, expected_printers); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersTwoExtensionsBothUnloaded) { ResultCatcher catcher; @@ -504,7 +496,7 @@ EXPECT_TRUE(printers.empty()); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersTwoExtensionsOneFails) { ResultCatcher catcher; @@ -546,7 +538,7 @@ ValidatePrinterListValue(printers, expected_printers); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersTwoExtensionsOneWithNoListener) { ResultCatcher catcher; @@ -588,7 +580,7 @@ ValidatePrinterListValue(printers, expected_printers); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersNoListener) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersNoListener) { ResultCatcher catcher; ExtensionId extension_id; @@ -607,7 +599,7 @@ EXPECT_TRUE(printers.empty()); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersNotArray) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersNotArray) { ResultCatcher catcher; ExtensionId extension_id; @@ -626,7 +618,7 @@ EXPECT_TRUE(printers.empty()); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersInvalidPrinterValueType) { ResultCatcher catcher; @@ -646,7 +638,7 @@ EXPECT_TRUE(printers.empty()); } -IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersInvalidPrinterValue) { +IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersInvalidPrinterValue) { ResultCatcher catcher; ExtensionId extension_id; @@ -710,11 +702,8 @@ }; // These are only instantiated for event page-based packaged apps. -INSTANTIATE_TEST_SUITE_P(EventPage, - PrinterProviderUsbApiTest, - ::testing::Values(ContextType::kEventPage)); -IN_PROC_BROWSER_TEST_P(PrinterProviderUsbApiTest, GetUsbPrinterInfo) { +IN_PROC_BROWSER_TEST_F(PrinterProviderUsbApiTest, GetUsbPrinterInfo) { ResultCatcher catcher; device::mojom::UsbDeviceInfoPtr device = usb_manager_.CreateAndAddDevice(0, 0, "Google", "USB Printer", ""); @@ -741,12 +730,12 @@ ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); } -IN_PROC_BROWSER_TEST_P(PrinterProviderUsbApiTest, +IN_PROC_BROWSER_TEST_F(PrinterProviderUsbApiTest, GetUsbPrinterInfoEmptyResponse) { RunUsbPrinterInfoRequestTest("EMPTY_RESPONSE"); } -IN_PROC_BROWSER_TEST_P(PrinterProviderUsbApiTest, GetUsbPrinterInfoNoListener) { +IN_PROC_BROWSER_TEST_F(PrinterProviderUsbApiTest, GetUsbPrinterInfoNoListener) { RunUsbPrinterInfoRequestTest("NO_LISTENER"); }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 191c7594..e9e398e 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -1877,11 +1877,6 @@ "expiry_milestone": 150 }, { - "name": "contextual-tasks-expand-button", - "owners": ["mdjones@google.com", "chrome-contextual-tasks-eng@google.com"], - "expiry_milestone": 150 - }, - { "name": "contextual-tasks-suggestions-enabled", "owners": ["dianaou@google.com", "chrome-contextual-tasks-eng@google.com"], "expiry_milestone": 150 @@ -2400,11 +2395,6 @@ "expiry_milestone": 150 }, { - "name": "disable-quick-answers-v2-translation", - "owners": [ "croissant-eng@chromium.org" ], - "expiry_milestone": 100 - }, - { "name": "disable-u18-feedback-desktop", "owners": [ "chrome-signin-team@google.com" ], "expiry_milestone": 160 @@ -5747,7 +5737,7 @@ "meacer@chromium.org", "chrome-secure-web-and-net@chromium.org" ], - "expiry_milestone": 125 + "expiry_milestone": 150 }, {
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index e05ed20..3b7f520 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -7934,11 +7934,6 @@ "Allows the ChromeOS print preview to be opened instead of the browser " " print preview."; -inline constexpr char kDisableQuickAnswersV2TranslationName[] = - "Disable Quick Answers Translation"; -inline constexpr char kDisableQuickAnswersV2TranslationDescription[] = - "Disable translation services of the Quick Answers."; - inline constexpr char kQuickAnswersRichCardName[] = "Enable Quick Answers Rich Card"; inline constexpr char kQuickAnswersRichCardDescription[] =
diff --git a/chrome/browser/glic/browser_ui/tab_underline_view_controller_impl.cc b/chrome/browser/glic/browser_ui/tab_underline_view_controller_impl.cc index 76cbeb58..59a64df2 100644 --- a/chrome/browser/glic/browser_ui/tab_underline_view_controller_impl.cc +++ b/chrome/browser/glic/browser_ui/tab_underline_view_controller_impl.cc
@@ -22,12 +22,7 @@ TabUnderlineViewControllerImpl::TabUnderlineViewControllerImpl() = default; -TabUnderlineViewControllerImpl::~TabUnderlineViewControllerImpl() { - if (glic_service_ && !GlicEnabling::IsMultiInstanceEnabled()) { - glic_service_->GetSingleInstanceWindowController().RemoveStateObserver( - this); - } -} +TabUnderlineViewControllerImpl::~TabUnderlineViewControllerImpl() = default; // This implementation makes many references to "pinned" tabs. All of these // refer to tabs that are selected to be shared with Gemini under the glic @@ -50,23 +45,6 @@ GlicSharingManager& sharing_manager = glic_service_->sharing_manager(); - if (!GlicEnabling::IsMultiInstanceEnabled()) { - // Subscribe to changes in the focused tab. - focus_change_subscription_ = - sharing_manager.AddFocusedTabChangedCallback(base::BindRepeating( - &TabUnderlineViewControllerImpl::OnFocusedTabChanged, - base::Unretained(this))); - // Subscribe to changes in the context access indicator status. - indicator_change_subscription_ = - glic_service_->AddContextAccessIndicatorStatusChangedCallback( - base::BindRepeating( - &TabUnderlineViewControllerImpl::OnIndicatorStatusChanged, - base::Unretained(this))); - - // Observe changes in the floaty state. - glic_service_->GetSingleInstanceWindowController().AddStateObserver(this); - } - // Subscribe to changes in the set of pinned tabs. pinned_tabs_change_subscription_ = sharing_manager.AddPinnedTabsChangedCallback(base::BindRepeating( @@ -270,8 +248,7 @@ case UpdateUnderlineReason::kContextAccessIndicatorOff: { // Underline should be hidden, with exception to pinned tabs while the // glic panel remains open. - if (IsUnderlineTabPinned() && - (GlicEnabling::IsMultiInstanceEnabled() || IsGlicWindowShowing())) { + if (IsUnderlineTabPinned()) { break; } HideUnderline(/*triggered_by_glic=*/true); @@ -320,24 +297,7 @@ } break; case UpdateUnderlineReason::kPinnedTabsChanged_TabInPinnedSet: - if (GlicEnabling::IsMultiInstanceEnabled()) { ShowAndAnimateUnderline(/*triggered_by_glic=*/true); - } else { - // If `underline_view_` is not visible, then this tab was just added - // to the set of pinned tabs. - if (!underline_view_->IsShowing()) { - // Pinned tab underlines should only be visible while the glic panel - // is open. For multi-instance this is controlled via the pinned - // tabs api. - if (IsGlicWindowShowing()) { - ShowAndAnimateUnderline(/*triggered_by_glic=*/true); - } - } else { - // This tab was already pinned - re-animate to reflect the change in - // the set of pinned tabs. - AnimateUnderline(); - } - } break; case UpdateUnderlineReason::kPinnedTabsChanged_TabNotInPinnedSet: // Re-animate to reflect the change in the set of pinned tabs. @@ -429,13 +389,9 @@ if (!IsUnderlineTabPinned()) { return; } - // For multi-instance, we rely on the umbrella sharing manager behavior to - // determine when to show or not show underlines via the pinned tabs api. - if (!GlicEnabling::IsMultiInstanceEnabled()) { - // Pinned underlines should never be visible if the glic window is closed. - if (!IsGlicWindowShowing()) { - return; - } + // Pinned underlines should never be visible if the glic window is closed. + if (!IsGlicWindowShowing()) { + return; } if (underline_view_->IsShowing()) { AnimateUnderline();
diff --git a/chrome/browser/glic/browser_ui/tab_underline_view_interactive_uitest.cc b/chrome/browser/glic/browser_ui/tab_underline_view_interactive_uitest.cc index 0ea7d868..4e8de099 100644 --- a/chrome/browser/glic/browser_ui/tab_underline_view_interactive_uitest.cc +++ b/chrome/browser/glic/browser_ui/tab_underline_view_interactive_uitest.cc
@@ -9,6 +9,7 @@ #include "cc/test/pixel_test_utils.h" #include "chrome/browser/glic/browser_ui/tab_underline_view.h" #include "chrome/browser/glic/browser_ui/tab_underline_view_controller_impl.h" +#include "chrome/browser/glic/host/glic.mojom.h" #include "chrome/browser/glic/host/glic_features.mojom.h" #include "chrome/browser/glic/test_support/glic_test_util.h" #include "chrome/browser/glic/test_support/interactive_glic_test.h" @@ -174,14 +175,16 @@ mojom::features::kGlicMultiTab.name; const std::string underline_feature_name = features::kGlicMultitabUnderlines.name; + const std::string multi_instance_feature_name = + features::kGlicMultiInstance.name; // Toggling `UiGpuRasterization` is only possible via command line. const std::string enabled_features = base::StrCat({multitab_feature_name, ",", underline_feature_name, ",", - "UiGpuRasterization"}); + multi_instance_feature_name, ",", "UiGpuRasterization"}); features_.InitFromCommandLine(enabled_features, /*disabled_features=*/ "ContextualTasks,GlicForceSimplifiedBorder," - "GlicForceNonSkSLBorder,GlicMultiInstance"); + "GlicForceNonSkSLBorder"); } ~TabUnderlineViewUiTest() override = default; @@ -363,14 +366,6 @@ EXPECT_TRUE(underline->IsShowing()); tester->AdvanceTimeAndTickAnimation(base::TimeDelta()); tester->AdvanceTimeAndTickAnimation(base::Seconds(0.3)); - - // The underline should hide when sharing is turned off. - glic_service()->SetContextAccessIndicator(false); - ASSERT_FALSE( - glic_service()->IsContextAccessIndicatorShown(GetActiveWebContents())); - tester->WaitForRampDownStarted(); - tester->FinishRampDown(); - EXPECT_FALSE(underline->IsShowing()); } IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest, @@ -379,50 +374,27 @@ EXPECT_TRUE(glic_service()->IsWindowShowing()); auto* underline = GetUnderlineOfActiveTab(); TesterImpl* tester = static_cast<TesterImpl*>(underline->tester()); - EXPECT_FALSE(underline->IsShowing()); - - // The underline should show when its tab is pinned. - tabs::TabHandle tab_handle = TabHandleAtIndex(0); - PinTabs({tab_handle}); - ASSERT_TRUE(sharing_manager().IsTabPinned(tab_handle)); - tester->WaitForAnimationStart(); + // Tab is pinned by default. EXPECT_TRUE(underline->IsShowing()); tester->AdvanceTimeAndTickAnimation(base::TimeDelta()); tester->AdvanceTimeAndTickAnimation(base::Seconds(0.3)); // The underline should hide when its tab is unpinned. sharing_manager().UnpinAllTabs(); - ASSERT_FALSE(sharing_manager().IsTabPinned(tab_handle)); + ASSERT_FALSE(sharing_manager().IsTabPinned(TabHandleAtIndex(0))); tester->WaitForRampDownStarted(); tester->FinishRampDown(); EXPECT_FALSE(underline->IsShowing()); } IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest, - SingleTabPinningWhileGlicWindowClosed) { - EXPECT_FALSE(glic_service()->IsWindowShowing()); - - // While the glic window is closed, changes to pinning have no effect on the - // underline UI. - auto* underline = GetUnderlineOfActiveTab(); - tabs::TabHandle tab_handle = TabHandleAtIndex(0); - PinTabs({tab_handle}); - EXPECT_TRUE(sharing_manager().IsTabPinned(tab_handle)); - EXPECT_FALSE(underline->IsShowing()); - - sharing_manager().UnpinAllTabs(); - ASSERT_FALSE(sharing_manager().IsTabPinned(tab_handle)); - EXPECT_FALSE(underline->IsShowing()); -} - -IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest, ToggleGlicWindowVisibilityWithPinnedTab) { auto* underline = GetUnderlineOfActiveTab(); TesterImpl* tester = static_cast<TesterImpl*>(underline->tester()); - tabs::TabHandle tab_handle = TabHandleAtIndex(0); - PinTabs({tab_handle}); - EXPECT_TRUE(sharing_manager().IsTabPinned(tab_handle)); + // tabs::TabHandle tab_handle = TabHandleAtIndex(0); + // PinTabs({tab_handle}); + // EXPECT_TRUE(sharing_manager().IsTabPinned(tab_handle)); // The underline of a pinned tab should show when the glic window is opened. RunTestSequence(OpenGlic()); @@ -440,36 +412,26 @@ IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest, FocusedTabChange) { auto* underline1 = GetUnderlineOfActiveTab(); - TesterImpl* tester1 = static_cast<TesterImpl*>(underline1->tester()); - // Add second tab + // Add second tab. AppendTabAndNavigate(browser(), Title2()); auto* underline2 = GetUnderlineOfActiveTab(); - TesterImpl* tester2 = static_cast<TesterImpl*>(underline2->tester()); // The underline of the active tab should show when sharing is turned on. + TrackOnlyGlicInstance(); OpenGlicWindowAndStartSharing(); - tester2->WaitForAnimationStart(); + GlicInstance* instance = glic_service()->GetInstanceForActiveTab(browser()); EXPECT_TRUE(underline2->IsShowing()); - tester2->AdvanceTimeAndTickAnimation(base::TimeDelta()); - tester2->AdvanceTimeAndTickAnimation(base::Seconds(0.3)); - // For tabs that are not pinned while sharing is turned on, the underline of a - // tab that loses focus should hide and the underline of a tab that gains - // focus should show. ActivateTabAt(0); - tester1->WaitForAnimationStart(); + instance->BindTabForTesting(TabHandleAtIndex(0).Get()); EXPECT_TRUE(underline1->IsShowing()); - tester1->AdvanceTimeAndTickAnimation(base::TimeDelta()); - tester1->AdvanceTimeAndTickAnimation(base::Seconds(0.3)); - - tester2->WaitForRampDownStarted(); - tester2->FinishRampDown(); - EXPECT_FALSE(underline2->IsShowing()); + EXPECT_TRUE(underline2->IsShowing()); } IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest, FocusedTabChangeBetweenPinnedTabs) { + // Open two tabs, and bind/pin them to the same glic instance. auto* underline1 = GetUnderlineOfActiveTab(); TesterImpl* tester1 = static_cast<TesterImpl*>(underline1->tester()); @@ -477,15 +439,19 @@ auto* underline2 = GetUnderlineOfActiveTab(); TesterImpl* tester2 = static_cast<TesterImpl*>(underline2->tester()); - // Pin both tabs - PinTabs({TabHandleAtIndex(0), TabHandleAtIndex(1)}); - EXPECT_TRUE(sharing_manager().IsTabPinned(TabHandleAtIndex(0))); - EXPECT_TRUE(sharing_manager().IsTabPinned(TabHandleAtIndex(1))); + TrackOnlyGlicInstance(); + OpenGlicWindowAndStartSharing(); + GlicInstance* instance = glic_service()->GetInstanceForActiveTab(browser()); + instance->BindTabForTesting(TabHandleAtIndex(0).Get()); + instance->BindTabForTesting(TabHandleAtIndex(1).Get()); // Underlines of all pinned tabs should show when the glic window is opened. - RunTestSequence(OpenGlic()); - tester1->WaitForAnimationStart(); - tester2->WaitForAnimationStart(); + if (!underline1->IsShowing()) { + tester1->WaitForAnimationStart(); + } + if (!underline2->IsShowing()) { + tester2->WaitForAnimationStart(); + } EXPECT_TRUE(underline1->IsShowing()); EXPECT_TRUE(underline2->IsShowing()); // Allow animations to reach their steady states. @@ -526,19 +492,13 @@ IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest, TabAlertIndicatorHidden_PinnedTab) { RunTestSequence(OpenGlic()); - EXPECT_TRUE(glic_service()->IsWindowShowing()); auto* underline = GetUnderlineOfActiveTab(); TesterImpl* tester = static_cast<TesterImpl*>(underline->tester()); - EXPECT_FALSE(underline->IsShowing()); - - tabs::TabHandle tab_handle = TabHandleAtIndex(0); - PinTabs({tab_handle}); - ASSERT_TRUE(sharing_manager().IsTabPinned(tab_handle)); - tester->WaitForAnimationStart(); EXPECT_TRUE(underline->IsShowing()); // The pinned tab should not have a visible tab alert indicator. EXPECT_FALSE(GetAlertIndicatorButtonOfActiveTab()->GetVisible()); + tester->FinishRampDown(); } // Ensure basic incognito window doesn't cause a crash. Simply opens an @@ -550,78 +510,7 @@ ui_test_utils::NavigateToURL(incognito_browser, GURL("about:blank"))); } -namespace { -class TabUnderlineViewFeatureDisabledBrowserTest - : public TabUnderlineViewUiTest { - public: - TabUnderlineViewFeatureDisabledBrowserTest() { - features_.InitAndDisableFeature(features::kGlicMultitabUnderlines); - } - ~TabUnderlineViewFeatureDisabledBrowserTest() override = default; - - private: - base::test::ScopedFeatureList features_; -}; -} // namespace - -IN_PROC_BROWSER_TEST_F(TabUnderlineViewFeatureDisabledBrowserTest, - TabAlertIndicatorShown) { - AlertIndicatorButton* alert_button = GetAlertIndicatorButtonOfActiveTab(); - EXPECT_FALSE(alert_button->GetVisible()); - - base::RunLoop wait_for_alert_loop; - auto callback_subscription = alert_button->AddVisibleChangedCallback( - wait_for_alert_loop.QuitClosure()); - - OpenGlicWindowAndStartSharing(); - tabs::TabInterface* tab = browser()->tab_strip_model()->GetTabAtIndex(0); - ASSERT_EQ(tab, sharing_manager().GetFocusedTabData().focus()); - - // Wait for the view's visibility change to trigger. - wait_for_alert_loop.Run(); - - // The shared tab should have a visible tab alert indicator. - EXPECT_TRUE(alert_button->GetVisible()); -} - -IN_PROC_BROWSER_TEST_F(TabUnderlineViewFeatureDisabledBrowserTest, - TabAlertIndicatorShown_PinnedTab) { - AlertIndicatorButton* alert_button = GetAlertIndicatorButtonOfActiveTab(); - EXPECT_FALSE(alert_button->GetVisible()); - - RunTestSequence(OpenGlic()); - EXPECT_TRUE(glic_service()->IsWindowShowing()); - - base::RunLoop wait_for_alert_loop; - auto callback_subscription = alert_button->AddVisibleChangedCallback( - wait_for_alert_loop.QuitClosure()); - - tabs::TabHandle tab_handle = TabHandleAtIndex(0); - PinTabs({tab_handle}); - ASSERT_TRUE(sharing_manager().IsTabPinned(tab_handle)); - - // Wait for the view's visibility change to trigger. - wait_for_alert_loop.Run(); - - // The pinned tab should have a visible tab alert indicator. - EXPECT_TRUE(GetAlertIndicatorButtonOfActiveTab()->GetVisible()); -} - -class TabUnderlineViewMultiInstanceUiTest : public TabUnderlineViewUiTest { - public: - TabUnderlineViewMultiInstanceUiTest() { - // kGlicMultiInstance, kGlicMultiTab, kGlicMultitabUnderlines are required - // for IsMultiInstanceEnabled(). - scoped_feature_list_.InitWithFeatures({features::kGlic}, {}); - } - ~TabUnderlineViewMultiInstanceUiTest() override = default; - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - -IN_PROC_BROWSER_TEST_F(TabUnderlineViewMultiInstanceUiTest, - AttachPinnedTabToNewWindow) { +IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest, AttachPinnedTabToNewWindow) { // Set up two windows, each with one tab ASSERT_EQ(browser()->tab_strip_model()->count(), 1); // Second browser window; this will be active.
diff --git a/chrome/browser/glic/glic_metrics.h b/chrome/browser/glic/glic_metrics.h index 6b848872..02aedec 100644 --- a/chrome/browser/glic/glic_metrics.h +++ b/chrome/browser/glic/glic_metrics.h
@@ -221,7 +221,11 @@ kIphAttachedAudio = 86, kIphDetachedText = 87, kIphDetachedAudio = 88, - kMaxValue = kIphDetachedAudio, + kAnchoredContextualCueAttachedText = 89, + kAnchoredContextualCueAttachedAudio = 90, + kAnchoredContextualCueDetachedText = 91, + kAnchoredContextualCueDetachedAudio = 92, + kMaxValue = kAnchoredContextualCueDetachedAudio, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:GlicResponseSegmentation)
diff --git a/chrome/browser/glic/host/glic.mojom b/chrome/browser/glic/host/glic.mojom index 52dd825..cc70ced 100644 --- a/chrome/browser/glic/host/glic.mojom +++ b/chrome/browser/glic/host/glic.mojom
@@ -1523,6 +1523,8 @@ [MinVersion=12] kCaptureRegionHotkey = 20, // From the in-product-help (IPH) entrypoint. [MinVersion=13] kIph = 21, + // User clicked an anchored contextual cue chip. + [MinVersion=14] kAnchoredContextualCue = 22, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:GlicInvocationSource, //chrome/browser/glic/glic_metrics.h:ResponseSegmentation)
diff --git a/chrome/browser/glic/public/glic_enabling_browsertest.cc b/chrome/browser/glic/public/glic_enabling_browsertest.cc index 3da7f89..0f8f97ac5 100644 --- a/chrome/browser/glic/public/glic_enabling_browsertest.cc +++ b/chrome/browser/glic/public/glic_enabling_browsertest.cc
@@ -401,156 +401,5 @@ } } -// Test fixtures for testing-related flags for multi-instance enablement by -// tier. -class GlicMultiInstanceEnablingTestingFlagsBrowserTest - : public GlicEnablingTest { - public: - void InitializeFeatureList() override { - scoped_feature_list_.InitWithFeatures( - { - features::kGlic, - features::kGlicEnableMultiInstanceBasedOnTier, -#if BUILDFLAG(IS_CHROMEOS) - chromeos::features::kFeatureManagementGlic, -#endif // BUILDFLAG(IS_CHROMEOS) - }, - {features::kGlicMultiInstance}); - } - ~GlicMultiInstanceEnablingTestingFlagsBrowserTest() override = default; - - protected: - // Helper to set the AI subscription tier for the profile. - void SetAiSubscriptionTier(int tier) { - profile()->GetPrefs()->SetInteger( - subscription_eligibility::prefs::kAiSubscriptionTier, tier); - } - - // Helper to set the kGlicMultiInstanceEnabledBySubscriptionTier pref. - void SetGlicMultiInstanceEnabledBySubscriptionTierPref(bool value) { - g_browser_process->local_state()->SetBoolean( - glic::prefs::kGlicMultiInstanceEnabledBySubscriptionTier, value); - } - - // Helper to get the kGlicMultiInstanceEnabledBySubscriptionTier pref. - bool GetGlicMultiInstanceEnabledBySubscriptionTierPref() { - return g_browser_process->local_state()->GetBoolean( - glic::prefs::kGlicMultiInstanceEnabledBySubscriptionTier); - } -}; - -class GlicMultiInstanceEnablingNoFlagsBrowserTest - : public GlicMultiInstanceEnablingTestingFlagsBrowserTest, - public testing::WithParamInterface<std::tuple<bool, bool>> { - public: - bool IsG1() const { return std::get<0>(GetParam()); } - bool WasPreviouslyEligible() const { return std::get<1>(GetParam()); } -}; - -IN_PROC_BROWSER_TEST_P(GlicMultiInstanceEnablingNoFlagsBrowserTest, - NoFlagsTest) { - SetAiSubscriptionTier(IsG1() ? 1 : 0); - SetGlicMultiInstanceEnabledBySubscriptionTierPref(WasPreviouslyEligible()); - - bool expected_eligibility = IsG1() || WasPreviouslyEligible(); - EXPECT_EQ( - GlicEnabling::GetAndUpdateEligibilityForGlicMultiInstanceTieredRollout( - profile()), - expected_eligibility); -} - -INSTANTIATE_TEST_SUITE_P(All, - GlicMultiInstanceEnablingNoFlagsBrowserTest, - testing::Combine(testing::Bool(), testing::Bool())); - -class GlicMultiInstanceEnablingForceG1ForMiBrowserTest - : public GlicMultiInstanceEnablingTestingFlagsBrowserTest, - public testing::WithParamInterface<bool> { - public: - void SetUpCommandLine(base::CommandLine* command_line) override { - GlicMultiInstanceEnablingTestingFlagsBrowserTest::SetUpCommandLine( - command_line); - command_line->AppendSwitchASCII( - switches::kGlicForceG1StatusForMultiInstance, - GetParam() ? "true" : "false"); - } -}; - -IN_PROC_BROWSER_TEST_P(GlicMultiInstanceEnablingForceG1ForMiBrowserTest, - ForceG1ForMi) { - bool force_g1_for_mi = GetParam(); - // Set the actual subscription tier to the opposite status of the test param, - // to ensure that it is overridden by the command line switch in effect. - SetAiSubscriptionTier(force_g1_for_mi ? 0 : 1); - EXPECT_EQ( - GlicEnabling::GetAndUpdateEligibilityForGlicMultiInstanceTieredRollout( - profile()), - force_g1_for_mi); -} - -INSTANTIATE_TEST_SUITE_P(All, - GlicMultiInstanceEnablingForceG1ForMiBrowserTest, - testing::Bool()); - -class GlicMultiInstanceEnablingResetPrefBrowserTest - : public GlicMultiInstanceEnablingTestingFlagsBrowserTest, - public testing::WithParamInterface<bool> { - public: - void SetUpCommandLine(base::CommandLine* command_line) override { - GlicMultiInstanceEnablingTestingFlagsBrowserTest::SetUpCommandLine( - command_line); - command_line->AppendSwitch(switches::kGlicResetMultiInstanceEnabledByTier); - } -}; - -IN_PROC_BROWSER_TEST_P(GlicMultiInstanceEnablingResetPrefBrowserTest, - ResetMiEnabledByTierPref) { - bool is_eligible_by_tier = GetParam(); - SetAiSubscriptionTier(is_eligible_by_tier ? 1 : 0); - SetGlicMultiInstanceEnabledBySubscriptionTierPref(/*value=*/true); - - EXPECT_EQ( - GlicEnabling::GetAndUpdateEligibilityForGlicMultiInstanceTieredRollout( - profile()), - is_eligible_by_tier); - // The pref should be reset to false if the user is not eligible by tier. - EXPECT_EQ(GetGlicMultiInstanceEnabledBySubscriptionTierPref(), - is_eligible_by_tier); -} - -INSTANTIATE_TEST_SUITE_P(All, - GlicMultiInstanceEnablingResetPrefBrowserTest, - testing::Bool()); - -class GlicEnablingMultiInstanceFlagPrecedenceBrowserTest - : public GlicMultiInstanceEnablingTestingFlagsBrowserTest, - public testing::WithParamInterface<bool> { - public: - void SetUpCommandLine(base::CommandLine* command_line) override { - GlicMultiInstanceEnablingTestingFlagsBrowserTest::SetUpCommandLine( - command_line); - command_line->AppendSwitch(switches::kGlicResetMultiInstanceEnabledByTier); - command_line->AppendSwitchASCII( - switches::kGlicForceG1StatusForMultiInstance, - GetParam() ? "true" : "false"); - } -}; - -IN_PROC_BROWSER_TEST_P(GlicEnablingMultiInstanceFlagPrecedenceBrowserTest, - FlagPrecedence) { - bool force_g1_for_mi = GetParam(); - SetAiSubscriptionTier(force_g1_for_mi ? 0 : 1); - SetGlicMultiInstanceEnabledBySubscriptionTierPref(/*value=*/true); - - EXPECT_EQ( - GlicEnabling::GetAndUpdateEligibilityForGlicMultiInstanceTieredRollout( - profile()), - force_g1_for_mi); -} - -INSTANTIATE_TEST_SUITE_P(All, - GlicEnablingMultiInstanceFlagPrecedenceBrowserTest, - testing::Bool()); - } // namespace } // namespace glic
diff --git a/chrome/browser/glic/selection/selection_overlay_untrusted_ui.cc b/chrome/browser/glic/selection/selection_overlay_untrusted_ui.cc index e1025cf..4f84be7 100644 --- a/chrome/browser/glic/selection/selection_overlay_untrusted_ui.cc +++ b/chrome/browser/glic/selection/selection_overlay_untrusted_ui.cc
@@ -75,6 +75,7 @@ html_source->AddLocalizedString( "bottomLeftSliderAriaLabel", IDS_LENS_OVERLAY_BOTTOM_LEFT_CORNER_SLIDER_ACCESSIBILITY_LABEL); + html_source->AddBoolean("enableMultiRegionSelection", true); // TODO(b/489801993): Refactor shared resources into a common directory to // avoid manual path concatenation for Lens and the Glic selection overlay.
diff --git a/chrome/browser/glic/test_support/glic_browser_test.h b/chrome/browser/glic/test_support/glic_browser_test.h index 7b96df2..302e406 100644 --- a/chrome/browser/glic/test_support/glic_browser_test.h +++ b/chrome/browser/glic/test_support/glic_browser_test.h
@@ -38,12 +38,8 @@ #endif #if defined(TOOLKIT_VIEWS) -#include "ui/views/buildflags.h" - -#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) #include "ui/views/test/mock_activation_controller.h" #endif -#endif namespace glic { @@ -98,12 +94,10 @@ void SetUpOnMainThread() override { T::SetUpOnMainThread(); -#if defined(TOOLKIT_VIEWS) -#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) +#if defined(USE_MOCK_ACTIVATION_CONTROLLER) activation_controller_ = std::make_unique<views::test::MockActivationController>(); #endif -#endif CHECK(glic_test_environment_.SetupEmbeddedTestServers( T::embedded_test_server(), &T::embedded_https_test_server())); @@ -111,11 +105,9 @@ } void TearDownOnMainThread() override { -#if defined(TOOLKIT_VIEWS) -#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) +#if defined(USE_MOCK_ACTIVATION_CONTROLLER) activation_controller_.reset(); #endif -#endif T::TearDownOnMainThread(); } @@ -264,11 +256,9 @@ private: GlicTestEnvironment glic_test_environment_; base::test::ScopedFeatureList scoped_feature_list_; -#if defined(TOOLKIT_VIEWS) -#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) +#if defined(USE_MOCK_ACTIVATION_CONTROLLER) std::unique_ptr<views::test::MockActivationController> activation_controller_; #endif -#endif }; using GlicBrowserTest = GlicBrowserTestMixin<PlatformBrowserTest>;
diff --git a/chrome/browser/glic/test_support/non_interactive_glic_test.cc b/chrome/browser/glic/test_support/non_interactive_glic_test.cc index 5bc2d19..a9af02a3 100644 --- a/chrome/browser/glic/test_support/non_interactive_glic_test.cc +++ b/chrome/browser/glic/test_support/non_interactive_glic_test.cc
@@ -32,20 +32,16 @@ void NonInteractiveGlicTest::SetUpOnMainThread() { test::InteractiveGlicTestMixin<InteractiveBrowserTest>::SetUpOnMainThread(); -#if defined(TOOLKIT_VIEWS) -#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) +#if defined(USE_MOCK_ACTIVATION_CONTROLLER) activation_controller_ = std::make_unique<views::test::MockActivationController>(); #endif -#endif } void NonInteractiveGlicTest::TearDownOnMainThread() { -#if defined(TOOLKIT_VIEWS) -#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) +#if defined(USE_MOCK_ACTIVATION_CONTROLLER) activation_controller_.reset(); #endif -#endif test::InteractiveGlicTestMixin< InteractiveBrowserTest>::TearDownOnMainThread(); }
diff --git a/chrome/browser/glic/test_support/non_interactive_glic_test.h b/chrome/browser/glic/test_support/non_interactive_glic_test.h index ccbdd063..0b6235fe 100644 --- a/chrome/browser/glic/test_support/non_interactive_glic_test.h +++ b/chrome/browser/glic/test_support/non_interactive_glic_test.h
@@ -12,12 +12,8 @@ #include "chrome/browser/glic/test_support/interactive_glic_test.h" #if defined(TOOLKIT_VIEWS) -#include "ui/views/buildflags.h" - -#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) #include "ui/views/test/mock_activation_controller.h" #endif -#endif namespace glic { @@ -40,11 +36,9 @@ private: base::test::ScopedFeatureList features_; -#if defined(TOOLKIT_VIEWS) -#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) +#if defined(USE_MOCK_ACTIVATION_CONTROLLER) std::unique_ptr<views::test::MockActivationController> activation_controller_; #endif -#endif }; } // namespace glic
diff --git a/chrome/browser/indigo/BUILD.gn b/chrome/browser/indigo/BUILD.gn index a17cab7a..e1c7417 100644 --- a/chrome/browser/indigo/BUILD.gn +++ b/chrome/browser/indigo/BUILD.gn
@@ -36,6 +36,7 @@ deps = [ ":indigo", "//base", + "//chrome/browser/indigo/onboarding", "//chrome/browser/optimization_guide", "//chrome/browser/profiles:profile", "//chrome/browser/signin",
diff --git a/chrome/browser/indigo/indigo_page_action_controller.cc b/chrome/browser/indigo/indigo_page_action_controller.cc index 0abed2e8..de3906e 100644 --- a/chrome/browser/indigo/indigo_page_action_controller.cc +++ b/chrome/browser/indigo/indigo_page_action_controller.cc
@@ -14,6 +14,7 @@ #include "base/notimplemented.h" #include "chrome/browser/indigo/indigo_agent_host.h" #include "chrome/browser/indigo/indigo_alpha_rpc.h" +#include "chrome/browser/indigo/onboarding/indigo_onboarding_dialog.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" @@ -35,6 +36,7 @@ namespace { const char kForceIndigoSwitch[] = "force-indigo"; +const char kForceIndigoOnboardingSwitch[] = "force-indigo-onboarding"; } // namespace DEFINE_USER_DATA(IndigoPageActionController); @@ -84,6 +86,21 @@ return; } + // For now, onboarding is only triggered when forced, and the URL is specified + // in the command line switch. In the future, this will typically be triggered + // automatically based on the user's enrolment status, and the URL will be + // determined by a feature param. + std::string onboarding_url = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + kForceIndigoOnboardingSwitch); + if (!onboarding_url.empty()) { + onboarding_dialog_ = IndigoOnboardingDialog::Show( + tab(), GURL(onboarding_url), + base::BindOnce(&IndigoPageActionController::OnOnboardingDialogClosed, + weak_ptr_factory_.GetWeakPtr())); + return; + } + if (IndigoAgentHost::GetOrCreateForPage(web_contents->GetPrimaryPage()) ->Invoke()) { return; @@ -178,6 +195,10 @@ is_shown_ = should_show; } +void IndigoPageActionController::OnOnboardingDialogClosed() { + onboarding_dialog_.reset(); +} + void IndigoPageActionController::OnOptimizationGuideDecision( const GURL& url, optimization_guide::OptimizationGuideDecision decision,
diff --git a/chrome/browser/indigo/indigo_page_action_controller.h b/chrome/browser/indigo/indigo_page_action_controller.h index fc19a19..3c0b860 100644 --- a/chrome/browser/indigo/indigo_page_action_controller.h +++ b/chrome/browser/indigo/indigo_page_action_controller.h
@@ -31,6 +31,8 @@ namespace indigo { +class IndigoOnboardingDialog; + // Manages the Indigo page action and its various entry points, ensuring they // are correctly displayed. class IndigoPageActionController : public tabs::ContentsObservingTabFeature, @@ -62,6 +64,9 @@ // Updates the visibility and states of all entry points. void UpdateEntryPointsState(); + // Called when the onboarding dialog is closed. + void OnOnboardingDialogClosed(); + // Called when optimization guide has decided whether this feature should be // enabled for the page. void OnOptimizationGuideDecision( @@ -93,6 +98,9 @@ // If true, the Indigo page action is currently shown. bool is_shown_ = false; + // The onboarding dialog, if shown. + std::unique_ptr<IndigoOnboardingDialog> onboarding_dialog_; + base::ScopedObservation<signin::IdentityManager, signin::IdentityManager::Observer> identity_manager_observation_{this};
diff --git a/chrome/browser/indigo/onboarding/BUILD.gn b/chrome/browser/indigo/onboarding/BUILD.gn new file mode 100644 index 0000000..cf9e2d48 --- /dev/null +++ b/chrome/browser/indigo/onboarding/BUILD.gn
@@ -0,0 +1,47 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("onboarding") { + sources = [ + "indigo_onboarding_dialog.cc", + "indigo_onboarding_dialog.h", + ] + + public_deps = [ "//chrome/browser:browser_public_dependencies" ] + deps = [ + "//base", + "//chrome/browser/profiles:profile", + "//chrome/browser/ui/tabs:tabs_public", + "//components/tabs:public", + "//content/public/browser", + "//ui/base", + "//ui/gfx/geometry", + "//ui/views", + "//ui/views/controls/webview", + "//url", + ] +} + +if (!is_android) { + source_set("browser_tests") { + testonly = true + defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] + sources = [ "indigo_onboarding_dialog_browsertest.cc" ] + deps = [ + ":onboarding", + "//chrome/browser", + "//chrome/browser/ui/tabs:tabs_public", + "//chrome/test:test_support", + "//chrome/test:test_support_ui", + "//components/tabs:public", + "//content/public/browser", + "//content/test:test_support", + "//testing/gtest", + "//ui/base/interaction", + "//ui/views", + "//ui/views/controls/webview", + "//url", + ] + } +}
diff --git a/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.cc b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.cc new file mode 100644 index 0000000..3a2acf3 --- /dev/null +++ b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.cc
@@ -0,0 +1,122 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/indigo/onboarding/indigo_onboarding_dialog.h" + +#include <memory> +#include <utility> + +#include "base/functional/bind.h" +#include "base/memory/ptr_util.h" +#include "chrome/browser/file_select_helper.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/tabs/public/tab_dialog_manager.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" +#include "components/tabs/public/tab_interface.h" +#include "content/public/browser/file_select_listener.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/web_contents.h" +#include "ui/base/mojom/dialog_button.mojom.h" +#include "ui/base/ui_base_types.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" +#include "ui/views/controls/webview/webview.h" +#include "ui/views/view_class_properties.h" +#include "ui/views/widget/widget.h" +#include "ui/views/window/dialog_delegate.h" +#include "url/gurl.h" + +namespace indigo { + +namespace { + +class OnboardingWebView : public views::WebView { + public: + using WebView::WebView; + + // WebContentsDelegate: + + void RunFileChooser(content::RenderFrameHost* render_frame_host, + scoped_refptr<content::FileSelectListener> listener, + const blink::mojom::FileChooserParams& params) override { + FileSelectHelper::RunFileChooser(render_frame_host, std::move(listener), + params); + } + + bool HandleKeyboardEvent( + content::WebContents* web_contents, + const input::NativeWebKeyboardEvent& event) override { + return unhandled_keyboard_event_handler_.HandleKeyboardEvent( + event, GetFocusManager()); + } + + private: + views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; +}; + +} // namespace + +DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(IndigoOnboardingDialog, kWebViewId); + +// static +std::unique_ptr<IndigoOnboardingDialog> IndigoOnboardingDialog::Show( + tabs::TabInterface& tab, + const GURL& onboarding_url, + base::OnceClosure close_callback) { + if (!tab.CanShowModalUI()) { + return nullptr; + } + return base::WrapUnique(new IndigoOnboardingDialog( + tab, onboarding_url, std::move(close_callback))); +} + +IndigoOnboardingDialog::IndigoOnboardingDialog(tabs::TabInterface& tab, + const GURL& onboarding_url, + base::OnceClosure close_callback) + : close_callback_(std::move(close_callback)) { + Profile* profile = + Profile::FromBrowserContext(tab.GetContents()->GetBrowserContext()); + auto web_view = std::make_unique<OnboardingWebView>(profile); + web_view->GetWebContents()->GetController().LoadURLWithParams( + content::NavigationController::LoadURLParams(onboarding_url)); + web_view->SetPreferredSize(gfx::Size(800, 600)); + web_view->SetProperty(views::kElementIdentifierKey, kWebViewId); + + delegate_ = std::make_unique<views::DialogDelegate>(); + delegate_->SetContentsView(std::move(web_view)); + delegate_->SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone)); + delegate_->SetShowCloseButton(true); + delegate_->SetModalType(ui::mojom::ModalType::kChild); + delegate_->SetOwnershipOfNewWidget( + views::Widget::InitParams::CLIENT_OWNS_WIDGET); + + auto* tab_dialog_manager = tab.GetTabFeatures()->tab_dialog_manager(); + widget_ = tab_dialog_manager->CreateTabScopedDialog(delegate_.get()); + widget_->MakeCloseSynchronous(base::BindOnce( + &IndigoOnboardingDialog::OnWidgetClosed, base::Unretained(this))); + + auto params = std::make_unique<tabs::TabDialogManager::Params>(); + tab_dialog_manager->ShowDialog(widget_.get(), std::move(params)); +} + +IndigoOnboardingDialog::~IndigoOnboardingDialog() = default; + +void IndigoOnboardingDialog::Close() { + if (widget_) { + widget_->Close(); + } +} + +void IndigoOnboardingDialog::OnWidgetClosed( + views::Widget::ClosedReason reason) { + // As recommended in the comment on `views::Widget::MakeCloseSynchronous`, + // destroy the widget here. + widget_.reset(); + + if (close_callback_) { + std::move(close_callback_).Run(); + } +} + +} // namespace indigo
diff --git a/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.h b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.h new file mode 100644 index 0000000..8354ad60 --- /dev/null +++ b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.h
@@ -0,0 +1,66 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_INDIGO_ONBOARDING_INDIGO_ONBOARDING_DIALOG_H_ +#define CHROME_BROWSER_INDIGO_ONBOARDING_INDIGO_ONBOARDING_DIALOG_H_ + +#include <memory> + +#include "base/functional/callback_forward.h" +#include "ui/base/interaction/element_identifier.h" +#include "ui/views/widget/widget.h" + +class GURL; + +namespace tabs { +class TabInterface; +} + +namespace views { +class DialogDelegate; +} + +namespace indigo { + +// Owns and manages an onboarding dialog which is mainly powered by a WebView. +class IndigoOnboardingDialog { + public: + DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kWebViewId); + + // Creates and shows a tab-modal dialog embedding a WebView pointing to an + // external onboarding URL. Returns a new `IndigoOnboardingDialog`, or nullptr + // if the tab cannot show modal UI (e.g., another modal UI is already shown). + // `close_callback` is run when the dialog is closed. + static std::unique_ptr<IndigoOnboardingDialog> Show( + tabs::TabInterface& tab, + const GURL& onboarding_url, + base::OnceClosure close_callback); + + IndigoOnboardingDialog(const IndigoOnboardingDialog&) = delete; + IndigoOnboardingDialog& operator=(const IndigoOnboardingDialog&) = delete; + + ~IndigoOnboardingDialog(); + + // Closes the dialog immediately. This will call the `close_callback` passed + // to `Show`, likely resulting in the destruction of this object. + void Close(); + + private: + explicit IndigoOnboardingDialog(tabs::TabInterface& tab, + const GURL& onboarding_url, + base::OnceClosure close_callback); + + void OnWidgetClosed(views::Widget::ClosedReason reason); + + base::OnceClosure close_callback_; + + // `widget_` must be destroyed before `delegate_` because the widget holds a + // raw pointer to the delegate. + std::unique_ptr<views::DialogDelegate> delegate_; + std::unique_ptr<views::Widget> widget_; +}; + +} // namespace indigo + +#endif // CHROME_BROWSER_INDIGO_ONBOARDING_INDIGO_ONBOARDING_DIALOG_H_
diff --git a/chrome/browser/indigo/onboarding/indigo_onboarding_dialog_browsertest.cc b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog_browsertest.cc new file mode 100644 index 0000000..8d8406c --- /dev/null +++ b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog_browsertest.cc
@@ -0,0 +1,59 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/indigo/onboarding/indigo_onboarding_dialog.h" + +#include "base/functional/callback_helpers.h" +#include "base/test/bind.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/public/tab_dialog_manager.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" +#include "chrome/test/interaction/interactive_browser_test.h" +#include "components/tabs/public/tab_interface.h" +#include "content/public/test/browser_test.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/controls/webview/webview.h" +#include "ui/views/view_utils.h" +#include "ui/views/widget/widget.h" +#include "url/gurl.h" + +namespace indigo { +namespace { + +class IndigoOnboardingDialogBrowserTest : public InteractiveBrowserTest { + protected: + void OpenDialog(tabs::TabInterface& tab, const GURL& url) { + dialog_ = IndigoOnboardingDialog::Show(tab, url, + base::BindLambdaForTesting([&]() { + closed_ = true; + dialog_.reset(); + })); + } + + bool WasDialogClosed() const { return closed_; } + + std::unique_ptr<IndigoOnboardingDialog> dialog_; + bool closed_ = false; +}; + +IN_PROC_BROWSER_TEST_F(IndigoOnboardingDialogBrowserTest, ShowAndClose) { + tabs::TabInterface* tab = browser()->GetActiveTabInterface(); + ASSERT_TRUE(tab); + + const GURL example_url("https://www.example.com/"); + RunTestSequence( + Do([&]() { OpenDialog(*tab, example_url); }), + WaitForShow(IndigoOnboardingDialog::kWebViewId), + CheckView( + IndigoOnboardingDialog::kWebViewId, + [](views::WebView* web_view) { return web_view->GetPreferredSize(); }, + gfx::Size(800, 600)), + Do([&]() { dialog_->Close(); }), + WaitForHide(IndigoOnboardingDialog::kWebViewId), + Check([&]() { return WasDialogClosed(); })); +} + +} // namespace +} // namespace indigo
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinator.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinator.java index 5484195..1979420 100644 --- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinator.java +++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinator.java
@@ -184,6 +184,14 @@ // We shouldn't use mTrackingTab, which can be set to null in removeObservers(), and it // won't be updated if mTabSupplierObserver is removed. Tab currentTab = mTabSupplier.get(); + if (currentTab == null + && mLayoutStateProvider != null + && mLayoutStateProvider.getActiveLayoutType() == LayoutType.TAB_SWITCHER) { + // We don't update toolbar's top padding on Tab switcher until tab switches. Thus, we + // should keep mConsumeTopInset reflect whether the top inset is consumed on the last + // Tab. See https://crbug.com/491888405. + return windowInsetsCompat; + } mSystemInsets = windowInsetsCompat.getInsets(WindowInsetsCompat.Type.systemBars());
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java index f25e8d4..f8a55e9 100644 --- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java +++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java
@@ -178,6 +178,21 @@ } @Test + public void testOnApplyWindowInsets_TabSwitcher_ReturnEarly() { + // Tab switcher is showing. + mTabSupplier.set(null); + mLayoutStateProviderSupplier.set(mLayoutStateProvider); + setBackgroundType(NtpBackgroundType.DEFAULT, NtpBackgroundType.CHROME_COLOR); + when(mLayoutStateProvider.getActiveLayoutType()).thenReturn(LayoutType.TAB_SWITCHER); + clearInvocations(mObserver); + + mTopInsetCoordinator.onApplyWindowInsets(mView, mWindowInsetsCompat); + + // Verify that notifyObservers() is NOT called. + verify(mObserver, never()).onToEdgeChange(any(Integer.class), any(Boolean.class), anyInt()); + } + + @Test public void testOnTabSwitched_RetriggerOnApplyWindowInsets() { // Verifies that retriggerOnApplyWindowInsets() is called if the new tab is a NTP. setCurrentTab(mNtpTab);
diff --git a/chrome/browser/optimization_guide/model_execution/model_execution_browsertest.cc b/chrome/browser/optimization_guide/model_execution/model_execution_browsertest.cc index f70af80..e4b2789c 100644 --- a/chrome/browser/optimization_guide/model_execution/model_execution_browsertest.cc +++ b/chrome/browser/optimization_guide/model_execution/model_execution_browsertest.cc
@@ -716,7 +716,7 @@ // Set up assets which are registered per-profile. void SetUpProfileAssets() { - compose_asset_.SendTo(broker_state()->service_controller()); + compose_asset_.SendTo(broker_state()->base_model_controller()); } private:
diff --git a/chrome/browser/page_info/BUILD.gn b/chrome/browser/page_info/BUILD.gn index 6bef4be..9c5ac5c 100644 --- a/chrome/browser/page_info/BUILD.gn +++ b/chrome/browser/page_info/BUILD.gn
@@ -11,21 +11,88 @@ } source_set("page_info") { - public = [ "page_info_features.h" ] - + sources = [ + "about_this_site_service_factory.h", + "about_this_site_tab_helper.h", + "merchant_trust_service_delegate.h", + "merchant_trust_service_factory.h", + "page_info_features.h", + "privacy_policy_insights_service_factory.h", + ] public_deps = [ "//base", "//chrome/browser:browser_process", + "//chrome/browser/profiles:profile", + "//components/optimization_guide/core", + "//components/page_info/core", + "//components/page_info/core:proto", + "//content/public/browser", ] + + if (!is_android) { + sources += [ "web_view_side_panel_throttle.h" ] + } } +# The reason `impl` is needed is because +# about_this_site_controller_android.cc includes +# c/b/android/android_theme_resources.h and c/b/android/resource_mapper.h, +# both part of //c/b:b, which causes a circular dependency against it. +# +# TODO(crbug.com/353332589): Merge `impl` into `page_info` when they get +# componentized. source_set("impl") { - sources = [ "page_info_features.cc" ] + sources = [ + "about_this_site_service_factory.cc", + "about_this_site_tab_helper.cc", + "merchant_trust_service_delegate.cc", + "merchant_trust_service_factory.cc", + "page_info_features.cc", + "privacy_policy_insights_service_factory.cc", + ] deps = [ ":page_info", + "//base", + "//chrome/browser:browser_process", + "//chrome/browser/engagement", + "//chrome/browser/optimization_guide", + "//chrome/browser/profiles:profile", + "//chrome/browser/search_engines", "//chrome/browser/ui:ui_features", + "//chrome/browser/ui/hats", + "//chrome/browser/ui/page_info", "//components/page_info/core", "//components/variations/service", ] + + if (is_android) { + sources += [ "about_this_site_controller_android.cc" ] + deps += [ "//chrome/android:jni_headers" ] + } else { + sources += [ "web_view_side_panel_throttle.cc" ] + deps += [ "//components/navigation_interception" ] + } + + public_deps = [ "//chrome/browser:browser_public_dependencies" ] +} + +source_set("unit_tests") { + testonly = true + sources = [] + deps = [] + + if (!is_android) { + sources += [ "merchant_trust_service_delegate_unittest.cc" ] + deps += [ + ":page_info", + "//base/test:test_support", + "//chrome/test:test_support", + "//components/page_info/core", + "//components/site_engagement/content", + "//content/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] + } }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 0673041..1640225 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -3218,7 +3218,7 @@ key::kSecondaryGoogleAccountSigninAllowed, ::account_manager::prefs::kSecondaryGoogleAccountSigninAllowed)); handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>( - key::kUsageTimeLimit, prefs::kUsageTimeLimit, chrome_schema, + key::kUsageTimeLimit, ash::prefs::kUsageTimeLimit, chrome_schema, SCHEMA_ALLOW_UNKNOWN, SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED, SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED)); @@ -3250,21 +3250,22 @@ SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED, SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED)); handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>( - key::kParentAccessCodeConfig, prefs::kParentAccessCodeConfig, + key::kParentAccessCodeConfig, ash::prefs::kParentAccessCodeConfig, chrome_schema, SCHEMA_ALLOW_UNKNOWN, SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED, SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED)); handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>( - key::kPerAppTimeLimits, prefs::kPerAppTimeLimitsPolicy, chrome_schema, + key::kPerAppTimeLimits, ash::prefs::kPerAppTimeLimitsPolicy, + chrome_schema, SCHEMA_ALLOW_UNKNOWN, + SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED, + SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED)); + handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>( + key::kPerAppTimeLimitsAllowlist, + ash::prefs::kPerAppTimeLimitsAllowlistPolicy, chrome_schema, SCHEMA_ALLOW_UNKNOWN, SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED, SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED)); handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>( - key::kPerAppTimeLimitsAllowlist, prefs::kPerAppTimeLimitsAllowlistPolicy, - chrome_schema, SCHEMA_ALLOW_UNKNOWN, - SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED, - SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED)); - handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>( key::kEduCoexistenceToSVersion, ash::prefs::kEduCoexistenceToSVersion, chrome_schema, SCHEMA_ALLOW_UNKNOWN, SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
diff --git a/chrome/browser/profiles/BUILD.gn b/chrome/browser/profiles/BUILD.gn index 1b1b58f..bdea8ab 100644 --- a/chrome/browser/profiles/BUILD.gn +++ b/chrome/browser/profiles/BUILD.gn
@@ -334,6 +334,7 @@ "//chrome/browser/page_content_annotations", "//chrome/browser/page_content_annotations:extraction_service", "//chrome/browser/page_image_service", + "//chrome/browser/page_info", "//chrome/browser/passage_embeddings", "//chrome/browser/password_manager/factories", "//chrome/browser/payments/browser_binding:browser_bound_key_deleter",
diff --git a/chrome/browser/resources/ash/settings/os_search_page/search_subpage.html b/chrome/browser/resources/ash/settings/os_search_page/search_subpage.html index e3717ac0..42f5b22 100644 --- a/chrome/browser/resources/ash/settings/os_search_page/search_subpage.html +++ b/chrome/browser/resources/ash/settings/os_search_page/search_subpage.html
@@ -44,16 +44,14 @@ label="$i18n{quickAnswersDefinitionEnable}" deep-link-focus-id$="[[Setting.kQuickAnswersDefinition]]"> </settings-toggle-button> - <template is="dom-if" if="[[!quickAnswersTranslationDisabled_]]" restamp> - <settings-toggle-button id="quick-answers-translation-enable" - class="hr subsection" - pref="{{prefs.settings.quick_answers.translation.enabled}}" - label="$i18n{quickAnswersTranslationEnable}" - sub-label-with-link="[[translationSubLabel_]]" - on-sub-label-link-clicked="onSettingsLinkClick_" - deep-link-focus-id$="[[Setting.kQuickAnswersTranslation]]"> - </settings-toggle-button> - </template> + <settings-toggle-button id="quick-answers-translation-enable" + class="hr subsection" + pref="{{prefs.settings.quick_answers.translation.enabled}}" + label="$i18n{quickAnswersTranslationEnable}" + sub-label-with-link="[[translationSubLabel_]]" + on-sub-label-link-clicked="onSettingsLinkClick_" + deep-link-focus-id$="[[Setting.kQuickAnswersTranslation]]"> + </settings-toggle-button> <settings-toggle-button id="quick-answers-unit-conversion-enable" class="hr subsection" pref="{{prefs.settings.quick_answers.unit_conversion.enabled}}"
diff --git a/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts b/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts index 7937586..4edeeee6 100644 --- a/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts
@@ -48,13 +48,6 @@ static get properties() { return { - quickAnswersTranslationDisabled_: { - type: Boolean, - value() { - return loadTimeData.getBoolean('quickAnswersTranslationDisabled'); - }, - }, - quickAnswersSubToggleEnabled_: { type: Boolean, value() { @@ -83,7 +76,6 @@ private quickAnswersSubLabel_: string; private quickAnswersSubToggleEnabled_: boolean; - private quickAnswersTranslationDisabled_: boolean; private translationSubLabel_: string; constructor() {
diff --git a/chrome/browser/resources/certificate_manager/BUILD.gn b/chrome/browser/resources/certificate_manager/BUILD.gn index d4c84ae..291ecd4 100644 --- a/chrome/browser/resources/certificate_manager/BUILD.gn +++ b/chrome/browser/resources/certificate_manager/BUILD.gn
@@ -23,7 +23,6 @@ "favicon.svg", ] web_component_files = [ - "certificate_list.ts", "certificate_manager.ts", "certificate_subpage.ts", "crs_section.ts", @@ -36,6 +35,8 @@ "certificate_entry.ts", "certificate_info_dialog.html.ts", "certificate_info_dialog.ts", + "certificate_list.html.ts", + "certificate_list.ts", "certificate_password_dialog.html.ts", "certificate_password_dialog.ts", "certificates_browser_proxy.ts", @@ -48,7 +49,9 @@ icons_html_files = [ "certificate_manager_icons.html" ] css_files = [ "certificate_entry.css", + "certificate_list.css", "certificate_manager_style.css", + "certificate_manager_style_lit.css", ] if (is_chromeos) {
diff --git a/chrome/browser/resources/certificate_manager/certificate_list.css b/chrome/browser/resources/certificate_manager/certificate_list.css new file mode 100644 index 0000000..de07b22 --- /dev/null +++ b/chrome/browser/resources/certificate_manager/certificate_list.css
@@ -0,0 +1,30 @@ +/* Copyright 2026 The Chromium Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +/* #css_wrapper_metadata_start + * #type=style-lit + * #import=//resources/cr_elements/cr_shared_vars.css.js + * #import=//resources/cr_elements/cr_shared_style_lit.css.js + * #import=./certificate_manager_style_lit.css.js + * #scheme=relative + * #include=cr-shared-style-lit certificate-manager-style-lit + * #css_wrapper_metadata_end */ + +.header-buttons { + margin-left: auto; +} + +.no-certs { + border-top: none; +} + +/* Used when list is embedded in a subpage; added in .ts code */ +.subpage-padding { + padding: 0 var(--cr-section-padding); +} + +.list-title { + display: flex; + align-items: center; +}
diff --git a/chrome/browser/resources/certificate_manager/certificate_list.html b/chrome/browser/resources/certificate_manager/certificate_list.html deleted file mode 100644 index 039f4a5..0000000 --- a/chrome/browser/resources/certificate_manager/certificate_list.html +++ /dev/null
@@ -1,63 +0,0 @@ -<style include="cr-shared-style certificate-manager-style"> - - .header-buttons { - margin-left: auto; - } - - .no-certs { - border-top: none; - } - - /* Used when list is embedded in a subpage; added in .ts code */ - .subpage-padding { - padding: 0 var(--cr-section-padding); - } - - .list-title { - display: flex; - align-items: center; - } -</style> -<div hidden$="[[hideEverything_]]"> - <div id="listHeader" class="section-title list-title first" - hidden$="[[hideHeader]]"> - [[headerText]] - <div class="header-buttons"> - <cr-button hidden$="[[!showImport]]" id="importCert" - aria-label="[[i18n('certificateManagerV2ImportButtonAriaLabel', headerText)]]" - on-click="onImportCertClick_"> - $i18n{certificateManagerV2ImportButtonLabel} - </cr-button> - <cr-button hidden$="[[!showImportAndBind]]" id="importAndBindCert" - aria-label="[[i18n('certificateManagerV2ImportAndBindButtonAriaLabel', headerText)]]" - on-click="onImportAndBindCertClick_"> - $i18n{certificateManagerV2ImportAndBindButtonLabel} - </cr-button> - <cr-button hidden$="[[hideExportButton_(hideExport, hasCerts_)]]" - aria-label="[[i18n('certificateManagerV2ExportButtonAriaLabel', headerText)]]" - id="exportCerts" on-click="onExportCertsClick_"> - $i18n{certificateManagerV2ExportButtonLabel} - </cr-button> - </div> - <cr-expand-button id="expandButton" expanded="{{expanded_}}" no-hover - aria-label="[[i18n('certificateManagerV2ListExpandAriaLabel', headerText)]]" - hidden$="[[hideCollapseButton_(noCollapse, hasCerts_)]]"> - </cr-expand-button> - </div> - - <cr-collapse id="certs" opened="[[expanded_]]" aria-live="polite"> - <template is="dom-repeat" items="[[certificates_]]"> - <certificate-entry - cert-source="[[certSource]]" - display-name="[[item.displayName]]" - sha256hash-hex="[[item.sha256hashHex]]" - show-edit-icon="[[certMetadataEditable]]" - is-deletable="[[item.isDeletable]]" - on-delete-result="onDeleteResult_"> - </certificate-entry> - </template> - <div id="noCertsRow" class="cr-row no-certs" hidden="[[hasCerts_]]"> - $i18n{certificateManagerV2NoCertificatesRow} - </div> - </cr-collapse> -</div>
diff --git a/chrome/browser/resources/certificate_manager/certificate_list.html.ts b/chrome/browser/resources/certificate_manager/certificate_list.html.ts new file mode 100644 index 0000000..e6e4683 --- /dev/null +++ b/chrome/browser/resources/certificate_manager/certificate_list.html.ts
@@ -0,0 +1,66 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {html} from '//resources/lit/v3_0/lit.rollup.js'; + +import type {CertificateListElement} from './certificate_list.js'; + +export function getHtml(this: CertificateListElement) { + // clang-format off + return html`<!--_html_template_start_--> +<div ?hidden="${this.shouldHideEverything_()}"> + <div id="listHeader" + class="section-title list-title first + ${this.getListHeaderAdditionalClass_()}" + ?hidden="${this.hideHeader}"> + ${this.headerText} + <div class="header-buttons"> + <cr-button ?hidden="${!this.showImport}" id="importCert" + aria-label="${this.i18n('certificateManagerV2ImportButtonAriaLabel', + this.headerText)}" + @click="${this.onImportCertClick_}"> + $i18n{certificateManagerV2ImportButtonLabel} + </cr-button> + <cr-button ?hidden="${!this.showImportAndBind}" id="importAndBindCert" + aria-label="${this.i18n( + 'certificateManagerV2ImportAndBindButtonAriaLabel', + this.headerText)}" + @click="${this.onImportAndBindCertClick_}"> + $i18n{certificateManagerV2ImportAndBindButtonLabel} + </cr-button> + <cr-button ?hidden="${this.hideExportButton_()}" + aria-label="${this.i18n('certificateManagerV2ExportButtonAriaLabel', + this.headerText)}" + id="exportCerts" @click="${this.onExportCertsClick_}"> + $i18n{certificateManagerV2ExportButtonLabel} + </cr-button> + </div> + <cr-expand-button id="expandButton" ?expanded="${this.expanded_}" no-hover + aria-label="${this.i18n('certificateManagerV2ListExpandAriaLabel', + this.headerText)}" + ?hidden="${this.hideCollapseButton_()}" + @expanded-changed="${this.onExpandedChanged_}"> + </cr-expand-button> + </div> + + <cr-collapse id="certs" ?opened="${this.expanded_}" aria-live="polite" + class="${this.getCertsClass_()}"> + ${this.certificates_.map(item => html` + <certificate-entry + .certSource="${this.certSource}" + .displayName="${item.displayName}" + .sha256hashHex="${item.sha256hashHex}" + ?show-edit-icon="${this.certMetadataEditable}" + ?is-deletable="${item.isDeletable}" + @delete-result="${this.onDeleteResult_}"> + </certificate-entry> + `)} + <div id="noCertsRow" class="cr-row no-certs" ?hidden="${this.hasCerts_}"> + $i18n{certificateManagerV2NoCertificatesRow} + </div> + </cr-collapse> +</div> +<!--_html_template_end_-->`; + // clang-format on +}
diff --git a/chrome/browser/resources/certificate_manager/certificate_list.ts b/chrome/browser/resources/certificate_manager/certificate_list.ts index da55d195..41a67c7 100644 --- a/chrome/browser/resources/certificate_manager/certificate_list.ts +++ b/chrome/browser/resources/certificate_manager/certificate_list.ts
@@ -14,22 +14,22 @@ import '/strings.m.js'; import './certificate_entry.js'; -import './certificate_manager_style.css.js'; import '//resources/cr_elements/cr_expand_button/cr_expand_button.js'; import '//resources/cr_elements/cr_button/cr_button.js'; import '//resources/cr_elements/cr_collapse/cr_collapse.js'; -import '//resources/cr_elements/cr_shared_style.css.js'; -import '//resources/cr_elements/cr_shared_vars.css.js'; import type {CrCollapseElement} from '//resources/cr_elements/cr_collapse/cr_collapse.js'; -import {I18nMixin} from '//resources/cr_elements/i18n_mixin.js'; -import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {I18nMixinLit} from '//resources/cr_elements/i18n_mixin_lit.js'; +import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js'; +import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js'; -import {getTemplate} from './certificate_list.html.js'; -import type {ActionResult, CertificateSource, SummaryCertInfo} from './certificate_manager.mojom-webui.js'; +import {getCss} from './certificate_list.css.js'; +import {getHtml} from './certificate_list.html.js'; +import {CertificateSource} from './certificate_manager.mojom-webui.js'; +import type {ActionResult, SummaryCertInfo} from './certificate_manager.mojom-webui.js'; import {CertificatesBrowserProxy} from './certificates_browser_proxy.js'; -const CertificateListElementBase = I18nMixin(PolymerElement); +const CertificateListElementBase = I18nMixinLit(CrLitElement); export interface CertificateListElement { $: { @@ -48,112 +48,60 @@ return 'certificate-list'; } - static get template() { - return getTemplate(); + static override get styles() { + return getCss(); } - static get properties() { + override render() { + return getHtml.bind(this)(); + } + + static override get properties() { return { - certSource: Number, - headerText: String, - - showImport: { - type: Boolean, - value: false, - }, - - showImportAndBind: { - type: Boolean, - value: false, - }, - - // True if the list should not be collapsible. - // Empty lists will always not be collapsible. - noCollapse: { - type: Boolean, - value: false, - }, - - // True if the export button should be hidden. - // Export button may also be hidden if there are no certs in the list. - hideExport: { - type: Boolean, - value: false, - }, - - // True if the entire list (including the header) should be hidden if the - // list is empty. - hideIfEmpty: { - type: Boolean, - value: false, - }, - - // True if the header should be hidden. This will make the list - // non-collapsible. - hideHeader: { - type: Boolean, - value: false, - }, - - // True if the cert metadata is editable - certMetadataEditable: { - type: Boolean, - value: false, - }, - - inSubpage: { - type: Boolean, - value: false, - }, - - expanded_: { - type: Boolean, - value: true, - }, - - certificates_: { - type: Array, - value: () => [], - }, - - hideEverything_: { - type: Boolean, - computed: 'computeHideEverything_(certificates_)', - }, - - hasCerts_: { - type: Boolean, - computed: 'computeHasCerts_(certificates_)', - }, + certSource: {type: Number}, + headerText: {type: String}, + showImport: {type: Boolean}, + showImportAndBind: {type: Boolean}, + noCollapse: {type: Boolean}, + hideExport: {type: Boolean}, + hideIfEmpty: {type: Boolean}, + hideHeader: {type: Boolean}, + certMetadataEditable: {type: Boolean}, + inSubpage: {type: Boolean}, + expanded_: {type: Boolean}, + certificates_: {type: Array}, + hasCerts_: {type: Boolean}, }; } - declare certSource: CertificateSource; - declare headerText: string; - declare certMetadataEditable: boolean; - declare showImport: boolean; - declare showImportAndBind: boolean; - declare hideExport: boolean; - declare hideHeader: boolean; - declare inSubpage: boolean; - declare noCollapse: boolean; - declare hideIfEmpty: boolean; - declare private expanded_: boolean; - declare private hideEverything_: boolean; - declare private certificates_: SummaryCertInfo[]; - declare private hasCerts_: boolean; + accessor certSource: CertificateSource = CertificateSource.MIN_VALUE; + accessor headerText: string = ''; + accessor showImport: boolean = false; + accessor showImportAndBind: boolean = false; + accessor noCollapse: boolean = false; + accessor hideExport: boolean = false; + accessor hideIfEmpty: boolean = false; + accessor hideHeader: boolean = false; + accessor certMetadataEditable: boolean = false; + accessor inSubpage: boolean = false; + protected accessor expanded_: boolean = true; + protected accessor certificates_: SummaryCertInfo[] = []; + protected accessor hasCerts_: boolean = false; - override ready() { - super.ready(); + override willUpdate(changedProperties: PropertyValues<this>) { + super.willUpdate(changedProperties); - this.refreshCertificates(); - - if (!this.inSubpage) { - this.$.certs.classList.add('card'); + const changedPrivateProperties = + changedProperties as Map<PropertyKey, unknown>; + if (changedPrivateProperties.has('certificates_')) { + this.hasCerts_ = this.certificates_.length > 0; } - if (this.inSubpage) { - this.$.listHeader.classList.add('subpage-padding'); - } + } + + override firstUpdated(changedProperties: PropertyValues<this>) { + super.firstUpdated(changedProperties); + + this.refreshCertificates_(); const proxy = CertificatesBrowserProxy.getInstance(); proxy.callbackRouter.triggerReload.addListener( @@ -162,11 +110,11 @@ private onRefreshRequested_(certSources: CertificateSource[]) { if (certSources.includes(this.certSource)) { - this.refreshCertificates(); + this.refreshCertificates_(); } } - private refreshCertificates() { + protected refreshCertificates_() { CertificatesBrowserProxy.getInstance() .handler.getCertificates(this.certSource) .then((results: {certs: SummaryCertInfo[]}) => { @@ -174,14 +122,14 @@ }); } - private onExportCertsClick_(e: Event) { + protected onExportCertsClick_(e: Event) { // Export button click shouldn't collapse the list as well. e.stopPropagation(); CertificatesBrowserProxy.getInstance().handler.exportCertificates( this.certSource); } - private onImportCertClick_(e: Event) { + protected onImportCertClick_(e: Event) { // Import button click shouldn't collapse the list as well. e.stopPropagation(); CertificatesBrowserProxy.getInstance() @@ -189,7 +137,7 @@ .then(this.handleImportResult.bind(this)); } - private onImportAndBindCertClick_(e: Event) { + protected onImportAndBindCertClick_(e: Event) { // Import button click shouldn't collapse the list as well. e.stopPropagation(); CertificatesBrowserProxy.getInstance() @@ -200,37 +148,43 @@ private handleImportResult(value: {result: ActionResult|null}) { if (value.result !== null && value.result.success !== undefined) { // On successful import, refresh the certificate list. - this.refreshCertificates(); + this.refreshCertificates_(); } - this.dispatchEvent(new CustomEvent( - 'import-result', - {composed: true, bubbles: true, detail: value.result})); + this.fire('import-result', value.result); } - private onDeleteResult_(e: CustomEvent<ActionResult|null>) { + protected onDeleteResult_(e: CustomEvent<ActionResult|null>) { const result = e.detail; if (result !== null && result.success !== undefined) { // On successful deletion, refresh the certificate list. - this.refreshCertificates(); + this.refreshCertificates_(); this.$.importCert.focus(); } } - private computeHasCerts_(): boolean { - return this.certificates_.length > 0; - } - - private computeHideEverything_(): boolean { + protected shouldHideEverything_(): boolean { return this.hideIfEmpty && this.certificates_.length === 0; } - private hideCollapseButton_(): boolean { + protected hideCollapseButton_(): boolean { return this.noCollapse || !this.hasCerts_; } - private hideExportButton_(): boolean { + protected hideExportButton_(): boolean { return this.hideExport || !this.hasCerts_; } + + protected onExpandedChanged_(e: CustomEvent<{value: boolean}>) { + this.expanded_ = e.detail.value; + } + + protected getListHeaderAdditionalClass_(): string { + return this.inSubpage ? 'subpage-padding' : ''; + } + + protected getCertsClass_(): string { + return this.inSubpage ? '' : 'card'; + } } declare global {
diff --git a/chrome/browser/resources/certificate_manager/certificate_manager_style.css b/chrome/browser/resources/certificate_manager/certificate_manager_style.css index 48949f8..5740038d 100644 --- a/chrome/browser/resources/certificate_manager/certificate_manager_style.css +++ b/chrome/browser/resources/certificate_manager/certificate_manager_style.css
@@ -8,28 +8,5 @@ * #import=//resources/cr_elements/cr_shared_vars.css.js * #css_wrapper_metadata_end */ -.section-title { - color: var(--cr-primary-text-color); - font-size: 108%; - font-weight: 400; - letter-spacing: .25px; - margin-bottom: 12px; - margin-top: var(--cr-section-vertical-margin); - outline: none; - padding-bottom: 4px; - padding-top: 8px; -} - -.page-title { - font-weight: 400; -} - -.card { - background-color: var(--cr-card-background-color); - border-radius: var(--cr-card-border-radius); - box-shadow: var(--cr-card-shadow); -} - -.cr-centered-card-container { - padding-bottom: 20px; -} +/* Purposefully empty since this style is generated at build time from the + * equivalent Lit version. */
diff --git a/chrome/browser/resources/certificate_manager/certificate_manager_style_lit.css b/chrome/browser/resources/certificate_manager/certificate_manager_style_lit.css new file mode 100644 index 0000000..8f720c9 --- /dev/null +++ b/chrome/browser/resources/certificate_manager/certificate_manager_style_lit.css
@@ -0,0 +1,35 @@ +/* Copyright 2026 The Chromium Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +/* #css_wrapper_metadata_start + * #type=style-lit + * #scheme=relative + * #import=//resources/cr_elements/cr_shared_vars.css.js + * #css_wrapper_metadata_end */ + +.section-title { + color: var(--cr-primary-text-color); + font-size: 108%; + font-weight: 400; + letter-spacing: .25px; + margin-bottom: 12px; + margin-top: var(--cr-section-vertical-margin); + outline: none; + padding-bottom: 4px; + padding-top: 8px; +} + +.page-title { + font-weight: 400; +} + +.card { + background-color: var(--cr-card-background-color); + border-radius: var(--cr-card-border-radius); + box-shadow: var(--cr-card-shadow); +} + +.cr-centered-card-container { + padding-bottom: 20px; +}
diff --git a/chrome/browser/resources/contextual_tasks/app.css b/chrome/browser/resources/contextual_tasks/app.css index 449e0a1..f004444 100644 --- a/chrome/browser/resources/contextual_tasks/app.css +++ b/chrome/browser/resources/contextual_tasks/app.css
@@ -10,6 +10,8 @@ :host { --color-composebox-cancel-button: rgba(10,10,10, 1); + --color-composebox-context-menu-checkbox-override: rgba(11, 87, 208, 1); + --color-composebox-context-menu-text: rgba(31, 31, 31, 1); /* The top border of matches/tab suggestions dropdown in context menu. */ --color-composebox-file-carousel-divider: rgba(210, 225, 252, 1); --color-composebox-file-carousel-remove-gradient-end: rgba(0, 0, 0, 0.2); @@ -42,6 +44,7 @@ --color-composebox-lens-active-background: rgba(225, 227, 232, 1); --color-composebox-zero-state-suggestions-background-hovered: rgba(247, 248, 250, 1); + --color-context-menu-hover-background: rgba(242, 242, 242, 1); --color-file-chip-remove-button-fill: rgba(255, 255, 255, 1); --color-full-file-chip-remove-button-hover-background: rgba(0, 0, 0, 0.6); @@ -180,6 +183,8 @@ :host([dark-mode_]) { --color-composebox-background: rgba(31, 31, 31, 1); --color-composebox-cancel-button: rgba(191, 191, 191, 1); + --color-composebox-context-menu-checkbox-override: rgba(168, 199, 250, 1); + --color-composebox-context-menu-text: rgba(227, 227, 227, 1); --color-composebox-file-carousel-divider: rgba(94, 94, 94, 1); --color-composebox-file-carousel-remove-gradient-end: rgba(100, 100, 100, 0.2); --color-composebox-file-carousel-remove-gradient-start: rgba(100, 100, 100, 0.2); @@ -201,9 +206,10 @@ --color-composebox-type-ahead-chip: rgba(199, 199, 199, 1); --color-composebox-voice-lens-button: rgba(191, 191, 191, 1); --color-composebox-lens-active-background: rgba(42, 43, 54, 1); - --color-searchbox-results-background-hovered: rgba(54, 54, 54, 1); --color-composebox-zero-state-suggestions-background-hovered: rgba(23, 24, 31, 1); + --color-context-menu-hover-background: rgba(53, 53, 53, 1); + --color-searchbox-results-background-hovered: rgba(54, 54, 54, 1); --color-suggestions-background-loading: rgba(60, 64, 67, 1); --color-zero-state-greeting: rgba(230, 232, 240, 1); --color-zero-state-match-icon: rgba(139, 140, 145, 1);
diff --git a/chrome/browser/resources/glic/glic_api/glic_api.ts b/chrome/browser/resources/glic/glic_api/glic_api.ts index f64b02b..10b0be1 100644 --- a/chrome/browser/resources/glic/glic_api/glic_api.ts +++ b/chrome/browser/resources/glic/glic_api/glic_api.ts
@@ -2760,6 +2760,8 @@ CAPTURE_REGION_HOTKEY = 20, // From the in-product-help (IPH) entrypoint. IPH = 21, + // User clicked an anchored contextual cue chip. + ANCHORED_CONTEXTUAL_CUE = 22, } ///////////////////////////////////////////////
diff --git a/chrome/browser/resources/intro/dice_app.html.ts b/chrome/browser/resources/intro/dice_app.html.ts index 460940a..3871ecdf 100644 --- a/chrome/browser/resources/intro/dice_app.html.ts +++ b/chrome/browser/resources/intro/dice_app.html.ts
@@ -4,9 +4,9 @@ import {html} from '//resources/lit/v3_0/lit.rollup.js'; -import type {DiceAppElement} from './dice_app.js'; +import type {IntroAppElement} from './dice_app.js'; -export function getHtml(this: DiceAppElement) { +export function getHtml(this: IntroAppElement) { return html`<!--_html_template_start_--> <cr-view-manager id="viewManager"> <div id="splash" slot="view">
diff --git a/chrome/browser/resources/intro/dice_app.ts b/chrome/browser/resources/intro/dice_app.ts index 9e89c9d..b8a0283 100644 --- a/chrome/browser/resources/intro/dice_app.ts +++ b/chrome/browser/resources/intro/dice_app.ts
@@ -12,13 +12,13 @@ import {getCss} from './dice_app.css.js'; import {getHtml} from './dice_app.html.js'; -export interface DiceAppElement { +export interface IntroAppElement { $: { viewManager: CrViewManagerElement, }; } -export class DiceAppElement extends CrLitElement { +export class IntroAppElement extends CrLitElement { static get is() { return 'intro-app'; } @@ -55,8 +55,8 @@ declare global { interface HTMLElementTagNameMap { - 'intro-app': DiceAppElement; + 'intro-app': IntroAppElement; } } -customElements.define(DiceAppElement.is, DiceAppElement); +customElements.define(IntroAppElement.is, IntroAppElement);
diff --git a/chrome/browser/resources/new_tab_page/action_chips/action_chips.html.ts b/chrome/browser/resources/new_tab_page/action_chips/action_chips.html.ts index 9e0cfd69..3d60343 100644 --- a/chrome/browser/resources/new_tab_page/action_chips/action_chips.html.ts +++ b/chrome/browser/resources/new_tab_page/action_chips/action_chips.html.ts
@@ -15,7 +15,9 @@ ${ this.actionChips_.length ? html` - <div class="action-chips-container"> + <div class="action-chips-container" + @contextmenu="${this.showBackground && this.showSimplifiedUI_ + ? this.onContextmenu_ : nothing}"> ${ this.actionChips_.map( (chip: ActionChip, index: number) => html` @@ -24,7 +26,8 @@ class="action-chip" data-index="${index}" title="${this.getChipTitle_(chip)}" - @click="${this.onClick_}"> + @click="${this.onClick_}" + @contextmenu="${this.onContextmenu_}"> <div class="action-chip-icon-container ${ this.getAdditionalIconClasses_(chip)}"> ${ @@ -57,6 +60,11 @@ </div> ` : nothing} </div> + <cr-action-menu id="actionMenu"> + <button class="dropdown-item" @click="${this.onDisableSuggestionClick_}"> + $i18n{disableSuggestion} + </button> + </cr-action-menu> <!--_html_template_end_-->`; // clang-format on }
diff --git a/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts b/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts index 92270ae..7a7536a 100644 --- a/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts +++ b/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts
@@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js'; +import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js'; import type {TabUpload} from 'chrome://resources/cr_components/composebox/common.js'; import {TabUploadOrigin} from 'chrome://resources/cr_components/composebox/common.js'; +import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js'; import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; @@ -48,6 +50,12 @@ const kActionChipsRetrievalStateChangedEvent = 'action-chips-retrieval-state-changed'; +export interface ActionChipsElement { + $: { + actionMenu: CrActionMenuElement, + }; +} + /** * The element for displaying Action Chips. */ @@ -190,6 +198,18 @@ this.actionChips_.filter((c) => c.suggestion !== chip.suggestion); } + protected onContextmenu_(e: MouseEvent) { + e.preventDefault(); + e.stopPropagation(); + this.$.actionMenu.showAt(e.target as HTMLElement); + } + + protected onDisableSuggestionClick_() { + this.$.actionMenu.close(); + this.handler.setActionChipsVisibility(false); + } + + protected getFaviconUrl_(url: string): string { const faviconUrl = new URL('chrome://favicon2/'); faviconUrl.searchParams.set('size', '24');
diff --git a/chrome/browser/resources/new_tab_page/modules/authentication/microsoft_auth_module.ts b/chrome/browser/resources/new_tab_page/modules/authentication/microsoft_auth_module.ts index d5d6aee..d00f5606 100644 --- a/chrome/browser/resources/new_tab_page/modules/authentication/microsoft_auth_module.ts +++ b/chrome/browser/resources/new_tab_page/modules/authentication/microsoft_auth_module.ts
@@ -36,7 +36,7 @@ */ export class MicrosoftAuthModuleElement extends MicrosoftAuthModuleElementBase { static get is() { - return 'ntp-microsoft-authentication-module'; + return 'ntp-microsoft-auth-module'; } static override get styles() { @@ -106,7 +106,7 @@ declare global { interface HTMLElementTagNameMap { - 'ntp-microsoft-authentication-module': MicrosoftAuthModuleElement; + 'ntp-microsoft-auth-module': MicrosoftAuthModuleElement; } }
diff --git a/chrome/browser/resources/omnibox_popup/aim_app.css b/chrome/browser/resources/omnibox_popup/aim_app.css index 4a70cda3..6fd6fbf6 100644 --- a/chrome/browser/resources/omnibox_popup/aim_app.css +++ b/chrome/browser/resources/omnibox_popup/aim_app.css
@@ -95,7 +95,7 @@ } cr-composebox::part(context-menu-and-tools) { - margin-top: 2px; + margin-top: var(--omnibox-context-menu-container-margin-top); } cr-composebox[searchbox-layout-mode='TallBottomContext']::part(context-menu-and-tools) {
diff --git a/chrome/browser/resources/omnibox_popup/app.css b/chrome/browser/resources/omnibox_popup/app.css index 9333214..1636f9af 100644 --- a/chrome/browser/resources/omnibox_popup/app.css +++ b/chrome/browser/resources/omnibox_popup/app.css
@@ -69,7 +69,7 @@ } .context-menu-container { - margin-top: 2px; + margin-top: var(--omnibox-context-menu-container-margin-top); } :host([searchbox-layout-mode_='TallBottomContext']) .context-menu-container:has(*),
diff --git a/chrome/browser/resources/omnibox_popup/omnibox_popup_shared_style.css b/chrome/browser/resources/omnibox_popup/omnibox_popup_shared_style.css index f202129..539b05d 100644 --- a/chrome/browser/resources/omnibox_popup/omnibox_popup_shared_style.css +++ b/chrome/browser/resources/omnibox_popup/omnibox_popup_shared_style.css
@@ -29,4 +29,5 @@ * TODO(crbug.com/450640041): Investigate webUI blink & views rendering * difference. */ --omnibox-font-size: 14.3px; + --omnibox-context-menu-container-margin-top: 2px; }
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts index 5cea6b7..f5cef48 100644 --- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts +++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts
@@ -138,12 +138,14 @@ private getMenuItemsForBookmarks_(): MenuItem[] { // TODO(crbug.com/40262319): Factor in URLs not available in incognito. let bookmarkCount = 0; - this.bookmarks_.forEach((bookmark) => { + // Filter out undefined bookmarks which might exist temporarily as the + // bookmarks tree is being manipulated. + this.bookmarks_.filter(bookmark => !!bookmark).forEach((bookmark) => { if (bookmark.url) { bookmarkCount += 1; } else if (bookmark.children) { bookmarkCount += - bookmark.children.filter((child) => !!child.url).length; + bookmark.children.filter((child) => child && !!child.url).length; } }); const menuItems: MenuItem[] = [
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts index 7fb971a8..6b5207f 100644 --- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts +++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
@@ -535,6 +535,9 @@ this.updatedElementIds_ = [bookmark.parentId]; this.set(`trackedProductInfos_.${bookmark.id}`, null); this.availableProductInfos_.delete(bookmark.id); + if (this.selectedBookmarks_[bookmark.id]) { + this.set(`selectedBookmarks_.${bookmark.id}`, false); + } // If the parent folder is visible, notify to ensure its displayed // child count is updated. @@ -1052,8 +1055,9 @@ const selectedEntries = Object.entries(this.selectedBookmarks_) .filter(([_id, selected]) => selected); const selectedIds = selectedEntries.map(([id, _selected]) => id); - return selectedIds.map( - (id) => this.bookmarksService_.findBookmarkWithId(id)!); + return selectedIds + .map((id) => this.bookmarksService_.findBookmarkWithId(id)!) + .filter(b => !!b); } private getSelectedBookmarksLength_(): number {
diff --git a/chrome/browser/resources/side_panel/read_anything/menus/simple_action_menu.ts b/chrome/browser/resources/side_panel/read_anything/menus/simple_action_menu.ts index 6f827e0c..9386c0f 100644 --- a/chrome/browser/resources/side_panel/read_anything/menus/simple_action_menu.ts +++ b/chrome/browser/resources/side_panel/read_anything/menus/simple_action_menu.ts
@@ -66,7 +66,9 @@ accessor label: string = ''; open(anchor: HTMLElement, showAtConfig?: ShowAtConfigPrefs) { - openMenu(this.$.lazyMenu.get(), anchor, showAtConfig); + openMenu( + this.$.lazyMenu.get(), anchor, showAtConfig, /* onShow= */ undefined, + this.nonModal); } close() {
diff --git a/chrome/browser/resources/side_panel/read_anything/shared/common.ts b/chrome/browser/resources/side_panel/read_anything/shared/common.ts index 70a9c00..a396ebb 100644 --- a/chrome/browser/resources/side_panel/read_anything/shared/common.ts +++ b/chrome/browser/resources/side_panel/read_anything/shared/common.ts
@@ -26,7 +26,8 @@ export function openMenu( menuToOpen: CrActionMenuElement, target: HTMLElement, - showAtConfig?: ShowAtConfigPrefs, onShow?: () => void) { + showAtConfig?: ShowAtConfigPrefs, onShow?: () => void, + isSubmenu: boolean = false) { // The button should stay active while the menu is open and deactivate when // the menu closes. menuToOpen.addEventListener('close', () => { @@ -53,7 +54,6 @@ }, showAtConfig)); - const isSubmenu = menuToOpen.nonModal; // We manually override submenu positions here because cr-action-menu's // native side-collision aggressively flips the entire menu. We must do // this after showAt() because <cr-lazy-render> keeps offsetWidth at 0
diff --git a/chrome/browser/search_integrity/search_integrity.cc b/chrome/browser/search_integrity/search_integrity.cc index 6f48af9..c2069bfd 100644 --- a/chrome/browser/search_integrity/search_integrity.cc +++ b/chrome/browser/search_integrity/search_integrity.cc
@@ -50,8 +50,10 @@ for (auto piece : base::SplitStringPiece(text, base::kWhitespaceUTF16, base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { - if (piece.length() >= kMinWordLength) { - words.push_back(base::i18n::ToLower(piece)); + std::u16string cleaned_piece; + base::RemoveChars(piece, u".,!?:;\"'()[]{}<>-", &cleaned_piece); + if (cleaned_piece.length() >= kMinWordLength) { + words.push_back(base::i18n::ToLower(cleaned_piece)); } } return words;
diff --git a/chrome/browser/search_integrity/search_integrity_unittest.cc b/chrome/browser/search_integrity/search_integrity_unittest.cc index 5d79c384..d0dd1ca 100644 --- a/chrome/browser/search_integrity/search_integrity_unittest.cc +++ b/chrome/browser/search_integrity/search_integrity_unittest.cc
@@ -236,6 +236,20 @@ EXPECT_TRUE(report.is_default_custom_with_matching_policy_engine); } +TEST_F(SearchIntegrityTest, IsNameMatch_PunctuationIsIgnored) { + // "Yahoo!" and "Yahoo" should match because "!" is stripped. + TemplateURL* custom_engine = + AddSearchEngine(u"Goog", "http://custom.goog.com"); + AddSearchEngine(u"Goog!", "http://policy.goog.com", + /*created_by_policy=*/true); + SetDefaultSearchProvider(custom_engine); + + SearchIntegrityReport report = CheckSearchEnginesReport(); + + EXPECT_TRUE(report.is_default_custom); + EXPECT_TRUE(report.is_default_custom_with_matching_policy_engine); +} + TEST_F(SearchIntegrityTest, Histograms_LoggedCorrectly) { base::HistogramTester histogram_tester;
diff --git a/chrome/browser/ssl/ssl_prerender_browsertest.cc b/chrome/browser/ssl/ssl_prerender_browsertest.cc index ec9fc6d..365542ae 100644 --- a/chrome/browser/ssl/ssl_prerender_browsertest.cc +++ b/chrome/browser/ssl/ssl_prerender_browsertest.cc
@@ -33,6 +33,7 @@ #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/request_handler_util.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" #include "url/gurl.h" using chrome_browser_interstitials::IsShowingSSLInterstitial; @@ -79,11 +80,22 @@ } // namespace -class SSLPrerenderTest : public InProcessBrowserTest { +// With the parameter being true, `kPrerenderActivationByFormSubmission` will be +// enabled to support non mixed prerender form submission. +class SSLPrerenderTest : public InProcessBrowserTest, + public testing::WithParamInterface<bool> { public: SSLPrerenderTest() : prerender_helper_(base::BindRepeating(&SSLPrerenderTest::web_contents, - base::Unretained(this))) {} + base::Unretained(this))) { + if (GetParam()) { + feature_list_.InitAndEnableFeature( + blink::features::kPrerenderActivationByFormSubmission); + } else { + feature_list_.InitAndDisableFeature( + blink::features::kPrerenderActivationByFormSubmission); + } + } ~SSLPrerenderTest() override = default; void SetUpOnMainThread() override { @@ -96,6 +108,9 @@ } content::test::PrerenderTestHelper prerender_helper_; + + private: + base::test::ScopedFeatureList feature_list_; }; class SecurityVisibleStateObserver : public content::WebContentsObserver { @@ -111,12 +126,14 @@ bool is_visible_state_changed_ = false; }; +INSTANTIATE_TEST_SUITE_P(All, SSLPrerenderTest, ::testing::Bool()); + // Verifies that a certificate error in a prerendered page causes cancelation // of prerendering without showing an interstitial. // TODO(bokan): In the future, when prerendering supports cross origin // triggering, this test can be more straightforward by using one server for // the initial page and another, with bad certs, for the prerendering page. -IN_PROC_BROWSER_TEST_F(SSLPrerenderTest, TestNoInterstitialInPrerender) { +IN_PROC_BROWSER_TEST_P(SSLPrerenderTest, TestNoInterstitialInPrerender) { auto server = CreateExpiredCertServer(GetChromeTestDataDir()); ASSERT_TRUE(server->Start()); @@ -180,7 +197,7 @@ // triggering, this test can be more straightforward by using one server for // the initial page and another, with bad certs, for the prerendering page. // TODO(crbug.com/40923072): the test has been flaky across platforms. -IN_PROC_BROWSER_TEST_F(SSLPrerenderTest, +IN_PROC_BROWSER_TEST_P(SSLPrerenderTest, DISABLED_TestNoInterstitialInPrerenderSW) { auto server = CreateExpiredCertServer(GetChromeTestDataDir()); ASSERT_TRUE(server->Start()); @@ -245,7 +262,7 @@ // Prerenders a page that tries to submit an insecure form and checks that this // cancels the prerender instead. -IN_PROC_BROWSER_TEST_F(SSLPrerenderTest, +IN_PROC_BROWSER_TEST_P(SSLPrerenderTest, InsecureFormSubmissionCancelsPrerender) { auto https_server = CreateHTTPSServer(GetChromeTestDataDir()); ASSERT_TRUE(https_server->Start()); @@ -296,7 +313,7 @@ // Prerenders a page that tries to submit an insecure form and checks that this // cancels the prerender even if the primary page is proceeding on an insecure // form. -IN_PROC_BROWSER_TEST_F(SSLPrerenderTest, +IN_PROC_BROWSER_TEST_P(SSLPrerenderTest, InsecureFormSubmissionCancelsPrerenderEvenIfProceeding) { auto https_server = CreateHTTPSServer(GetChromeTestDataDir()); ASSERT_TRUE(https_server->Start()); @@ -372,7 +389,7 @@ } } -IN_PROC_BROWSER_TEST_F(SSLPrerenderTest, +IN_PROC_BROWSER_TEST_P(SSLPrerenderTest, TestNoVisibleStateChangedOnInitialPrerendering) { auto https_server = CreateHTTPSServer(GetChromeTestDataDir()); ASSERT_TRUE(https_server->Start()); @@ -404,3 +421,42 @@ EXPECT_TRUE(visible_state_observer.is_visible_state_changed()); } } + +// Prerenders a page that tries to submit a HTTPS form submission without a +// schema change and checks that this is proceeded normally. +IN_PROC_BROWSER_TEST_P(SSLPrerenderTest, HTTPSFormSubmissionIsAllowed) { + if (!GetParam()) { + GTEST_SKIP() << "This test requires the feature to be enabled"; + } + auto https_server = CreateHTTPSServer(GetChromeTestDataDir()); + ASSERT_TRUE(https_server->Start()); + + // Navigate to an initial page. + GURL url = https_server->GetURL("a.test", "/empty.html"); + ASSERT_TRUE(content::NavigateToURL(web_contents(), url)); + + // Trigger prerender with form submission. + GURL prerender_url = https_server->GetURL("a.test", "/english_page.html?"); + content::TestActivationManager activation_manager(web_contents(), + prerender_url); + prerender_helper_.AddPrerendersAsync( + {prerender_url}, + /*eagerness=*/std::nullopt, + /*no_vary_search_hint=*/std::nullopt, + /*target_hint=*/std::string(), + /*ruleset_tag=*/std::nullopt, + /*world_id=*/content::ISOLATED_WORLD_ID_GLOBAL, + /*form_submission=*/true); + content::test::PrerenderTestHelper::WaitForPrerenderLoadCompletion( + *web_contents(), prerender_url); + + ASSERT_TRUE(content::ExecJs(web_contents()->GetPrimaryMainFrame(), + content::JsReplace(R"( + const form = document.createElement('form'); + form.action = $1; + document.body.appendChild(form); + form.submit(); )", + prerender_url))); + activation_manager.WaitForNavigationFinished(); + EXPECT_TRUE(activation_manager.was_activated()); +}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 32f3d82c..f81e133 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -2165,7 +2165,6 @@ "//ash/webui/scanner_feedback_ui", "//ash/webui/scanning", "//ash/webui/settings/public/constants", - "//ash/webui/settings/public/constants:mojom", "//ash/webui/shimless_rma", "//ash/webui/shortcut_customization_ui", "//ash/webui/shortcut_customization_ui/backend/search:mojo_bindings", @@ -2438,7 +2437,6 @@ "//chrome/browser/ui/webui/ash/print_preview_cros", "//chrome/browser/ui/webui/ash/sanitize_dialog", "//chrome/browser/ui/webui/ash/settings", - "//chrome/browser/ui/webui/ash/settings/app_management", "//chrome/browser/ui/webui/ash/settings/pages", "//chrome/browser/ui/webui/ash/settings/pages/a11y", "//chrome/browser/ui/webui/ash/settings/pages/about", @@ -2675,7 +2673,6 @@ "//chrome/browser/ash/usb", "//chrome/browser/ui/ash/login", "//chrome/browser/ui/webui/ash/login", - "//chrome/browser/ui/webui/ash/settings/app_management", "//ui/base/mojom:ui_base_types", ] @@ -4534,8 +4531,6 @@ "views/tabs/tab_layout_state.h", "views/tabs/tab_search_button.cc", "views/tabs/tab_search_button.h", - "views/tabs/tab_search_container.cc", - "views/tabs/tab_search_container.h", "views/tabs/tab_slot_animation_delegate.cc", "views/tabs/tab_slot_animation_delegate.h", "views/tabs/tab_slot_controller.h", @@ -4663,6 +4658,10 @@ "views/web_apps/web_app_identity_update_confirmation_view.h", "views/web_apps/web_app_install_dialog_delegate.cc", "views/web_apps/web_app_install_dialog_delegate.h", + "views/web_apps/web_app_install_dialog_flow_view.cc", + "views/web_apps/web_app_install_dialog_flow_view.h", + "views/web_apps/web_app_install_flow_dialog_delegate.cc", + "views/web_apps/web_app_install_flow_dialog_delegate.h", "views/web_apps/web_app_install_not_supported_dialog.cc", "views/web_apps/web_app_launch_dialog.cc", "views/web_apps/web_app_modal_dialog_delegate.cc",
diff --git a/chrome/browser/ui/actions/chrome_action_id.h b/chrome/browser/ui/actions/chrome_action_id.h index ac894a39..6a06451 100644 --- a/chrome/browser/ui/actions/chrome_action_id.h +++ b/chrome/browser/ui/actions/chrome_action_id.h
@@ -605,6 +605,7 @@ E(kActionTabSearch, IDC_TAB_SEARCH) \ E(kActionSplitTab, IDC_SPLIT_TAB) \ E(kActionFederation) \ + E(kActionGlicContextualCueing) \ #define CHROME_ACTION_IDS \ CHROME_COMMON_ACTION_IDS \
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java index 03643422..aa1ac67f 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java
@@ -82,8 +82,8 @@ mModel.set( BackButtonProperties.CLICK_LISTENER, - (metaState) -> { - onBackPressed.onClickWithMeta(metaState); + (metaState, buttonState) -> { + onBackPressed.onClickWithMeta(metaState, buttonState); updateButtonEnabledState(); }); mModel.set(
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java index f4a9da1e..63c73c3 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java
@@ -16,6 +16,7 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.os.Looper; +import android.view.MotionEvent; import org.junit.Before; import org.junit.Rule; @@ -157,8 +158,15 @@ @Test public void testClick_shouldForwardCallToParent() { - mModel.get(BackButtonProperties.CLICK_LISTENER).onClickWithMeta(0); - verify(mOnBackPressed).onClickWithMeta(0); + mModel.get(BackButtonProperties.CLICK_LISTENER).onClickWithMeta(0, 0); + verify(mOnBackPressed).onClickWithMeta(0, 0); + } + + @Test + public void testMiddleClick_shouldForwardCallToParent() { + mModel.get(BackButtonProperties.CLICK_LISTENER) + .onClickWithMeta(0, MotionEvent.BUTTON_TERTIARY); + verify(mOnBackPressed).onClickWithMeta(0, MotionEvent.BUTTON_TERTIARY); } @Test
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/forward_button/ForwardButtonCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/forward_button/ForwardButtonCoordinator.java index 186e33f2..451cd24ab 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/forward_button/ForwardButtonCoordinator.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/forward_button/ForwardButtonCoordinator.java
@@ -7,6 +7,7 @@ import android.content.Context; import android.content.res.ColorStateList; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; import androidx.core.widget.ImageViewCompat; @@ -85,7 +86,9 @@ mTopUiThemeColorProvider.getBrandedColorScheme()); onIncognitoStateChanged(mIncognitoStateProvider.isIncognitoSelected()); - mImageButton.setClickCallback(metaState -> forward(metaState, "MobileToolbarForward")); + mImageButton.setClickCallback( + (metaState, buttonState) -> + forward(metaState, buttonState, "MobileToolbarForward")); mImageButton.setLongClickable(true); } @@ -167,13 +170,15 @@ * * @return Whether or not the current Tab did go forward. */ - protected boolean forward(int metaState, String reportingTagPrefix) { + protected boolean forward(int metaState, int buttonState, String reportingTagPrefix) { if (!mActivityLifecycleDispatcher.isNativeInitializationFinished()) return false; maybeUnfocusUrlBar(); boolean hasControl = (metaState & KeyEvent.META_CTRL_ON) != 0; boolean hasShift = (metaState & KeyEvent.META_SHIFT_ON) != 0; - if (hasControl && hasShift) { + boolean isMiddleClick = (buttonState & MotionEvent.BUTTON_TERTIARY) != 0; + + if ((hasControl && hasShift) || (isMiddleClick && hasShift)) { // Holding ALT is allowed as well (reference desktop behavior). // Note on recording user actions: "forward" is recorded regardless of whether it @@ -181,7 +186,7 @@ // https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java;l=196;drc=14aab80e079b7db3e85a8302da6d660bafeddfbc RecordUserAction.record(reportingTagPrefix + "InNewForegroundTab"); return mToolbarTabController.forwardInNewTab(/* foregroundNewTab= */ true); - } else if (hasControl) { + } else if (hasControl || isMiddleClick) { RecordUserAction.record(reportingTagPrefix + "InNewBackgroundTab"); return mToolbarTabController.forwardInNewTab(/* foregroundNewTab= */ false); } else if (hasShift) {
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java index fcb3bfe..5134dafc 100644 --- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java +++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
@@ -373,7 +373,7 @@ mBackButtonCoordinator = new BackButtonCoordinator( backButton, - (ignored) -> { + (metaState, buttonState) -> { if (mMediator != null) mMediator.goBack(); }, mThemeColorProvider,
diff --git a/chrome/browser/ui/ash/app_access/BUILD.gn b/chrome/browser/ui/ash/app_access/BUILD.gn index fc399f8..ffde60f9 100644 --- a/chrome/browser/ui/ash/app_access/BUILD.gn +++ b/chrome/browser/ui/ash/app_access/BUILD.gn
@@ -24,6 +24,8 @@ "//chrome/browser:browser_public_dependencies", "//chrome/browser/apps/app_service", "//chrome/browser/ash/privacy_hub", + "//chromeos/ash/components/browser_context_helper", + "//chromeos/ash/experiences/settings_ui", "//components/session_manager:base", ]
diff --git a/chrome/browser/ui/ash/app_access/app_access_notifier.cc b/chrome/browser/ui/ash/app_access/app_access_notifier.cc index 439ec9a6..e6d4407 100644 --- a/chrome/browser/ui/ash/app_access/app_access_notifier.cc +++ b/chrome/browser/ui/ash/app_access/app_access_notifier.cc
@@ -9,11 +9,11 @@ #include <string> #include <vector> -#include "app_access_notifier.h" #include "ash/constants/ash_features.h" #include "ash/system/privacy/privacy_indicators_controller.h" #include "ash/system/privacy_hub/camera_privacy_switch_controller.h" #include "base/check.h" +#include "base/check_deref.h" #include "base/functional/bind.h" #include "base/metrics/histogram_functions.h" #include "base/strings/string_util.h" @@ -23,6 +23,8 @@ #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/chrome_pages.h" +#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h" +#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" #include "components/account_id/account_id.h" #include "components/services/app_service/public/cpp/app_capability_access_cache.h" #include "components/services/app_service/public/cpp/app_capability_access_cache_wrapper.h" @@ -30,6 +32,7 @@ #include "components/services/app_service/public/cpp/app_types.h" #include "components/session_manager/core/session_manager.h" #include "components/session_manager/session_manager_types.h" +#include "components/user_manager/user.h" namespace { @@ -278,9 +281,15 @@ DCHECK(app_type != apps::AppType::kSystemWeb); if (app_type == apps::AppType::kWeb) { - chrome::ShowAppManagementPage(profile, app_id, - ash::settings::AppManagementEntryPoint:: - kPrivacyIndicatorsNotificationSettings); + const user_manager::User* user = + ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile); + ash::SettingsAppManager::Get()->Open( + CHECK_DEREF(user), + ash::SettingsAppManager::OpenParams{ + .sub_page = + ash::SettingsAppManager::CreateAppManagementPagePath(app_id), + .entry_point = ash::SettingsAppManager::EntryPoint:: + kPrivacyIndicatorsNotificationSettings}); } else { apps::AppServiceProxyFactory::GetForProfile(profile)->OpenNativeSettings( app_id);
diff --git a/chrome/browser/ui/ash/birch/birch_release_notes_provider.cc b/chrome/browser/ui/ash/birch/birch_release_notes_provider.cc index c24635a9..4e55c4c1 100644 --- a/chrome/browser/ui/ash/birch/birch_release_notes_provider.cc +++ b/chrome/browser/ui/ash/birch/birch_release_notes_provider.cc
@@ -15,7 +15,6 @@ #include "base/command_line.h" #include "base/time/time.h" #include "chrome/browser/ash/app_list/search/help_app_zero_state_provider.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ui/ash/login/login_display_host_common.cc b/chrome/browser/ui/ash/login/login_display_host_common.cc index c6ef67629..3c6f819 100644 --- a/chrome/browser/ui/ash/login/login_display_host_common.cc +++ b/chrome/browser/ui/ash/login/login_display_host_common.cc
@@ -74,7 +74,6 @@ #include "chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.h" #include "chrome/browser/ui/webui/ash/login/user_allowlist_check_screen_handler.h" #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "chromeos/ash/components/attestation/attestation_flow_adaptive.h" #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
diff --git a/chrome/browser/ui/ash/login/login_display_host_webui.cc b/chrome/browser/ui/ash/login/login_display_host_webui.cc index 06c9902..adc47463 100644 --- a/chrome/browser/ui/ash/login/login_display_host_webui.cc +++ b/chrome/browser/ui/ash/login/login_display_host_webui.cc
@@ -80,7 +80,6 @@ #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/audio/public/cpp/sounds/sounds_manager.h" #include "chromeos/ash/components/audio/sounds.h" #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
diff --git a/chrome/browser/ui/ash/login/login_ui_pref_controller.cc b/chrome/browser/ui/ash/login/login_ui_pref_controller.cc index f49fb01..b745c86e 100644 --- a/chrome/browser/ui/ash/login/login_ui_pref_controller.cc +++ b/chrome/browser/ui/ash/login/login_ui_pref_controller.cc
@@ -12,7 +12,6 @@ #include "base/logging.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/ash/system/input_device_settings.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/geolocation/system_location_provider.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc b/chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc index 4fc34bd..4364d16 100644 --- a/chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc +++ b/chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/ash/system/fake_input_device_settings.h" #include "chrome/browser/ash/system/input_device_settings.h" #include "chrome/browser/browser_process.h" -#include "chrome/common/pref_names.h" #include "components/policy/policy_constants.h" #include "components/policy/proto/chrome_device_policy.pb.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ui/ash/login/user_adding_screen_browsertest.cc b/chrome/browser/ui/ash/login/user_adding_screen_browsertest.cc index fb75d12..8c8ee92 100644 --- a/chrome/browser/ui/ash/login/user_adding_screen_browsertest.cc +++ b/chrome/browser/ui/ash/login/user_adding_screen_browsertest.cc
@@ -22,7 +22,6 @@ #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ash/login/login_display_host.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/prefs/pref_service.h" #include "components/session_manager/core/session_manager.h"
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos.cc index 2115620e..28e2313c 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos.cc +++ b/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" #include "chrome/browser/ui/ash/session/session_controller_client_impl.h" #include "chrome/browser/ui/dialogs/browser_dialogs.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "components/account_id/account_id.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ui/ash/network/network_portal_signin_controller.cc b/chrome/browser/ui/ash/network/network_portal_signin_controller.cc index 3218f707..2bafa5b 100644 --- a/chrome/browser/ui/ash/network/network_portal_signin_controller.cc +++ b/chrome/browser/ui/ash/network/network_portal_signin_controller.cc
@@ -18,7 +18,6 @@ #include "chrome/browser/ui/dialogs/browser_dialogs.h" #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" #include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/network/network_event_log.h" #include "chromeos/ash/components/network/network_handler.h" #include "chromeos/ash/components/network/network_state_handler.h"
diff --git a/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc b/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc index 6d240a22..c2f09f5 100644 --- a/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc +++ b/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc
@@ -19,7 +19,6 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h" #include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ui/ash/shelf/BUILD.gn b/chrome/browser/ui/ash/shelf/BUILD.gn index e6e1b32..b455d381 100644 --- a/chrome/browser/ui/ash/shelf/BUILD.gn +++ b/chrome/browser/ui/ash/shelf/BUILD.gn
@@ -120,7 +120,6 @@ "//chrome/browser/ui/extensions", "//chrome/browser/ui/tabs:tab_enums", "//chrome/browser/ui/web_applications", - "//chrome/browser/ui/webui/ash/settings/app_management", "//chrome/browser/web_applications", "//chrome/common", "//chromeos/ash/components/browser_context_helper", @@ -130,6 +129,7 @@ "//chromeos/ash/experiences/arc:arc_app_constants", "//chromeos/ash/experiences/arc:arc_base_utils", "//chromeos/ash/experiences/arc:prefs", + "//chromeos/ash/experiences/settings_ui", "//chromeos/constants", "//chromeos/strings:strings_grit", "//chromeos/ui/wm",
diff --git a/chrome/browser/ui/ash/shelf/app_service/BUILD.gn b/chrome/browser/ui/ash/shelf/app_service/BUILD.gn index 37d1b7b..1d7c7a5 100644 --- a/chrome/browser/ui/ash/shelf/app_service/BUILD.gn +++ b/chrome/browser/ui/ash/shelf/app_service/BUILD.gn
@@ -57,12 +57,13 @@ "//chrome/browser/ui/ash/multi_user", "//chrome/browser/ui/browser_window", "//chrome/browser/ui/views/crostini", - "//chrome/browser/ui/webui/ash/settings/app_management", "//chrome/browser/web_applications", "//chromeos/ash/components/borealis", + "//chromeos/ash/components/browser_context_helper", "//chromeos/ash/experiences/arc", "//chromeos/ash/experiences/arc:arc_app_constants", "//chromeos/ash/experiences/arc/video_accelerator:protected_native_pixmap_query_client", + "//chromeos/ash/experiences/settings_ui", "//chromeos/crosapi/cpp:crosapi_constants", "//chromeos/ui/base", "//components/account_id",
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc index fcc48a8..9032131a 100644 --- a/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc +++ b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
@@ -7,6 +7,7 @@ #include "ash/public/cpp/app_menu_constants.h" #include "ash/public/cpp/new_window_delegate.h" #include "ash/strings/grit/ash_strings.h" +#include "base/check_deref.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/functional/callback_helpers.h" @@ -42,10 +43,12 @@ #include "chrome/browser/ui/ash/shelf/shelf_context_menu.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/views/crostini/crostini_app_restart_dialog.h" -#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h" #include "chrome/grit/generated_resources.h" +#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h" +#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" #include "components/app_constants/constants.h" #include "components/services/app_service/public/cpp/app_types.h" +#include "components/user_manager/user.h" #include "content/public/browser/context_menu_params.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/launch_util.h" @@ -525,9 +528,16 @@ void AppServiceShelfContextMenu::ShowAppInfo() { if (app_type_ == apps::AppType::kArc) { - chrome::ShowAppManagementPage( - controller()->profile(), item().id.app_id, - ash::settings::AppManagementEntryPoint::kShelfContextMenuAppInfoArc); + const user_manager::User* user = + ash::BrowserContextHelper::Get()->GetUserByBrowserContext( + controller()->profile()); + ash::SettingsAppManager::Get()->Open( + CHECK_DEREF(user), + ash::SettingsAppManager::OpenParams{ + .sub_page = ash::SettingsAppManager::CreateAppManagementPagePath( + item().id.app_id), + .entry_point = ash::SettingsAppManager::EntryPoint:: + kShelfContextMenuAppInfoArc}); return; }
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc index 7ac3e0b..02718d6 100644 --- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc +++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
@@ -76,7 +76,6 @@ #include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/chrome_pages.h" -#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/pref_names.h" @@ -88,6 +87,7 @@ #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h" #include "chromeos/ash/experiences/arc/arc_prefs.h" #include "chromeos/ash/experiences/arc/arc_util.h" +#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" #include "chromeos/constants/chromeos_features.h" #include "chromeos/strings/grit/chromeos_strings.h" #include "components/account_id/account_id.h" @@ -921,16 +921,24 @@ return; } + std::string sub_page; + std::optional<ash::SettingsAppManager::EntryPoint> entry_point; if (app_type == apps::AppType::kWeb || app_type == apps::AppType::kSystemWeb) { - chrome::ShowAppManagementPage( - profile_, app_id, - ash::settings::AppManagementEntryPoint::kShelfContextMenuAppInfoWebApp); + sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(app_id); + entry_point = + ash::SettingsAppManager::EntryPoint::kShelfContextMenuAppInfoWebApp; } else { - chrome::ShowAppManagementPage(profile_, app_id, - ash::settings::AppManagementEntryPoint:: - kShelfContextMenuAppInfoChromeApp); + sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(app_id); + entry_point = + ash::SettingsAppManager::EntryPoint::kShelfContextMenuAppInfoChromeApp; } + + const user_manager::User* user = + ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile_); + ash::SettingsAppManager::Get()->Open( + *user, ash::SettingsAppManager::OpenParams{.sub_page = sub_page, + .entry_point = entry_point}); } ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc index 3653155..6f02c3a 100644 --- a/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc +++ b/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc
@@ -50,7 +50,6 @@ #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/contents_web_view.h" #include "chrome/browser/ui/webui/ash/settings/pref_names.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/cryptohome/system_salt_getter.h" #include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h" #include "chromeos/ash/components/settings/cros_settings_names.h"
diff --git a/chrome/browser/ui/browser_element_identifiers.cc b/chrome/browser/ui/browser_element_identifiers.cc index d56d771..557b3d16 100644 --- a/chrome/browser/ui/browser_element_identifiers.cc +++ b/chrome/browser/ui/browser_element_identifiers.cc
@@ -172,7 +172,6 @@ DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabDeclutterButtonElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabSearchBubbleElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabSearchButtonElementId); -DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabSearchContainerElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabStripActionContainerElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabStripComboButtonElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabStripElementId);
diff --git a/chrome/browser/ui/browser_element_identifiers.h b/chrome/browser/ui/browser_element_identifiers.h index 6db6f6ef..947cd63 100644 --- a/chrome/browser/ui/browser_element_identifiers.h +++ b/chrome/browser/ui/browser_element_identifiers.h
@@ -215,7 +215,6 @@ DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabDeclutterButtonElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabSearchBubbleElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabSearchButtonElementId); -DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabSearchContainerElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabStripActionContainerElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabStripComboButtonElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabStripElementId);
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc index e05fb06..959fe88 100644 --- a/chrome/browser/ui/chrome_pages.cc +++ b/chrome/browser/ui/chrome_pages.cc
@@ -71,10 +71,8 @@ #if BUILDFLAG(IS_CHROMEOS) #include "ash/constants/ash_features.h" #include "ash/constants/webui_url_constants.h" -#include "ash/webui/settings/public/constants/routes.mojom.h" #include "ash/webui/settings/public/constants/routes_util.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" -#include "chrome/browser/ui/settings_window_manager_chromeos.h" #else #include "chrome/browser/ui/signin/signin_view_controller.h" #endif @@ -682,24 +680,6 @@ GURL(data_sharing::features::kActivityLogsURL.Get())); } -#if BUILDFLAG(IS_CHROMEOS) -void ShowAppManagementPage(Profile* profile, - const std::string& app_id, - ash::settings::AppManagementEntryPoint entry_point) { - // This histogram is also declared and used at chrome/browser/resources/ - // settings/chrome_os/os_apps_page/app_management_page/constants.js. - constexpr char kAppManagementEntryPointsHistogramName[] = - "AppManagement.EntryPoints"; - - base::UmaHistogramEnumeration(kAppManagementEntryPointsHistogramName, - entry_point); - std::string sub_page = base::StrCat( - {chromeos::settings::mojom::kAppDetailsSubpagePath, "?id=", app_id}); - chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(profile, - sub_page); -} -#endif // BUILDFLAG(IS_CHROMEOS) - #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) void ShowWebAppSettingsImpl(Browser* browser, Profile* profile,
diff --git a/chrome/browser/ui/chrome_pages.h b/chrome/browser/ui/chrome_pages.h index 78cbb21..b2b7d1cc 100644 --- a/chrome/browser/ui/chrome_pages.h +++ b/chrome/browser/ui/chrome_pages.h
@@ -23,10 +23,6 @@ #include "chrome/browser/signin/signin_promo.h" #endif -#if BUILDFLAG(IS_CHROMEOS) -#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h" -#endif - namespace apps { enum class LaunchSource; } @@ -158,12 +154,6 @@ // Shows the enterprise management info page in a browser tab. void ShowEnterpriseManagementPageInTabbedBrowser(Browser* browser); -#if BUILDFLAG(IS_CHROMEOS) -void ShowAppManagementPage(Profile* profile, - const std::string& app_id, - ash::settings::AppManagementEntryPoint entry_point); -#endif - #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) // Show chrome://app-settings/<app-id> page. void ShowWebAppSettings(Browser* browser,
diff --git a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc index 5896131a..b745a207 100644 --- a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc +++ b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
@@ -291,6 +291,7 @@ html_source->AddBoolean( "enableKeyboardSelection", lens::features::IsLensOverlayKeyboardSelectionEnabled()); + html_source->AddBoolean("enableMultiRegionSelection", false); LensOverlayController& controller = GetLensOverlayController(); html_source->AddDouble("invocationTime",
diff --git a/chrome/browser/ui/omnibox/omnibox_context_menu_controller_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_context_menu_controller_browsertest.cc index a79aa74..56e8d04 100644 --- a/chrome/browser/ui/omnibox/omnibox_context_menu_controller_browsertest.cc +++ b/chrome/browser/ui/omnibox/omnibox_context_menu_controller_browsertest.cc
@@ -61,6 +61,7 @@ {omnibox::kShowToolsAndModels.name, "true"}}}, {omnibox::kWebUIOmniboxPopup, {}}}, /*disabled_features=*/{omnibox::kAimServerEligibilityEnabled, + omnibox::kAimFuseboxEligibilityCheckEnabled, omnibox::kAimUsePecApi}); } @@ -291,7 +292,8 @@ {omnibox::kShowToolsAndModels.name, "true"}}}, {omnibox::kWebUIOmniboxPopup, {}}, {omnibox::kAimUsePecApi, {}}}, - /*disabled_features=*/{omnibox::kAimServerEligibilityEnabled}); + /*disabled_features=*/{omnibox::kAimServerEligibilityEnabled, + omnibox::kAimFuseboxEligibilityCheckEnabled}); } OmniboxContextMenuControllerPecBrowserTest(
diff --git a/chrome/browser/ui/omnibox/omnibox_next_features.cc b/chrome/browser/ui/omnibox/omnibox_next_features.cc index 106669f..bb6e543 100644 --- a/chrome/browser/ui/omnibox/omnibox_next_features.cc +++ b/chrome/browser/ui/omnibox/omnibox_next_features.cc
@@ -204,7 +204,7 @@ } auto* aim_service = AimEligibilityServiceFactory::GetForProfile(profile); - return aim_service && aim_service->IsAimEligible(); + return aim_service && aim_service->IsFuseboxEligible(); } bool IsContentSharingEnabled(
diff --git a/chrome/browser/ui/omnibox/omnibox_next_features_unittest.cc b/chrome/browser/ui/omnibox/omnibox_next_features_unittest.cc index f207b39a..74c7648 100644 --- a/chrome/browser/ui/omnibox/omnibox_next_features_unittest.cc +++ b/chrome/browser/ui/omnibox/omnibox_next_features_unittest.cc
@@ -259,6 +259,7 @@ is_aim_eligible_(is_aim_eligible) {} bool IsAimEligible() const override { return is_aim_eligible_; } + bool IsFuseboxEligible() const override { return is_aim_eligible_; } bool IsAimAllowedByDse() const override { return is_aim_eligible_; } private:
diff --git a/chrome/browser/ui/page_action/page_action_icon_type.cc b/chrome/browser/ui/page_action/page_action_icon_type.cc index 0f04c36..0d3b242 100644 --- a/chrome/browser/ui/page_action/page_action_icon_type.cc +++ b/chrome/browser/ui/page_action/page_action_icon_type.cc
@@ -60,6 +60,7 @@ // Page actions on the new framework that don't have an implementation on the legacy path // and don't have a feature param. switch (page_action) { + case PageActionIconType::kGlic: case PageActionIconType::kLensOverlay: case PageActionIconType::kMemorySaver: case PageActionIconType::kTranslate:
diff --git a/chrome/browser/ui/page_action/page_action_icon_type.h b/chrome/browser/ui/page_action/page_action_icon_type.h index 57aa911f..8a3c01d6 100644 --- a/chrome/browser/ui/page_action/page_action_icon_type.h +++ b/chrome/browser/ui/page_action/page_action_icon_type.h
@@ -55,7 +55,8 @@ kRecordReplay = 39, kIndigo = 40, kFederation = 41, - kMaxValue = kFederation, + kGlic = 42, + kMaxValue = kGlic, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/page/enums.xml:PageActionIconType) @@ -93,6 +94,7 @@ static_assert(static_cast<int>(PageActionIconType::kRecordReplay) == 39); static_assert(static_cast<int>(PageActionIconType::kIndigo) == 40); static_assert(static_cast<int>(PageActionIconType::kFederation) == 41); +static_assert(static_cast<int>(PageActionIconType::kGlic) == 42); // Returns a bool indicating whether the given page action type has been // migrated to the new framework, which is based on ActionItems instead of
diff --git a/chrome/browser/ui/page_info/BUILD.gn b/chrome/browser/ui/page_info/BUILD.gn index a33b10bd..dbbd8c3 100644 --- a/chrome/browser/ui/page_info/BUILD.gn +++ b/chrome/browser/ui/page_info/BUILD.gn
@@ -63,10 +63,7 @@ "//chrome/browser/web_applications", ] if (is_chromeos) { - deps += [ - "//chrome/browser/ash/floating_sso", - "//chrome/browser/ui/webui/ash/settings/app_management", - ] + deps += [ "//chrome/browser/ash/floating_sso" ] } } deps += [
diff --git a/chrome/browser/ui/page_info/chrome_page_info_delegate.cc b/chrome/browser/ui/page_info/chrome_page_info_delegate.cc index eb8590d..56e31e0 100644 --- a/chrome/browser/ui/page_info/chrome_page_info_delegate.cc +++ b/chrome/browser/ui/page_info/chrome_page_info_delegate.cc
@@ -83,7 +83,6 @@ #include "chrome/browser/ash/floating_sso/floating_sso_service_factory.h" #include "chrome/browser/smart_card/smart_card_permission_context.h" #include "chrome/browser/smart_card/smart_card_permission_context_factory.h" -#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h" #endif namespace {
diff --git a/chrome/browser/ui/settings_window_manager_chromeos.cc b/chrome/browser/ui/settings_window_manager_chromeos.cc index ae8a573..5b7b3669 100644 --- a/chrome/browser/ui/settings_window_manager_chromeos.cc +++ b/chrome/browser/ui/settings_window_manager_chromeos.cc
@@ -13,6 +13,7 @@ #include "ash/webui/settings/public/constants/routes_util.h" #include "ash/webui/system_apps/public/system_web_app_type.h" #include "ash/wm/window_properties.h" +#include "base/metrics/histogram_functions.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "chrome/browser/app_mode/app_mode_utils.h" @@ -102,6 +103,11 @@ void SettingsWindowManager::Open(const user_manager::User& user, OpenParams params) { + if (params.entry_point.has_value()) { + base::UmaHistogramEnumeration("AppManagement.EntryPoints", + params.entry_point.value()); + } + Profile* profile = Profile::FromBrowserContext( ash::BrowserContextHelper::Get()->GetBrowserContextByUser(&user));
diff --git a/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/BUILD.gn b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/BUILD.gn index 4d3592f..f09d8ec 100644 --- a/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/BUILD.gn +++ b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/BUILD.gn
@@ -64,6 +64,7 @@ source_set("unit_tests") { testonly = true sources = [ + "pin_infobar_controller_crash_unittest.cc", "pin_infobar_controller_unittest.cc", "pin_infobar_delegate_unittest.cc", "pin_infobar_prefs_unittest.cc",
diff --git a/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller.cc b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller.cc index 423467c..345152f 100644 --- a/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller.cc +++ b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller.cc
@@ -1,4 +1,4 @@ -// Copyright 2025 The Chromium Authors +// Copyright 2026 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -142,8 +142,18 @@ // Show the pin-to-taskbar infobar. content::WebContents* web_contents = browser_->GetTabStripModel()->GetActiveWebContents(); + if (!web_contents) { + std::move(done_callback).Run(false); + return; + } + infobar_manager_ = infobars::ContentInfoBarManager::FromWebContents(web_contents); + if (!infobar_manager_) { + std::move(done_callback).Run(false); + return; + } + infobar_manager_->AddObserver(this); infobar_ = PinInfoBarDelegate::Create(infobar_manager_); SetInfoBarShownRecently();
diff --git a/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller_crash_unittest.cc b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller_crash_unittest.cc new file mode 100644 index 0000000..beab4e0 --- /dev/null +++ b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller_crash_unittest.cc
@@ -0,0 +1,84 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> + +#include "base/test/bind.h" +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/ui/browser_window/public/browser_window_interface.h" +#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h" +#include "chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller.h" +#include "chrome/browser/ui/tabs/tab_model.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" +#include "chrome/browser/ui/ui_features.h" +#include "chrome/browser/ui/views/chrome_layout_provider.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/test_renderer_host.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace default_browser { + +class PinInfoBarControllerCrashTest : public testing::Test { + protected: + PinInfoBarControllerCrashTest() + : profile_(std::make_unique<TestingProfile>()), + delegate_(std::make_unique<TestTabStripModelDelegate>()), + tab_strip_model_( + std::make_unique<TabStripModel>(delegate_.get(), profile_.get())), + browser_window_interface_( + std::make_unique<MockBrowserWindowInterface>()) { + feature_list_.InitAndEnableFeature(features::kOfferPinToTaskbarInfoBar); + + ON_CALL(*browser_window_interface_, GetTabStripModel()) + .WillByDefault(::testing::Return(tab_strip_model_.get())); + ON_CALL(*browser_window_interface_, GetProfile()) + .WillByDefault(::testing::Return(profile_.get())); + ON_CALL(*browser_window_interface_, GetType()) + .WillByDefault( + ::testing::Return(BrowserWindowInterface::Type::TYPE_NORMAL)); + delegate_->SetBrowserWindowInterface(browser_window_interface_.get()); + } + + ~PinInfoBarControllerCrashTest() override { + delegate_->SetBrowserWindowInterface(nullptr); + } + + MockBrowserWindowInterface* browser_window_interface() { + return browser_window_interface_.get(); + } + + private: + content::BrowserTaskEnvironment task_environment_; + base::test::ScopedFeatureList feature_list_; + ChromeLayoutProvider layout_provider_; + content::RenderViewHostTestEnabler rvh_test_enabler_; + const std::unique_ptr<TestingProfile> profile_; + const std::unique_ptr<TestTabStripModelDelegate> delegate_; + const std::unique_ptr<TabStripModel> tab_strip_model_; + const std::unique_ptr<MockBrowserWindowInterface> browser_window_interface_; + const tabs::TabModel::PreventFeatureInitializationForTesting prevent_; +}; + +// Reproduce the crash in b/491643039: OnShouldOfferToPinResult called when +// there are no active web contents. +TEST_F(PinInfoBarControllerCrashTest, ReproduceCrashWithNoWebContents) { + PinInfoBarController controller(browser_window_interface()); + + base::RunLoop run_loop; + bool callback_called = false; + controller.OnShouldOfferToPinResult( + base::BindLambdaForTesting([&](bool result) { + callback_called = true; + EXPECT_FALSE(result); + run_loop.Quit(); + }), + /*should_offer_to_pin=*/true); + run_loop.Run(); + + EXPECT_TRUE(callback_called); +} + +} // namespace default_browser
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc index ba1f46d2..31ed121 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
@@ -553,6 +553,7 @@ // Following definitions are equal to content::PrerenderFinalStatus. constexpr int kFinalStatusActivated = 0; +constexpr int kTriggerDestroyed = 16; constexpr int kPrerenderFailedDuringPrefetch = 86; // Following definitions are equal to content::PrefetchStatus. @@ -974,6 +975,90 @@ kPrerenderFailedDuringPrefetch, 1); } +// Prefetch and prerender triggered, prerender cancelled, and then prerender +// triggered. +// +// Scenario: +// +// - mouseenter to a bookmark button. +// - mousedown +// - Prefetch A and prerender A' are triggered. +// - A' matched to A. +// - mouseleave +// - Prerender is cancelled. +// - mouseup outside the button. +// - mouseenter +// - mousedown +// - Prerender B' is triggered. +// - B' matched to A. +// - mouseup +// - Navigate with B' activation. +IN_PROC_BROWSER_TEST_F( + PreloadBookmarkBarPrefetchEnabledPrerenderEnabledNavigationTest, + PreloadsTriggered_PrerenderCancelled_PrerenderTriggered) { + StartServers(); + base::HistogramTester histogram_tester; + + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), https_test_server()->GetURL("/empty.html"))); + + GURL preload_url = https_test_server()->GetURL("/empty.html?preload"); + + CreateBookmarkButton(preload_url); + views::LabelButton* button = GetBookmarkButton(0); + + gfx::Point center(10, 10); + + // Trigger prefetch and prerender. + button->OnMouseEntered(ui::MouseEvent(ui::EventType::kMouseEntered, center, + center, ui::EventTimeForNow(), + /*flags=*/ui::EF_NONE, + /*changed_button_flags=*/ui::EF_NONE)); + button->OnMousePressed(ui::MouseEvent( + ui::EventType::kMousePressed, center, center, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + content::test::PrerenderTestHelper::WaitForPrerenderLoadCompletion( + *GetActiveWebContents(), preload_url); + // Cancel prerender. + button->OnMouseExited(ui::MouseEvent(ui::EventType::kMouseExited, center, + center, ui::EventTimeForNow(), + /*flags=*/ui::EF_NONE, + /*changed_button_flags=*/ui::EF_NONE)); + histogram_tester.ExpectUniqueSample( + "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_BookmarkBar", + kTriggerDestroyed, 1); + // mouseup outside button + + // Trigger prerender again. + button->OnMouseEntered(ui::MouseEvent(ui::EventType::kMouseEntered, center, + center, ui::EventTimeForNow(), + /*flags=*/ui::EF_NONE, + /*changed_button_flags=*/ui::EF_NONE)); + content::test::PrerenderHostObserver prerender_observer( + *GetActiveWebContents(), preload_url); + button->OnMousePressed(ui::MouseEvent( + ui::EventType::kMousePressed, center, center, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + // Navigate. + button->OnMouseReleased(ui::MouseEvent( + ui::EventType::kMouseReleased, center, center, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + prerender_observer.WaitForActivation(); + + EXPECT_EQ(1, prerender_helper().GetRequestCount(preload_url)); + histogram_tester.ExpectUniqueSample("Preloading.Prefetch.PrefetchStatus", + kPrefetchResponseUsed, 1); + histogram_tester.ExpectTotalCount( + "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_BookmarkBar", + 2); + histogram_tester.ExpectBucketCount( + "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_BookmarkBar", + kTriggerDestroyed, 1); + histogram_tester.ExpectBucketCount( + "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_BookmarkBar", + kFinalStatusActivated, 1); +} + namespace { class BookmarkBarTest : public BookmarkBarTestBase {
diff --git a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.cc b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.cc index f58acb6..7262a66 100644 --- a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.cc +++ b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.cc
@@ -34,7 +34,6 @@ #include "chrome/browser/ui/views/tabs/shared/tab_strip_flat_edge_button.h" #include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h" #include "chrome/browser/ui/views/tabs/tab_search_button.h" -#include "chrome/browser/ui/views/tabs/tab_search_container.h" #include "chrome/browser/ui/views/tabs/tab_strip.h" #include "chrome/browser/ui/views/tabs/tab_strip_action_container.h" #include "chrome/browser/ui/views/tabs/tab_strip_control_button.h" @@ -273,8 +272,8 @@ views::LayoutAlignment::kCenter); } - // Add and configure the TabSearchContainer and TabStripComboButton. - std::unique_ptr<TabSearchContainer> tab_search_container; + // Add and configure the TabSearchButton and TabStripComboButton. + std::unique_ptr<TabSearchButton> tab_search_button; std::unique_ptr<TabStripActionContainer> tab_strip_action_container; if (browser && (browser->GetType() == BrowserWindowInterface::Type::TYPE_NORMAL)) { @@ -286,22 +285,22 @@ views::LayoutAlignment::kStart); } else if (!base::FeatureList::IsEnabled( tabs::kHorizontalTabStripComboButton)) { - tab_search_container = std::make_unique<TabSearchContainer>( - render_tab_search_before_tab_strip_, this, tab_strip_); - tab_search_container->SetProperty(views::kCrossAxisAlignmentKey, - views::LayoutAlignment::kCenter); + tab_search_button = + std::make_unique<TabSearchButton>(browser, Edge::kNone, Edge::kNone); + tab_search_button->SetProperty(views::kCrossAxisAlignmentKey, + views::LayoutAlignment::kCenter); } } - if (tab_search_container && render_tab_search_before_tab_strip_) { - tab_search_container->SetPaintToLayer(); - tab_search_container->layer()->SetFillsBoundsOpaquely(false); + if (tab_search_button && render_tab_search_before_tab_strip_) { + tab_search_button->SetPaintToLayer(); + tab_search_button->layer()->SetFillsBoundsOpaquely(false); - tab_search_container_ = AddChildView(std::move(tab_search_container)); + tab_search_button_ = AddChildView(std::move(tab_search_button)); // Inset between the tabsearch and tabstrip should be reduced to account for // extra spacing. - tab_search_container_->SetProperty(views::kViewIgnoredByLayoutKey, true); + tab_search_button_->SetProperty(views::kViewIgnoredByLayoutKey, true); } // Allow the |tab_strip_| to grow into the free space available in @@ -337,9 +336,9 @@ SetProperty(views::kElementIdentifierKey, kTabStripRegionElementId); - if (browser && tab_search_container && !render_tab_search_before_tab_strip_) { - tab_search_container_ = AddChildView(std::move(tab_search_container)); - tab_search_container_->SetProperty( + if (browser && tab_search_button && !render_tab_search_before_tab_strip_) { + tab_search_button_ = AddChildView(std::move(tab_search_button)); + tab_search_button_->SetProperty( views::kMarginsKey, gfx::Insets::TLBR(0, 0, 0, GetLayoutConstant(LayoutConstant::kTabStripPadding))); @@ -365,8 +364,8 @@ if (unfocus_button_) { RemoveChildViewT(std::exchange(unfocus_button_, nullptr)); } - if (tab_search_container_) { - RemoveChildViewT(std::exchange(tab_search_container_, nullptr)); + if (tab_search_button_) { + RemoveChildViewT(std::exchange(tab_search_button_, nullptr)); } } @@ -380,8 +379,8 @@ return false; } - if (render_tab_search_before_tab_strip_ && tab_search_container_ && - IsHitInView(tab_search_container_, point)) { + if (render_tab_search_before_tab_strip_ && tab_search_button_ && + IsHitInView(tab_search_button_, point)) { return false; } @@ -431,8 +430,8 @@ children.emplace_back(unfocus_button_.get()); } - if (tab_search_container_) { - children.emplace_back(tab_search_container_.get()); + if (tab_search_button_) { + children.emplace_back(tab_search_button_.get()); } if (tab_strip_action_container_) { @@ -454,9 +453,9 @@ return; } - const bool tab_search_container_before_tab_strip = - tab_search_container_ && render_tab_search_before_tab_strip_; - if (tab_search_container_before_tab_strip || + const bool tab_search_button_before_tab_strip = + tab_search_button_ && render_tab_search_before_tab_strip_; + if (tab_search_button_before_tab_strip || (unfocus_button_ && unfocus_button_->GetVisible()) || combo_button_) { UpdateTabStripMargin(); } @@ -464,9 +463,9 @@ LayoutSuperclass<views::AccessiblePaneView>(this); int leading_offset = 0; - if (tab_search_container_before_tab_strip) { - AdjustViewBoundsRect(tab_search_container_, leading_offset); - leading_offset += tab_search_container_->GetPreferredSize().width() + + if (tab_search_button_before_tab_strip) { + AdjustViewBoundsRect(tab_search_button_, leading_offset); + leading_offset += tab_search_button_->GetPreferredSize().width() + GetLayoutConstant(LayoutConstant::kTabStripPadding); } @@ -568,11 +567,11 @@ : AccessiblePaneView::GetDefaultFocusableChild(); } -TabStripFlatEdgeButton* HorizontalTabStripRegionView::GetTabSearchButton() { +views::Button* HorizontalTabStripRegionView::GetTabSearchButton() { if (combo_button_) { return combo_button_->end_button(); } - return nullptr; + return tab_search_button_; } views::LabelButton* HorizontalTabStripRegionView::GetGlicButton() { @@ -687,8 +686,8 @@ if (unfocus_button_ && unfocus_button_->GetVisible()) { return true; } - if (tab_search_container_ && render_tab_search_before_tab_strip_ && - tab_search_container_->GetVisible()) { + if (tab_search_button_ && render_tab_search_before_tab_strip_ && + tab_search_button_->GetVisible()) { return true; } return false; @@ -733,14 +732,8 @@ if (unfocus_button_) { UpdateBorderInsetsIfNeeded(unfocus_button_, border_insets); } - if (tab_search_container_) { - UpdateBorderInsetsIfNeeded(tab_search_container_->tab_search_button(), - border_insets); - - if (tab_search_container_->auto_tab_group_button()) { - UpdateBorderInsetsIfNeeded(tab_search_container_->auto_tab_group_button(), - border_insets); - } + if (tab_search_button_) { + UpdateBorderInsetsIfNeeded(tab_search_button_, border_insets); } } @@ -778,11 +771,11 @@ std::optional<int> tab_strip_left_margin; int current_leading_width = 0; - if (tab_search_container_ && render_tab_search_before_tab_strip_) { - // The `tab_search_container_` is being laid out manually. - CHECK(tab_search_container_->GetProperty(views::kViewIgnoredByLayoutKey)); + if (tab_search_button_ && render_tab_search_before_tab_strip_) { + // The `tab_search_button_` is being laid out manually. + CHECK(tab_search_button_->GetProperty(views::kViewIgnoredByLayoutKey)); current_leading_width += - tab_search_container_->GetPreferredSize().width() + + tab_search_button_->GetPreferredSize().width() + GetLayoutConstant(LayoutConstant::kTabStripPadding); }
diff --git a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.h b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.h index d109873..239e34a7 100644 --- a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.h +++ b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.h
@@ -9,7 +9,6 @@ #include "build/buildflag.h" #include "chrome/browser/ui/tabs/tab_data.h" #include "chrome/browser/ui/views/frame/tab_strip_region_view.h" -#include "chrome/browser/ui/views/tabs/tab_search_container.h" #include "chrome/browser/ui/views/tabs/tab_strip.h" #include "chrome/common/buildflags.h" #include "ui/base/metadata/metadata_header_macros.h" @@ -31,7 +30,6 @@ class TabStripScrollContainer; class TabSearchPositionMetricsLogger; class TabStripControlButton; -class TabStripFlatEdgeButton; // Container for the tabstrip and the other views sharing space with it - // with the exception of the caption buttons. @@ -85,7 +83,7 @@ TabStrip* tab_strip() { return tab_strip_; } - TabStripFlatEdgeButton* GetTabSearchButton(); + views::Button* GetTabSearchButton(); TabStripComboButton* GetComboButton() { return combo_button_; } views::LabelButton* GetGlicButton(); @@ -128,12 +126,12 @@ private: // Updates the border padding for `new_tab_button_` and - // `tab_search_container_`, if present. This should be called whenever any + // `tab_search_button_`, if present. This should be called whenever any // input of the computation of the border's sizing changes. void UpdateButtonBorders(); // Updates the left and right margins for the tab strip. This should be - // called whenever `tab_search_container_` changes size, if + // called whenever `tab_search_button_` changes size, if // `render_tab_search_before_tab_strip_` is true. void UpdateTabStripMargin(); @@ -151,7 +149,7 @@ raw_ptr<TabStripScrollContainer> tab_strip_scroll_container_ = nullptr; raw_ptr<TabStripComboButton> combo_button_ = nullptr; raw_ptr<views::Button> new_tab_button_ = nullptr; - raw_ptr<TabSearchContainer> tab_search_container_ = nullptr; + raw_ptr<TabSearchButton> tab_search_button_ = nullptr; raw_ptr<TabStripControlButton> unfocus_button_ = nullptr; // On some platforms for Chrome Refresh, the TabSearchButton should be
diff --git a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view_interactive_uitest.cc index afe31d90..590e787 100644 --- a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view_interactive_uitest.cc
@@ -50,11 +50,6 @@ TabStrip* tab_strip() { return tab_strip_region_view()->tab_strip(); } - TabSearchContainer* tab_search_container() { - return BrowserElementsViews::From(browser())->GetViewAs<TabSearchContainer>( - kTabSearchContainerElementId); - } - TabSearchButton* tab_search_button() { return BrowserElementsViews::From(browser())->GetViewAs<TabSearchButton>( kTabSearchButtonElementId); @@ -249,25 +244,25 @@ } IN_PROC_BROWSER_TEST_F(HorizontalTabStripRegionViewBrowserTest, - DefaultTestSearchContainerIsEndAligned) { + DefaultTabSearchButtonIsEndAligned) { if (tabs::GetTabSearchPosition(browser()) == tabs::TabSearchPosition::kLeadingHorizontalTabstrip) { - // The TabSearchContainer is calculated as controls padding away from the + // The TabSearchButton is calculated as controls padding away from the // first tab (not including bottom corner radius) - const int tab_search_container_expected_end = + const int tab_search_button_expected_end = tab_strip_region_view()->tab_strip()->x() + TabStyle::Get()->GetBottomCornerRadius() - GetLayoutConstant(LayoutConstant::kTabStripPadding); - EXPECT_EQ(tab_search_container()->bounds().right(), - tab_search_container_expected_end); + EXPECT_EQ(tab_search_button()->bounds().right(), + tab_search_button_expected_end); } else if (tabs::GetTabSearchPosition(browser()) != tabs::TabSearchPosition::kToolbarButton) { - const int tab_search_container_expected_end = + const int tab_search_button_expected_end = tab_strip_region_view()->GetLocalBounds().right() - GetLayoutConstant(LayoutConstant::kTabStripPadding); - EXPECT_EQ(tab_search_container()->bounds().right(), - tab_search_container_expected_end); + EXPECT_EQ(tab_search_button()->bounds().right(), + tab_search_button_expected_end); } }
diff --git a/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc b/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc index 3bcc8646..9dd2915 100644 --- a/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc +++ b/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc
@@ -268,8 +268,8 @@ layout.force_top_container_to_top = remainder < min_toolbar_height_side_panel_width; - // If still allowing toolbar height, clamp the sidepanel based on what the - // toolbar actually supports. + // If still allowing toolbar height, clamp the side panel based on what + // the toolbar actually supports. if (!layout.force_top_container_to_top) { preferred_toolbar_height_side_panel_width = std::min(preferred_toolbar_height_side_panel_width, remainder); @@ -402,7 +402,8 @@ gfx::Size BrowserViewTabbedLayoutImpl::GetMinimumMainAreaSize( const BrowserLayoutParams& params) const { gfx::Size toolbar_size = views().toolbar->GetMinimumSize(); - if (GetTabStripType() == TabStripType::kVertical) { + const auto tab_strip_type = GetTabStripType(); + if (tab_strip_type == TabStripType::kVertical) { toolbar_size.Enlarge(GetExclusionWidth(params), 0); } const gfx::Size bookmark_bar_size = @@ -418,14 +419,23 @@ ? views().contents_height_side_panel->GetMinimumSize() : gfx::Size(); - const int width = std::max({toolbar_size.width(), bookmark_bar_size.width(), - infobar_container_size.width(), - contents_height_side_panel_size.width() + - kContentsContainerMinimumWidth}); + int width = std::max({toolbar_size.width(), bookmark_bar_size.width(), + infobar_container_size.width(), + contents_height_side_panel_size.width() + + kContentsContainerMinimumWidth}); const int height = toolbar_size.height() + bookmark_bar_size.height() + infobar_container_size.height() + std::max(contents_size.height(), contents_height_side_panel_size.height()); + + if (tab_strip_type == TabStripType::kHorizontal && + IsParentedToAndVisible(views().horizontal_tab_strip_region_view, + views().browser_view)) { + // When toolbar-height side panel is present, ensure that the shadow box + // padding is included in the size calculation. + width += GetLayoutConstant(LayoutConstant::kToolbarHeightSidePanelInset); + } + return gfx::Size(width, height); }
diff --git a/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl_interactive_uitest.cc b/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl_interactive_uitest.cc index 77a2c4c..bdff164 100644 --- a/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl_interactive_uitest.cc +++ b/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl_interactive_uitest.cc
@@ -60,7 +60,10 @@ static constexpr char kBaselineCl[] = "7635098"; explicit BrowserViewTabbedLayoutImplUiTest(bool disable_animations = true) - : disable_animations_(disable_animations) {} + : render_mode_resetter_(gfx::AnimationTestApi::SetRichAnimationRenderMode( + disable_animations + ? gfx::Animation::RichAnimationRenderMode::FORCE_DISABLED + : gfx::Animation::RichAnimationRenderMode::FORCE_ENABLED)) {} ~BrowserViewTabbedLayoutImplUiTest() override = default; @@ -70,7 +73,6 @@ feature_list_.InitWithFeatures( {tabs::kVerticalTabs, features::kSidePanelFlyoverAnimation}, {}); set_open_about_blank_on_browser_launch(true); - gfx::Animation::SetPrefersReducedMotionForTesting(disable_animations_); InteractiveBrowserTest::SetUp(); } @@ -84,7 +86,9 @@ chrome::AddTabAt(browser(), GURL("about:blank"), -1, true); } - auto ReplaceAndShowSidePanel(SidePanelEntry::PanelType type) { + auto ReplaceAndShowSidePanel( + SidePanelEntry::PanelType type, + std::optional<int> force_preferred_width = std::nullopt) { return Steps( Do([this, type]() { // Note: there is a registry at browser level and one per tab; we want @@ -111,8 +115,71 @@ SidePanelEntry::Id::kCustomizeChrome); }), WaitForShow(kSidePanelElementId), + If( + [force_preferred_width] { + return force_preferred_width.has_value(); + }, + Then(WithView(kSidePanelElementId, + [force_preferred_width](SidePanel* side_panel) { + side_panel->SetPanelWidth(*force_preferred_width); + side_panel->InvalidateLayout(); + }), + Do([this] { RunScheduledLayouts(); }))), // Wait for the indicator to show. - WaitForShow(kSidePanelTestElement)); + WaitForShow(kSidePanelTestElement), + // Ensure that the browser gets a full layout. + Do([this] { RunScheduledLayouts(); })); + } + + using Bounds = base::RefCountedData<gfx::Rect>; + + auto VerifyLayout() { + auto browser_bounds = base::MakeRefCounted<Bounds>(); + auto side_panel_bounds = base::MakeRefCounted<Bounds>(); + auto content_bounds = base::MakeRefCounted<Bounds>(); + auto toolbar_bounds = base::MakeRefCounted<Bounds>(); + return Steps( + WithElement(kBrowserViewElementId, + [browser_bounds](ui::TrackedElement* el) { + browser_bounds->data = el->GetScreenBounds(); + }), + WithElement(kSidePanelElementId, + [side_panel_bounds](ui::TrackedElement* el) { + side_panel_bounds->data = el->GetScreenBounds(); + }), + WithElement(kMultiContentsViewElementId, + [content_bounds](ui::TrackedElement* el) { + content_bounds->data = el->GetScreenBounds(); + }), + WithElement(ToolbarView::kToolbarElementId, + [toolbar_bounds](ui::TrackedElement* el) { + toolbar_bounds->data = el->GetScreenBounds(); + }), + Check( + [=] { + return browser_bounds->data.Contains(side_panel_bounds->data); + }, + "Side Panel in browser"), + Check( + [=] { + return !side_panel_bounds->data.Intersects(toolbar_bounds->data); + }, + "Side Panel does not overlap Toolbar"), + Check( + [=] { return browser_bounds->data.Contains(toolbar_bounds->data); }, + "Toolbar in browser"), + Check( + [=] { return browser_bounds->data.Contains(content_bounds->data); }, + "Content in browser"), + CheckView( + ToolbarView::kToolbarElementId, + [=](ToolbarView* toolbar) { + LOG(INFO) << "Toolbar minimum size is " + << toolbar->GetMinimumSize().ToString(); + return toolbar_bounds->data.width() >= + toolbar->GetMinimumSize().width(); + }, + "Toolbar is at least minimum size")); } auto CloseSidePanel() { @@ -125,8 +192,6 @@ return GetLayoutConstant(LayoutConstant::kToolbarCornerRadius); } - using Bounds = base::RefCountedData<gfx::Rect>; - template <typename F> auto ScreenshotSubregion(ui::ElementSpecifier spec, const std::string& name, @@ -253,9 +318,22 @@ }); } + auto ResizeToMinimumWidth() { + auto steps = Steps( + WithView(kBrowserViewElementId, + [=](BrowserView* browser_view) { + auto* const widget = browser_view->GetWidget(); + widget->SetSize(gfx::Size(widget->GetMinimumSize().width(), + widget->GetSize().height())); + }), + Do([this]() { RunScheduledLayouts(); })); + AddDescriptionPrefix(steps, "ResizeToMinimumWidth"); + return steps; + } + private: - const bool disable_animations_; base::test::ScopedFeatureList feature_list_; + const gfx::AnimationTestApi::RenderModeResetter render_mode_resetter_; }; DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(BrowserViewTabbedLayoutImplUiTest, @@ -430,6 +508,84 @@ ScreenshotBottom(kSidePanelElementId, "side_panel_bottom", 8)); } +// Regression tests to ensure that when the side panel is open and the browser +// is at its minimum size, it doesn't go into content-height mode. +// Content-height mode is only for cases where the browser is already too small +// when the side panel is opened. +// +// See https://crbug.com/491484034 for rationale. + +IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest, + OpenSidePanelAtSmallWidthCollapsesLayout) { + gfx::Rect toolbar_bounds_in_screen; + RunTestSequence(ResizeToMinimumWidth(), SelectTab(kBrowserViewElementId, 0), + ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar), + WithView(ToolbarView::kToolbarElementId, + [&](views::View* toolbar) { + toolbar_bounds_in_screen = + toolbar->GetBoundsInScreen(); + }), + CheckView(kSidePanelElementId, [&](views::View* side_panel) { + return side_panel->GetBoundsInScreen().y() > + toolbar_bounds_in_screen.bottom(); + })); +} + +IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest, + TestMinimumWindowSizeWhenSidePanelIsOpen) { + gfx::Rect toolbar_bounds_in_screen; + RunTestSequence(SelectTab(kBrowserViewElementId, 0), + ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar), + ResizeToMinimumWidth(), + WithView(ToolbarView::kToolbarElementId, + [&](views::View* toolbar) { + toolbar_bounds_in_screen = + toolbar->GetBoundsInScreen(); + }), + CheckView(kSidePanelElementId, [&](views::View* side_panel) { + return side_panel->GetBoundsInScreen().y() < + toolbar_bounds_in_screen.bottom(); + })); +} + +// Regression tests for cases where preferred width for side panel is +// unreasonably large or small. + +IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest, NormalSidePanelSize) { + RunScheduledLayouts(); + RunTestSequence(SelectTab(kBrowserViewElementId, 0), + ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar), + VerifyLayout()); +} + +IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest, SmallSidePanelSize) { + RunScheduledLayouts(); + RunTestSequence( + SelectTab(kBrowserViewElementId, 0), + ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar, 100), + VerifyLayout()); +} + +IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest, + LargeSidePanelPreferredSize) { + RunScheduledLayouts(); + const int large_width = browser()->GetBrowserView().width() - 20; + RunTestSequence( + SelectTab(kBrowserViewElementId, 0), + ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar, large_width), + VerifyLayout()); +} + +IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest, + VeryLargeSidePanelPreferredSize) { + RunScheduledLayouts(); + const int large_width = browser()->GetBrowserView().width() + 100; + RunTestSequence( + SelectTab(kBrowserViewElementId, 0), + ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar, large_width), + VerifyLayout()); +} + // Regression test for limiting the amount of WebContents resizing when the // "flyover" animation flag is enabled. class BrowserViewTabbedLayoutImplContentLayoutUiTest
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.cc b/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.cc index f55e33a..13ade0e 100644 --- a/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.cc +++ b/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.cc
@@ -6,6 +6,7 @@ #include <optional> +#include "base/functional/bind.h" #include "base/i18n/rtl.h" #include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics_action.h" @@ -13,12 +14,12 @@ #include "chrome/app/vector_icons/vector_icons.h" #include "chrome/browser/favicon/favicon_utils.h" #include "chrome/browser/search/search.h" +#include "chrome/browser/ui/tab_ui_helper.h" #include "chrome/browser/ui/tabs/alert/tab_alert.h" #include "chrome/browser/ui/tabs/alert/tab_alert_controller.h" #include "chrome/browser/ui/tabs/alert/tab_alert_icon.h" #include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/tabs/split_tab_menu_model.h" -#include "chrome/browser/ui/tabs/tab_data.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/contents_container_outline.h" @@ -134,12 +135,6 @@ .WithOrder(1)); views::InstallCircleHighlightPathGenerator(image_button_); - // Update minitoolbar contents. - std::optional<tabs::TabData> tab_data = GetTabData(); - if (tab_data.has_value()) { - UpdateContents(tab_data.value()); - } - web_contents_attached_subscription_ = web_view->AddWebContentsAttachedCallback( base::BindRepeating(&MultiContentsViewMiniToolbar::UpdateWebContents, @@ -149,14 +144,10 @@ base::BindRepeating(&MultiContentsViewMiniToolbar::ClearWebContents, base::Unretained(this))); - RegisterTabAlertSubscription(); - - browser_view_->browser()->tab_strip_model()->AddObserver(this); + RegisterTabSubscriptions(); } -MultiContentsViewMiniToolbar::~MultiContentsViewMiniToolbar() { - browser_view_->browser()->tab_strip_model()->RemoveObserver(this); -} +MultiContentsViewMiniToolbar::~MultiContentsViewMiniToolbar() = default; void MultiContentsViewMiniToolbar::UpdateState(bool is_active, bool is_highlighted) { @@ -188,20 +179,53 @@ } void MultiContentsViewMiniToolbar::UpdateContents() { - std::optional<tabs::TabData> tab_data = GetTabData(); - if (tab_data.has_value()) { - UpdateContents(tab_data.value()); + auto* const interface = GetTabInterface(web_contents_); + if (!interface) { + return; } + + TabUIHelper* const tab_ui_helper = TabUIHelper::From(interface); + + GURL domain_url = tab_ui_helper->GetVisibleURL(); + if (tab_ui_helper->GetLastCommittedURL().is_valid()) { + domain_url = tab_ui_helper->GetLastCommittedURL(); + } + + // Create the formatted domain, this will match the hover card domain. + std::u16string domain; + if (IsNTP(domain_url)) { + domain = u""; + } else if (domain_url.SchemeIsFile()) { + domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_FILE_URL_SOURCE); + } else if (domain_url.SchemeIsBlob()) { + domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_BLOB_URL_SOURCE); + } else if (domain_url.SchemeIs(url::kViewSourceScheme)) { + domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_VIEW_SOURCE_URL_SOURCE); + } else if (tab_ui_helper->ShouldDisplayURL()) { + domain = url_formatter::FormatUrl( + domain_url, + url_formatter::kFormatUrlOmitDefaults | + url_formatter::kFormatUrlOmitTrivialSubdomains | + url_formatter::kFormatUrlOmitHTTPS | + url_formatter::kFormatUrlTrimAfterHost, + base::UnescapeRule::SPACES, nullptr, nullptr, nullptr); + } + domain_label_->SetText(domain); + domain_label_->SetElideBehavior(domain_url.IsStandard() && + !domain_url.SchemeIsFile() && + !domain_url.SchemeIsFileSystem() + ? gfx::ELIDE_HEAD + : gfx::ELIDE_TAIL); + domain_label_->SetCustomTooltipText(base::ASCIIToUTF16(domain_url.spec())); + + UpdateFavicon(tab_ui_helper); } void MultiContentsViewMiniToolbar::UpdateWebContents(views::WebView* web_view) { tab_alert_status_subscription_.reset(); + tab_ui_updated_subscription_.reset(); web_contents_ = web_view->web_contents(); - RegisterTabAlertSubscription(); - std::optional<tabs::TabData> tab_data = GetTabData(); - if (tab_data.has_value()) { - UpdateContents(tab_data.value()); - } + RegisterTabSubscriptions(); } void MultiContentsViewMiniToolbar::ClearWebContents(views::WebView*) { @@ -210,15 +234,6 @@ web_contents_ = nullptr; } -void MultiContentsViewMiniToolbar::OnTabChangedAt(tabs::TabInterface* tab, - int index, - TabChangeType change_type) { - if (!web_contents_ || tab->GetContents() != web_contents_) { - return; - } - UpdateContents(tabs::TabData::FromTabInterface(tab)); -} - void MultiContentsViewMiniToolbar::OnPaint(gfx::Canvas* canvas) { // Paint the mini toolbar background to match the toolbar. ThemedBackground::PaintBackground(canvas, this, browser_view_); @@ -226,19 +241,20 @@ void MultiContentsViewMiniToolbar::OnThemeChanged() { views::View::OnThemeChanged(); - std::optional<tabs::TabData> tab_data = GetTabData(); - if (tab_data.has_value()) { - UpdateFavicon(tab_data.value()); - } if (auto* interface = GetTabInterface(web_contents_)) { auto* tab_alert_controller = tabs::TabAlertController::From(interface); if (tab_alert_controller) { OnAlertStatusIndicatorChanged(tab_alert_controller->GetAlertToShow()); } + + TabUIHelper* const tab_ui_helper = TabUIHelper::From(interface); + if (tab_ui_helper) { + UpdateFavicon(tab_ui_helper); + } } } -void MultiContentsViewMiniToolbar::RegisterTabAlertSubscription() { +void MultiContentsViewMiniToolbar::RegisterTabSubscriptions() { if (auto* interface = GetTabInterface(web_contents_)) { auto* tab_alert_controller = tabs::TabAlertController::From(interface); OnAlertStatusIndicatorChanged(tab_alert_controller->GetAlertToShow()); @@ -246,6 +262,11 @@ tab_alert_controller->AddAlertToShowChangedCallback(base::BindRepeating( &MultiContentsViewMiniToolbar::OnAlertStatusIndicatorChanged, base::Unretained(this))); + tab_ui_updated_subscription_ = + TabUIHelper::From(interface)->AddTabUIChangeCallback( + base::BindRepeating(&MultiContentsViewMiniToolbar::UpdateContents, + base::Unretained(this))); + UpdateContents(); } } @@ -265,62 +286,10 @@ } } -std::optional<tabs::TabData> MultiContentsViewMiniToolbar::GetTabData() { - if (!web_contents_) { - return std::nullopt; - } - - TabStripModel* const model = browser_view_->browser()->tab_strip_model(); - const int tab_index = model->GetIndexOfWebContents(web_contents_); - if (tab_index == TabStripModel::kNoTab) { - return std::nullopt; - } - - tabs::TabInterface* const tab_interface = GetTabInterface(web_contents_); - return tab_interface ? std::make_optional( - tabs::TabData::FromTabInterface(tab_interface)) - : std::nullopt; -} - -void MultiContentsViewMiniToolbar::UpdateContents(tabs::TabData tab_data) { - GURL domain_url = tab_data.visible_url; - if (tab_data.last_committed_url.is_valid()) { - domain_url = tab_data.last_committed_url; - } - // Create the formatted domain, this will match the hover card domain. - std::u16string domain; - if (IsNTP(domain_url)) { - domain = u""; - } else if (domain_url.SchemeIsFile()) { - domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_FILE_URL_SOURCE); - } else if (domain_url.SchemeIsBlob()) { - domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_BLOB_URL_SOURCE); - } else if (domain_url.SchemeIs(url::kViewSourceScheme)) { - domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_VIEW_SOURCE_URL_SOURCE); - } else if (tab_data.should_display_url) { - domain = url_formatter::FormatUrl( - domain_url, - url_formatter::kFormatUrlOmitDefaults | - url_formatter::kFormatUrlOmitTrivialSubdomains | - url_formatter::kFormatUrlOmitHTTPS | - url_formatter::kFormatUrlTrimAfterHost, - base::UnescapeRule::SPACES, nullptr, nullptr, nullptr); - } - domain_label_->SetText(domain); - domain_label_->SetElideBehavior(domain_url.IsStandard() && - !domain_url.SchemeIsFile() && - !domain_url.SchemeIsFileSystem() - ? gfx::ELIDE_HEAD - : gfx::ELIDE_TAIL); - domain_label_->SetCustomTooltipText(base::ASCIIToUTF16(domain_url.spec())); - - UpdateFavicon(tab_data); -} - -void MultiContentsViewMiniToolbar::UpdateFavicon(tabs::TabData tab_data) { +void MultiContentsViewMiniToolbar::UpdateFavicon(TabUIHelper* tab_ui_helper) { // Theme the favicon similar to how favicons are themed in the bookmarks bar. - ui::ImageModel favicon = tab_data.favicon; - bool themify_favicon = tab_data.should_themify_favicon; + ui::ImageModel favicon = tab_ui_helper->GetFavicon(); + bool themify_favicon = tab_ui_helper->ShouldThemifyFavicon(); if (favicon.IsEmpty()) { favicon = favicon::GetDefaultFaviconModel(kColorBookmarkBarBackground); themify_favicon = true;
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h b/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h index 8402967..1eb06b0 100644 --- a/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h +++ b/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h
@@ -9,13 +9,14 @@ #include "base/memory/raw_ptr.h" #include "chrome/browser/ui/color/chrome_color_id.h" -#include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "ui/base/metadata/metadata_header_macros.h" #include "ui/views/view.h" class ContentsWebView; -namespace tabs { -struct TabData; +class TabUIHelper; + +namespace content { +class WebContents; } namespace tabs { @@ -36,8 +37,7 @@ // MultiContentsViewMiniToolbar is shown for the inactive side of a split and // displays the favicon, domain, tab alert state, and a menu button. -class MultiContentsViewMiniToolbar : public views::View, - public TabStripModelObserver { +class MultiContentsViewMiniToolbar : public views::View { METADATA_HEADER(MultiContentsViewMiniToolbar, views::View) public: @@ -54,11 +54,6 @@ views::ImageButton* image_button_for_testing() { return image_button_; } private: - // TabStripModelObserver: - void OnTabChangedAt(tabs::TabInterface* tab, - int index, - TabChangeType change_type) override; - // View: void OnPaint(gfx::Canvas* canvas) override; void OnThemeChanged() override; @@ -66,13 +61,10 @@ void UpdateWebContents(views::WebView* web_view); void ClearWebContents(views::WebView*); - void RegisterTabAlertSubscription(); + void RegisterTabSubscriptions(); void OnAlertStatusIndicatorChanged(std::optional<tabs::TabAlert> new_alert); - std::optional<tabs::TabData> GetTabData(); - // Updates the favicon and domain based on the provided |tab_data|. - void UpdateContents(tabs::TabData tab_data); - void UpdateFavicon(tabs::TabData tab_data); + void UpdateFavicon(TabUIHelper* tab_ui_helper); void OpenSplitViewMenu(); void CloseCurrentView(); @@ -91,6 +83,7 @@ base::CallbackListSubscription web_contents_attached_subscription_; base::CallbackListSubscription web_contents_detached_subscription_; std::optional<base::CallbackListSubscription> tab_alert_status_subscription_; + std::optional<base::CallbackListSubscription> tab_ui_updated_subscription_; }; #endif // CHROME_BROWSER_UI_VIEWS_FRAME_MULTI_CONTENTS_VIEW_MINI_TOOLBAR_H_
diff --git a/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc b/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc index 503b8d5f..af75394f 100644 --- a/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc +++ b/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc
@@ -919,22 +919,19 @@ // If the rect doesn't fit on the monitor, push the arrow to the other side. display::Screen* screen = display::Screen::Get(); - display::Display display = - screen->GetDisplayNearestView(GetWidget()->GetNativeView()); + gfx::Rect display_bounds = + screen->GetDisplayNearestView(GetWidget()->GetNativeView()).bounds(); - if (!display.bounds().Contains(drop_bounds)) { - // Only left/right arrows should be outside the display bounds. - CHECK_NE(*direction, DropArrow::Direction::kDown); + DropArrow::MaybeAdjustDisplayBounds(display_bounds); - const gfx::Size drop_arrow_size = DropArrow::GetSize(); - if (base::i18n::IsRTL()) { + if (!display_bounds.Contains(drop_bounds)) { + if (base::i18n::IsRTL() && *direction == DropArrow::Direction::kLeft) { *direction = DropArrow::Direction::kRight; - drop_bounds.Offset( - -GetBoundsInScreen().width() - drop_arrow_size.height(), 0); - } else { + drop_bounds.Offset(-GetBoundsInScreen().width() - DropArrow::kSize, 0); + } else if (base::i18n::IsRTL() && + *direction == DropArrow::Direction::kRight) { *direction = DropArrow::Direction::kLeft; - drop_bounds.Offset(GetBoundsInScreen().width() + drop_arrow_size.height(), - 0); + drop_bounds.Offset(GetBoundsInScreen().width() + DropArrow::kSize, 0); } } @@ -1016,18 +1013,15 @@ gfx::Rect VerticalTabStripRegionView::GetLinkDropBoundsFromPosition( gfx::Point position, DropArrow::Direction direction) { - gfx::Size drop_arrow_size = DropArrow::GetSize(); if (direction == DropArrow::Direction::kRight) { - drop_arrow_size.Transpose(); - position.Offset(-drop_arrow_size.width(), -drop_arrow_size.height() / 2); + position.Offset(-DropArrow::kSize, -DropArrow::kSize / 2); } else if (direction == DropArrow::Direction::kLeft) { - drop_arrow_size.Transpose(); - position.Offset(drop_arrow_size.width(), -drop_arrow_size.height() / 2); + position.Offset(DropArrow::kSize, -DropArrow::kSize / 2); } else { - position.Offset(-drop_arrow_size.width() / 2, -drop_arrow_size.height()); + position.Offset(-DropArrow::kSize / 2, -DropArrow::kSize); } - return gfx::Rect(position, drop_arrow_size); + return gfx::Rect(position, gfx::Size(DropArrow::kSize, DropArrow::kSize)); } BEGIN_METADATA(VerticalTabStripRegionView)
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc b/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc index cc6374d..bf93b7b 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc +++ b/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc
@@ -622,7 +622,8 @@ {{omnibox::kWebUIOmniboxAimPopupAddContextButtonVariantParam.name, "inline"}}}, {omnibox::kWebUIOmniboxPopup, {}}}, - /*disabled_features=*/{omnibox::kAimServerEligibilityEnabled}); + /*disabled_features=*/{omnibox::kAimServerEligibilityEnabled, + omnibox::kAimFuseboxEligibilityCheckEnabled}); } ~LocationBarViewAddContextButtonBrowserTest() override = default;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.cc index e01aa99f..61943ec 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.cc
@@ -111,11 +111,11 @@ } } - if (auto* handler = presenter() - ->GetWebUIContent() - ->contents_wrapper() - ->GetWebUIController() - ->omnibox_handler()) { + auto* controller = presenter() + ->GetWebUIContent() + ->contents_wrapper() + ->GetWebUIController(); + if (auto* handler = controller ? controller->omnibox_handler() : nullptr) { handler->SetAimButtonVisible(omnibox_view_->AimButtonVisible()); } } @@ -137,11 +137,9 @@ void OmniboxPopupViewWebUI::StepSelection( OmniboxPopupSelection::Direction direction, OmniboxPopupSelection::Step step) { - if (auto* handler = presenter() - ->GetWebUIContent() - ->contents_wrapper() - ->GetWebUIController() - ->omnibox_handler()) { + auto* controller = + presenter()->GetWebUIContent()->contents_wrapper()->GetWebUIController(); + if (auto* handler = controller ? controller->omnibox_handler() : nullptr) { handler->SetAimButtonVisible(omnibox_view_->AimButtonVisible()); handler->StepSelection(direction, step); } @@ -149,11 +147,9 @@ void OmniboxPopupViewWebUI::OpenCurrentSelection( WindowOpenDisposition disposition) { - if (auto* handler = presenter() - ->GetWebUIContent() - ->contents_wrapper() - ->GetWebUIController() - ->omnibox_handler()) { + auto* controller = + presenter()->GetWebUIContent()->contents_wrapper()->GetWebUIController(); + if (auto* handler = controller ? controller->omnibox_handler() : nullptr) { handler->OpenCurrentSelection(disposition); } }
diff --git a/chrome/browser/ui/views/page_action/action_ids.h b/chrome/browser/ui/views/page_action/action_ids.h index b282a07..a5f6c92 100644 --- a/chrome/browser/ui/views/page_action/action_ids.h +++ b/chrome/browser/ui/views/page_action/action_ids.h
@@ -41,6 +41,7 @@ kActionFilledCardInformation, kActionShowPaymentsBubbleOrPage, kActionSidePanelShowContextualTasks, + kActionGlicContextualCueing, kActionBookmarkThisTab, kActionFederation, });
diff --git a/chrome/browser/ui/views/page_action/page_action_properties_provider.cc b/chrome/browser/ui/views/page_action/page_action_properties_provider.cc index bf5ab45d..84f5e20 100644 --- a/chrome/browser/ui/views/page_action/page_action_properties_provider.cc +++ b/chrome/browser/ui/views/page_action/page_action_properties_provider.cc
@@ -246,6 +246,13 @@ .element_identifier = kFederationElementId, }, }, + { + kActionGlicContextualCueing, + { + .histogram_name = "Glic", + .type = PageActionIconType::kGlic, + }, + }, }); constexpr bool CheckIgnoreFlagUsage() {
diff --git a/chrome/browser/ui/views/tabs/projects/BUILD.gn b/chrome/browser/ui/views/tabs/projects/BUILD.gn index b5821fb..e901fdfcd 100644 --- a/chrome/browser/ui/views/tabs/projects/BUILD.gn +++ b/chrome/browser/ui/views/tabs/projects/BUILD.gn
@@ -8,6 +8,7 @@ "projects_panel_controller.h", "projects_panel_controls_view.h", "projects_panel_no_tab_groups_view.h", + "projects_panel_recent_threads_expand_button.h", "projects_panel_recent_threads_view.h", "projects_panel_tab_groups_drag_scroll_handler.h", "projects_panel_tab_groups_item_view.h", @@ -34,6 +35,7 @@ "projects_panel_controller.cc", "projects_panel_controls_view.cc", "projects_panel_no_tab_groups_view.cc", + "projects_panel_recent_threads_expand_button.cc", "projects_panel_recent_threads_view.cc", "projects_panel_tab_groups_drag_scroll_handler.cc", "projects_panel_tab_groups_item_view.cc", @@ -75,6 +77,7 @@ testonly = true sources = [ "projects_panel_controller_unittest.cc", + "projects_panel_recent_threads_expand_button_unittest.cc", "projects_panel_recent_threads_view_unittest.cc", "projects_panel_tab_groups_drag_scroll_handler_unittest.cc", "projects_panel_tab_groups_item_view_unittest.cc",
diff --git a/chrome/browser/ui/views/tabs/projects/layout_constants.h b/chrome/browser/ui/views/tabs/projects/layout_constants.h index 99cc5b7..67576d0 100644 --- a/chrome/browser/ui/views/tabs/projects/layout_constants.h +++ b/chrome/browser/ui/views/tabs/projects/layout_constants.h
@@ -55,6 +55,9 @@ // any affected histograms. inline constexpr size_t kMaxNumberOfRecentThreads = 300; +// Number of threads visible when the threads section when collapsed. +inline constexpr int kNumThreadsVisibleWhenCollapsed = 3; + // Minimum width of the projects panel. inline constexpr int kProjectsPanelMinWidth = 240;
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.cc b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.cc new file mode 100644 index 0000000..802a023a --- /dev/null +++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.cc
@@ -0,0 +1,77 @@ +// Copyright 2026 The Chromium Authors +// 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/tabs/projects/projects_panel_recent_threads_expand_button.h" + +#include "chrome/app/vector_icons/vector_icons.h" +#include "chrome/browser/ui/color/chrome_color_id.h" +#include "chrome/browser/ui/views/tabs/projects/layout_constants.h" +#include "chrome/browser/ui/views/tabs/projects/projects_panel_utils.h" +#include "chrome/grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/base/models/image_model.h" +#include "ui/views/accessibility/view_accessibility.h" +#include "ui/views/animation/ink_drop.h" +#include "ui/views/controls/image_view.h" +#include "ui/views/controls/label.h" +#include "ui/views/layout/flex_layout.h" + +namespace { +constexpr int kExpandIconSize = 20; +constexpr gfx::Insets kExpandIconMargins = gfx::Insets::TLBR(0, 2, 0, 2); +} // namespace + +ProjectsPanelRecentThreadsExpandButton::ProjectsPanelRecentThreadsExpandButton( + base::RepeatingClosure callback) + : views::Button(std::move(callback)) { + SetLayoutManager(std::make_unique<views::FlexLayout>()) + ->SetInteriorMargin(projects_panel::kListItemMargins) + .SetOrientation(views::LayoutOrientation::kHorizontal) + .SetCrossAxisAlignment(views::LayoutAlignment::kCenter); + + icon_ = AddChildView(std::make_unique<views::ImageView>()); + icon_->SetProperty(views::kMarginsKey, kExpandIconMargins); + + title_ = AddChildView(std::make_unique<views::Label>()); + title_->SetTextStyle(views::style::STYLE_BODY_3_MEDIUM); + title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); + title_->SetBackgroundColor(SK_ColorTRANSPARENT); + title_->SetProperty(views::kMarginsKey, + projects_panel::kListItemTitleMargins); + title_->SetProperty( + views::kFlexBehaviorKey, + views::FlexSpecification(views::LayoutOrientation::kHorizontal, + views::MinimumFlexSizeRule::kScaleToMinimum, + views::MaximumFlexSizeRule::kUnbounded)); + + projects_panel::ConfigureInkDropForButton(this); + + SetHideInkDropWhenShowingContextMenu(true); +} + +ProjectsPanelRecentThreadsExpandButton:: + ~ProjectsPanelRecentThreadsExpandButton() = default; + +void ProjectsPanelRecentThreadsExpandButton::SetExpanded(bool expanded) { + const int string_id = + expanded ? IDS_THREADS_SHOW_LESS : IDS_THREADS_SHOW_MORE; + const gfx::VectorIcon& icon = expanded ? kKeyboardArrowUpChromeRefreshIcon + : kKeyboardArrowDownChromeRefreshIcon; + + icon_->SetImage(ui::ImageModel::FromVectorIcon( + icon, kColorProjectsPanelButtonIcon, kExpandIconSize)); + title_->SetText(l10n_util::GetStringUTF16(string_id)); + GetViewAccessibility().SetName(l10n_util::GetStringUTF16(string_id)); + + // Reset the button and ink drop state to ensure it doesn't get stuck in the + // 'activated' or 'hovered' state if the button moves during the animation. + SetState(STATE_NORMAL); + auto* ink_drop = views::InkDrop::Get(this)->GetInkDrop(); + ink_drop->AnimateToState(views::InkDropState::HIDDEN); + ink_drop->SetHovered(false); +} + +BEGIN_METADATA(ProjectsPanelRecentThreadsExpandButton) +END_METADATA
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.h b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.h new file mode 100644 index 0000000..00bc890 --- /dev/null +++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.h
@@ -0,0 +1,41 @@ +// Copyright 2026 The Chromium Authors +// 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_TABS_PROJECTS_PROJECTS_PANEL_RECENT_THREADS_EXPAND_BUTTON_H_ +#define CHROME_BROWSER_UI_VIEWS_TABS_PROJECTS_PROJECTS_PANEL_RECENT_THREADS_EXPAND_BUTTON_H_ + +#include "base/memory/raw_ptr.h" +#include "ui/base/metadata/metadata_header_macros.h" +#include "ui/views/controls/button/button.h" + +namespace views { +class ImageView; +class Label; +} // namespace views + +class ProjectsPanelRecentThreadsExpandButton : public views::Button { + METADATA_HEADER(ProjectsPanelRecentThreadsExpandButton, views::Button) + + public: + explicit ProjectsPanelRecentThreadsExpandButton( + base::RepeatingClosure callback); + ProjectsPanelRecentThreadsExpandButton( + const ProjectsPanelRecentThreadsExpandButton&) = delete; + ProjectsPanelRecentThreadsExpandButton& operator=( + const ProjectsPanelRecentThreadsExpandButton&) = delete; + ~ProjectsPanelRecentThreadsExpandButton() override; + + // Sets whether the list this button corresponds to is expanded, updating the + // title and icon. + void SetExpanded(bool expanded); + + views::Label* title_label_for_testing() { return title_; } + views::ImageView* icon_view_for_testing() { return icon_; } + + private: + raw_ptr<views::ImageView> icon_ = nullptr; + raw_ptr<views::Label> title_ = nullptr; +}; + +#endif // CHROME_BROWSER_UI_VIEWS_TABS_PROJECTS_PROJECTS_PANEL_RECENT_THREADS_EXPAND_BUTTON_H_
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button_unittest.cc b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button_unittest.cc new file mode 100644 index 0000000..9eb291c --- /dev/null +++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button_unittest.cc
@@ -0,0 +1,55 @@ +// Copyright 2026 The Chromium Authors +// 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/tabs/projects/projects_panel_recent_threads_expand_button.h" + +#include "base/functional/callback_helpers.h" +#include "chrome/app/vector_icons/vector_icons.h" +#include "chrome/grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/image_model.h" +#include "ui/gfx/vector_icon_types.h" +#include "ui/views/controls/image_view.h" +#include "ui/views/controls/label.h" +#include "ui/views/test/views_test_base.h" + +class ProjectsPanelRecentThreadsExpandButtonTest : public views::ViewsTestBase { + public: + ProjectsPanelRecentThreadsExpandButtonTest() = default; +}; + +TEST_F(ProjectsPanelRecentThreadsExpandButtonTest, ShowsCorrectTextAndIcon) { + auto button = std::make_unique<ProjectsPanelRecentThreadsExpandButton>( + base::DoNothing()); + + // Initially the button should be in the collapsed state. + button->SetExpanded(false); + EXPECT_EQ(button->title_label_for_testing()->GetText(), + l10n_util::GetStringUTF16(IDS_THREADS_SHOW_MORE)); + + ui::ImageModel icon_model = button->icon_view_for_testing()->GetImageModel(); + EXPECT_TRUE(icon_model.IsVectorIcon()); + EXPECT_EQ(icon_model.GetVectorIcon().vector_icon()->name, + kKeyboardArrowDownChromeRefreshIcon.name); + + // Switch to expanded. + button->SetExpanded(true); + EXPECT_EQ(button->title_label_for_testing()->GetText(), + l10n_util::GetStringUTF16(IDS_THREADS_SHOW_LESS)); + + icon_model = button->icon_view_for_testing()->GetImageModel(); + EXPECT_TRUE(icon_model.IsVectorIcon()); + EXPECT_EQ(icon_model.GetVectorIcon().vector_icon()->name, + kKeyboardArrowUpChromeRefreshIcon.name); + + // Switch back to collapsed. + button->SetExpanded(false); + EXPECT_EQ(button->title_label_for_testing()->GetText(), + l10n_util::GetStringUTF16(IDS_THREADS_SHOW_MORE)); + + icon_model = button->icon_view_for_testing()->GetImageModel(); + EXPECT_TRUE(icon_model.IsVectorIcon()); + EXPECT_EQ(icon_model.GetVectorIcon().vector_icon()->name, + kKeyboardArrowDownChromeRefreshIcon.name); +}
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.cc b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.cc index 87a564f..2f0ece6f 100644 --- a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.cc +++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.cc
@@ -8,33 +8,97 @@ #include "chrome/browser/ui/views/tabs/projects/projects_panel_thread_item_view.h" #include "components/contextual_tasks/public/contextual_task.h" #include "ui/base/metadata/metadata_impl_macros.h" -#include "ui/views/controls/label.h" +#include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/animation/tween.h" #include "ui/views/layout/box_layout.h" +namespace { +constexpr base::TimeDelta kExpandAnimationTime = base::Milliseconds(250); + +static bool disable_animations_for_testing_ = false; +} // namespace + ProjectsPanelRecentThreadsView::ProjectsPanelRecentThreadsView( ThreadPressedCallback thread_button_callback) : thread_button_callback_(std::move(thread_button_callback)) { SetLayoutManager(std::make_unique<views::BoxLayout>()) ->SetOrientation(views::BoxLayout::Orientation::kVertical); + + expansion_animation_.SetTweenType(gfx::Tween::Type::EASE_IN_OUT_EMPHASIZED); } ProjectsPanelRecentThreadsView::~ProjectsPanelRecentThreadsView() = default; void ProjectsPanelRecentThreadsView::SetThreads( const std::vector<contextual_tasks::Thread>& threads) { + threads_ = threads; + UpdateDisplayedThreads(); +} + +void ProjectsPanelRecentThreadsView::SetExpanded(bool expanded) { + if (expanded_ == expanded) { + return; + } + + // Compute the start and end heights after changing the number of threads + // displayed. + start_height_ = GetPreferredSize().height(); + expanded_ = expanded; + UpdateDisplayedThreads(); + target_height_ = GetPreferredSize().height(); + + if (disable_animations_for_testing_) { + PreferredSizeChanged(); + return; + } + + expansion_animation_.Reset(0.0); + expansion_animation_.SetSlideDuration(kExpandAnimationTime); + expansion_animation_.Show(); +} + +void ProjectsPanelRecentThreadsView::UpdateDisplayedThreads() { item_views_.clear(); RemoveAllChildViews(); size_t count = 0; - for (const auto& thread : threads) { + for (const auto& thread : threads_) { + if (count >= projects_panel::kMaxNumberOfRecentThreads || + (!expanded_ && + count >= projects_panel::kNumThreadsVisibleWhenCollapsed)) { + break; + } item_views_.push_back( AddChildView(std::make_unique<ProjectsPanelThreadItemView>( thread, thread_button_callback_))); - if (++count >= projects_panel::kMaxNumberOfRecentThreads) { - return; - } + count++; } } +gfx::Size ProjectsPanelRecentThreadsView::CalculatePreferredSize( + const views::SizeBounds& available_size) const { + gfx::Size size = views::View::CalculatePreferredSize(available_size); + if (expansion_animation_.is_animating()) { + size.set_height(gfx::Tween::IntValueBetween( + expansion_animation_.GetCurrentValue(), start_height_, target_height_)); + } + return size; +} + +void ProjectsPanelRecentThreadsView::AnimationProgressed( + const gfx::Animation* animation) { + PreferredSizeChanged(); +} + +void ProjectsPanelRecentThreadsView::AnimationEnded( + const gfx::Animation* animation) { + PreferredSizeChanged(); +} + +// static +void ProjectsPanelRecentThreadsView::disable_animations_for_testing() { + disable_animations_for_testing_ = true; +} + BEGIN_METADATA(ProjectsPanelRecentThreadsView) END_METADATA
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.h b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.h index 9710abd..3c57e856 100644 --- a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.h +++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.h
@@ -7,13 +7,16 @@ #include "base/functional/callback_helpers.h" #include "chrome/browser/ui/views/tabs/projects/projects_panel_thread_item_view.h" +#include "ui/gfx/animation/animation_delegate.h" +#include "ui/gfx/animation/slide_animation.h" #include "ui/views/view.h" namespace contextual_tasks { struct Thread; } -class ProjectsPanelRecentThreadsView : public views::View { +class ProjectsPanelRecentThreadsView : public views::View, + public gfx::AnimationDelegate { METADATA_HEADER(ProjectsPanelRecentThreadsView, views::View) public: @@ -31,13 +34,35 @@ // Updates the threads shown in the list. void SetThreads(const std::vector<contextual_tasks::Thread>& threads); + // Sets whether the list is expanded to show all threads. + void SetExpanded(bool expanded); + bool expanded() { return expanded_; } + + // views::View: + gfx::Size CalculatePreferredSize( + const views::SizeBounds& available_size) const override; + + // gfx::AnimationDelegate: + void AnimationProgressed(const gfx::Animation* animation) override; + void AnimationEnded(const gfx::Animation* animation) override; + const std::vector<ProjectsPanelThreadItemView*> item_views_for_testing() { return item_views_; } + static void disable_animations_for_testing(); + private: + void UpdateDisplayedThreads(); + + std::vector<contextual_tasks::Thread> threads_; + bool expanded_ = false; std::vector<ProjectsPanelThreadItemView*> item_views_; ThreadPressedCallback thread_button_callback_; + + gfx::SlideAnimation expansion_animation_{this}; + int start_height_ = 0; + int target_height_ = 0; }; #endif // CHROME_BROWSER_UI_VIEWS_TABS_PROJECTS_PROJECTS_PANEL_RECENT_THREADS_VIEW_H_
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view_unittest.cc b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view_unittest.cc index c000340..b629411 100644 --- a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view_unittest.cc +++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view_unittest.cc
@@ -46,6 +46,18 @@ return *thread; } +const contextual_tasks::Thread& GetThread4() { + static const base::NoDestructor<contextual_tasks::Thread> thread( + CreateThread("Thread 4", "id4")); + return *thread; +} + +const std::vector<contextual_tasks::Thread>& GetManyThreads() { + static const base::NoDestructor<std::vector<contextual_tasks::Thread>> + threads({GetThread1(), GetThread2(), GetThread3(), GetThread4()}); + return *threads; +} + MATCHER_P(IsForThread, expected_thread, "") { const views::Label* label = arg->title_for_testing(); return base::UTF8ToUTF16(expected_thread.title) == label->GetText(); @@ -128,7 +140,45 @@ auto recent_threads_view = std::make_unique<ProjectsPanelRecentThreadsView>(); recent_threads_view->SetThreads(threads); + recent_threads_view->SetExpanded(true); + // Ensure only the max number of threads are shown. EXPECT_EQ(projects_panel::kMaxNumberOfRecentThreads, recent_threads_view->item_views_for_testing().size()); } + +TEST_F(ProjectsPanelRecentThreadsViewTest, CollapsesThreadsInitially) { + auto recent_threads_view = std::make_unique<ProjectsPanelRecentThreadsView>(); + recent_threads_view->SetThreads(GetManyThreads()); + + // Only 3 thread should be visible. + EXPECT_EQ(3u, recent_threads_view->children().size()); + EXPECT_EQ(3u, recent_threads_view->item_views_for_testing().size()); +} + +TEST_F(ProjectsPanelRecentThreadsViewTest, ExpandList) { + auto recent_threads_view = std::make_unique<ProjectsPanelRecentThreadsView>(); + recent_threads_view->SetThreads(GetManyThreads()); + + recent_threads_view->SetExpanded(true); + + // All 4 threads should be visible after expanding. + EXPECT_EQ(4u, recent_threads_view->children().size()); + EXPECT_EQ(4u, recent_threads_view->item_views_for_testing().size()); + EXPECT_TRUE(recent_threads_view->expanded()); +} + +TEST_F(ProjectsPanelRecentThreadsViewTest, CollapseList) { + auto recent_threads_view = std::make_unique<ProjectsPanelRecentThreadsView>(); + recent_threads_view->SetThreads(GetManyThreads()); + recent_threads_view->SetExpanded(true); + + EXPECT_EQ(4u, recent_threads_view->children().size()); + + recent_threads_view->SetExpanded(false); + + // Only 3 threads should be visible after collapsing. + EXPECT_EQ(3u, recent_threads_view->children().size()); + EXPECT_EQ(3u, recent_threads_view->item_views_for_testing().size()); + EXPECT_FALSE(recent_threads_view->expanded()); +}
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_view.cc b/chrome/browser/ui/views/tabs/projects/projects_panel_view.cc index 6e613ec..9c98d81 100644 --- a/chrome/browser/ui/views/tabs/projects/projects_panel_view.cc +++ b/chrome/browser/ui/views/tabs/projects/projects_panel_view.cc
@@ -30,6 +30,7 @@ #include "chrome/browser/ui/views/tabs/projects/layout_constants.h" #include "chrome/browser/ui/views/tabs/projects/projects_panel_controller.h" #include "chrome/browser/ui/views/tabs/projects/projects_panel_controls_view.h" +#include "chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.h" #include "chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.h" #include "chrome/browser/ui/views/tabs/projects/projects_panel_tab_groups_view.h" #include "chrome/browser/ui/views/tabs/projects/projects_panel_utils.h" @@ -51,6 +52,7 @@ #include "ui/gfx/text_constants.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/actions/action_view_controller.h" +#include "ui/views/animation/ink_drop.h" #include "ui/views/background.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/button/image_button_factory.h" @@ -83,7 +85,7 @@ constexpr int kListHeaderHeight = 28; constexpr int kCreateNewTabGroupIconSize = 20; constexpr gfx::Insets kCreateNewTabGroupIconMargins = - gfx::Insets::TLBR(0, 4, 0, 0); + gfx::Insets::TLBR(0, 2, 0, 2); constexpr base::TimeDelta kPanelShowAnimationDuration = base::Milliseconds(250); constexpr base::TimeDelta kPanelHideAnimationDuration = base::Milliseconds(200); @@ -329,8 +331,16 @@ std::make_unique<ProjectsPanelRecentThreadsView>(base::BindRepeating( &ProjectsPanelView::OnThreadButtonPressed, base::Unretained(this))); threads_view_ = threads_scroll_view->SetContents(std::move(threads_view)); + if (disable_animations_for_testing_) { + threads_view_->disable_animations_for_testing(); // IN-TEST + } SetScrollViewProperties(*threads_scroll_view); + threads_expand_button_ = threads_container_->AddChildView( + std::make_unique<ProjectsPanelRecentThreadsExpandButton>( + base::BindRepeating(&ProjectsPanelView::OnThreadExpandButtonPressed, + base::Unretained(this)))); + threads_activity_menu_model_ = std::make_unique<ui::SimpleMenuModel>(this); threads_activity_menu_model_->AddItemWithIcon( kGeminiActivity, @@ -429,6 +439,12 @@ const bool show_threads = show_threads_for_testing_ || threads.size() > 0; threads_container_->SetVisible(show_threads); separator_->SetVisible(show_threads); + + if (show_threads) { + threads_expand_button_->SetExpanded(threads_view_->expanded()); + threads_expand_button_->SetVisible( + threads.size() > projects_panel::kNumThreadsVisibleWhenCollapsed); + } } base::UmaHistogramCounts100( @@ -718,6 +734,12 @@ ClosePanel(); } +void ProjectsPanelView::OnThreadExpandButtonPressed() { + const bool expanded = !threads_view_->expanded(); + threads_view_->SetExpanded(expanded); + threads_expand_button_->SetExpanded(expanded); +} + void ProjectsPanelView::OnThreadsActivityMenuButtonPressed() { threads_activity_menu_runner_->RunMenuAt( GetWidget(), threads_activity_menu_button_->button_controller(),
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_view.h b/chrome/browser/ui/views/tabs/projects/projects_panel_view.h index bd020ea..dd69fda3 100644 --- a/chrome/browser/ui/views/tabs/projects/projects_panel_view.h +++ b/chrome/browser/ui/views/tabs/projects/projects_panel_view.h
@@ -43,6 +43,7 @@ class BrowserWindowInterface; class ProjectsPanelController; +class ProjectsPanelRecentThreadsExpandButton; class ProjectsPanelRecentThreadsView; class ProjectsPanelStateController; class ProjectsPanelTabGroupsView; @@ -163,6 +164,7 @@ contextual_tasks::ThreadType thread_type); void OnTabGroupDragUpdated(const gfx::Point& location); void OnTabGroupDragExited(); + void OnThreadExpandButtonPressed(); const raw_ptr<BrowserWindowInterface> browser_; raw_ptr<actions::ActionItem> root_action_item_ = nullptr; @@ -174,6 +176,8 @@ raw_ptr<ProjectsPanelTabGroupsView> tab_groups_view_ = nullptr; raw_ptr<views::View> threads_container_ = nullptr; raw_ptr<ProjectsPanelRecentThreadsView> threads_view_ = nullptr; + raw_ptr<ProjectsPanelRecentThreadsExpandButton> threads_expand_button_ = + nullptr; raw_ptr<views::Separator> separator_ = nullptr; raw_ptr<views::MenuButton> threads_activity_menu_button_ = nullptr; raw_ptr<views::Button> create_new_tab_group_button_ = nullptr;
diff --git a/chrome/browser/ui/views/tabs/shared/drop_arrow.cc b/chrome/browser/ui/views/tabs/shared/drop_arrow.cc index 39f48ce..1a1e7f8 100644 --- a/chrome/browser/ui/views/tabs/shared/drop_arrow.cc +++ b/chrome/browser/ui/views/tabs/shared/drop_arrow.cc
@@ -7,25 +7,36 @@ #include <utility> #include "base/notreached.h" -#include "chrome/grit/theme_resources.h" +#include "build/build_config.h" +#include "chrome/app/vector_icons/vector_icons.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/base/models/image_model.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image_skia.h" +#include "ui/gfx/image/image_skia_operations.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/shadow_value.h" #include "ui/views/controls/image_view.h" +#include "ui/views/layout/layout_provider.h" #include "ui/views/widget/widget.h" namespace { -int GetDropArrowImageResourceId(DropArrow::Direction direction) { +gfx::ShadowValues GetShadowValues() { + constexpr int kShadowBlur = 3; + return {gfx::ShadowValue(gfx::Vector2d(0, 0), kShadowBlur, + SkColorSetA(SK_ColorBLACK, 0xFF))}; +} + +const gfx::VectorIcon& GetDropArrowIcon(DropArrow::Direction direction) { switch (direction) { case DropArrow::Direction::kUp: - return IDR_TAB_DROP_UP; + return kArrowUpwardIcon; case DropArrow::Direction::kDown: - return IDR_TAB_DROP_DOWN; + return kArrowDownwardIcon; case DropArrow::Direction::kLeft: - return IDR_TAB_DROP_LEFT; + return kArrowBackIcon; case DropArrow::Direction::kRight: - return IDR_TAB_DROP_RIGHT; + return kArrowForwardIcon; default: NOTREACHED(); } @@ -44,7 +55,7 @@ params.z_order = ui::ZOrderLevel::kFloatingUIElement; params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent; params.accept_events = false; - params.bounds = gfx::Rect(GetSize()); + params.bounds = gfx::Rect(kSize, kSize); params.context = context; arrow_widget_->Init(std::move(params)); arrow_view_ = @@ -57,22 +68,24 @@ } DropArrow::~DropArrow() { - // Close eventually deletes the window, which deletes arrow_view too. + // Close eventually deletes the window, which deletes arrow_view_ too. if (arrow_widget_) { arrow_widget_->Close(); } } // static -gfx::Size DropArrow::GetSize() { - static const gfx::Size size = []() { - // Direction doesn't matter, both images are the same size. - const gfx::ImageSkia* drop_image = - ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( - GetDropArrowImageResourceId(Direction::kDown)); - return gfx::Size(drop_image->width(), drop_image->height()); - }(); - return size; +void DropArrow::MaybeAdjustDisplayBounds(gfx::Rect& display_bounds) { +#if BUILDFLAG(IS_LINUX) + // On Linux, `GetBoundsInScreen` returns coordinates relative to the browser + // window (plus shadow elevation outsets) rather than the screen. To handle + // this, we adjust the display bounds by the difference between the drop arrow + // size and the window elevation. Note that this only works on the first + // display. + const int elevation = views::LayoutProvider::Get()->GetShadowElevationMetric( + views::Emphasis::kHigh); + display_bounds.Outset(DropArrow::kSize - elevation); +#endif } void DropArrow::SetIndex(const BrowserRootView::DropIndex& index) { @@ -80,23 +93,26 @@ UpdateBounds(); } +void DropArrow::OnWidgetDestroying(views::Widget* widget) { + DCHECK(scoped_observation_.IsObservingSource(arrow_widget_.get())); + scoped_observation_.Reset(); + arrow_view_ = nullptr; + arrow_widget_ = nullptr; +} + void DropArrow::UpdateBounds() { Direction direction; gfx::Rect drop_bounds = bounds_callback_.Run(index_, &direction); if (!direction_.has_value() || direction_ != direction) { direction_ = direction; - arrow_view_->SetImage(ui::ImageModel::FromResourceId( - GetDropArrowImageResourceId(*direction_))); + gfx::ImageSkia icon = gfx::CreateVectorIcon(GetDropArrowIcon(*direction_), + kSize, SK_ColorWHITE); + arrow_view_->SetImage(ui::ImageModel::FromImageSkia( + gfx::ImageSkiaOperations::CreateImageWithDropShadow( + icon, GetShadowValues()))); arrow_view_->SchedulePaint(); } arrow_widget_->SetBounds(drop_bounds); } - -void DropArrow::OnWidgetDestroying(views::Widget* widget) { - DCHECK(scoped_observation_.IsObservingSource(arrow_widget_.get())); - scoped_observation_.Reset(); - arrow_view_ = nullptr; - arrow_widget_ = nullptr; -}
diff --git a/chrome/browser/ui/views/tabs/shared/drop_arrow.h b/chrome/browser/ui/views/tabs/shared/drop_arrow.h index 9c5a81ca..bf451448 100644 --- a/chrome/browser/ui/views/tabs/shared/drop_arrow.h +++ b/chrome/browser/ui/views/tabs/shared/drop_arrow.h
@@ -45,9 +45,12 @@ DropArrow& operator=(const DropArrow&) = delete; ~DropArrow() override; - // Returns the size of the arrow image. Height represents the length of the - // arrow in the direction it points and width is the opposite dimension. - static gfx::Size GetSize(); + // Represents the size of the arrow image, excluding the drop shadow. + static constexpr int kSize = 20; + + // Potentially adjusts `display_bounds` based on platform-specific + // requirements. + static void MaybeAdjustDisplayBounds(gfx::Rect& display_bounds); void SetIndex(const BrowserRootView::DropIndex& index); BrowserRootView::DropIndex index() const { return index_; }
diff --git a/chrome/browser/ui/views/tabs/tab_container_impl.cc b/chrome/browser/ui/views/tabs/tab_container_impl.cc index 5872d41..c164a34d 100644 --- a/chrome/browser/ui/views/tabs/tab_container_impl.cc +++ b/chrome/browser/ui/views/tabs/tab_container_impl.cc
@@ -41,6 +41,7 @@ #include "ui/gfx/image/image_skia_operations.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/controls/image_view.h" +#include "ui/views/layout/layout_provider.h" #include "ui/views/mouse_watcher_view_host.h" #include "ui/views/rect_based_targeting_utils.h" #include "ui/views/view_utils.h" @@ -1642,18 +1643,19 @@ center_x = GetMirroredXInView(center_x); // Determine the screen bounds. - const gfx::Size drop_arrow_size = DropArrow::GetSize(); - gfx::Point drop_loc(center_x - drop_arrow_size.width() / 2, - -drop_arrow_size.height()); + gfx::Point drop_loc(center_x - DropArrow::kSize / 2, -DropArrow::kSize); ConvertPointToScreen(this, &drop_loc); - gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), drop_arrow_size.width(), - drop_arrow_size.height()); + gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), DropArrow::kSize, + DropArrow::kSize); // If the rect doesn't fit on the monitor, push the arrow to the bottom. display::Screen* screen = display::Screen::Get(); - display::Display display = - screen->GetDisplayNearestView(GetWidget()->GetNativeView()); - const bool is_beneath = !display.bounds().Contains(drop_bounds); + gfx::Rect display_bounds = + screen->GetDisplayNearestView(GetWidget()->GetNativeView()).bounds(); + + DropArrow::MaybeAdjustDisplayBounds(display_bounds); + + const bool is_beneath = drop_bounds.y() < display_bounds.y(); *direction = is_beneath ? DropArrow::Direction::kUp : DropArrow::Direction::kDown; if (is_beneath) {
diff --git a/chrome/browser/ui/views/tabs/tab_group_style.cc b/chrome/browser/ui/views/tabs/tab_group_style.cc index 6d70878..217849b 100644 --- a/chrome/browser/ui/views/tabs/tab_group_style.cc +++ b/chrome/browser/ui/views/tabs/tab_group_style.cc
@@ -35,6 +35,7 @@ constexpr int kEmptyChipSize = 20; constexpr int kCornerRadius = 6; constexpr int kDetachedTabsCornerRadius = 100; +constexpr int kDetachedTabsHorizontalInsets = 8; constexpr int kTabGroupOverlapAdjustment = 2; } // namespace @@ -113,10 +114,9 @@ gfx::Insets TabGroupStyle::GetInsetsForHeaderChip() const { if (base::FeatureList::IsEnabled(features::kDetachedTabs)) { - return gfx::Insets::TLBR(0, kCornerRadius, 0, kCornerRadius); + return gfx::Insets::VH(0, kDetachedTabsHorizontalInsets); } - return gfx::Insets::TLBR(kHeaderChipVerticalInset, kCornerRadius, - kHeaderChipVerticalInset, kCornerRadius); + return gfx::Insets::VH(kHeaderChipVerticalInset, kCornerRadius); } int TabGroupStyle::GetTitleAdjustmentToTabGroupHeaderDesiredWidth(
diff --git a/chrome/browser/ui/views/tabs/tab_search_container.cc b/chrome/browser/ui/views/tabs/tab_search_container.cc deleted file mode 100644 index 688b6704..0000000 --- a/chrome/browser/ui/views/tabs/tab_search_container.cc +++ /dev/null
@@ -1,446 +0,0 @@ -// Copyright 2023 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/views/tabs/tab_search_container.h" - -#include <memory> - -#include "base/i18n/rtl.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" -#include "base/time/time.h" -#include "base/types/pass_key.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/browser_window/public/browser_window_features.h" -#include "chrome/browser/ui/browser_window/public/browser_window_interface.h" -#include "chrome/browser/ui/tabs/organization/tab_organization_service.h" -#include "chrome/browser/ui/tabs/organization/tab_organization_service_factory.h" -#include "chrome/browser/ui/tabs/organization/tab_organization_utils.h" -#include "chrome/browser/ui/ui_features.h" -#include "chrome/browser/ui/views/frame/browser_view.h" -#include "chrome/browser/ui/views/tabs/tab_search_button.h" -#include "chrome/browser/ui/views/tabs/tab_strip.h" -#include "chrome/browser/ui/views/tabs/tab_strip_controller.h" -#include "chrome/browser/ui/views/tabs/tab_strip_nudge_button.h" -#include "chrome/browser/ui/webui/tab_search/tab_search.mojom.h" -#include "chrome/common/chrome_features.h" -#include "chrome/grit/generated_resources.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/base/metadata/metadata_impl_macros.h" -#include "ui/gfx/animation/tween.h" -#include "ui/gfx/vector_icon_types.h" -#include "ui/views/animation/animation_delegate_views.h" -#include "ui/views/layout/flex_layout.h" -#include "ui/views/mouse_watcher.h" -#include "ui/views/mouse_watcher_view_host.h" -#include "ui/views/view_class_properties.h" - -namespace { - -constexpr base::TimeDelta kExpansionInDuration = base::Milliseconds(500); -constexpr base::TimeDelta kExpansionOutDuration = base::Milliseconds(250); -constexpr base::TimeDelta kFlatEdgeInDuration = base::Milliseconds(400); -constexpr base::TimeDelta kFlatEdgeOutDuration = base::Milliseconds(250); -constexpr base::TimeDelta kOpacityInDuration = base::Milliseconds(300); -constexpr base::TimeDelta kOpacityOutDuration = base::Milliseconds(100); -constexpr base::TimeDelta kOpacityDelay = base::Milliseconds(100); -constexpr base::TimeDelta kShowDuration = base::Seconds(16); -constexpr int kSpaceBetweenButtons = 2; - -Edge GetFlatEdge(bool is_search_button, bool tab_search_before_chips) { - const bool is_rtl = base::i18n::IsRTL(); - if ((!is_search_button && tab_search_before_chips) || - (is_search_button && !tab_search_before_chips)) { - return is_rtl ? Edge::kRight : Edge::kLeft; - } - return is_rtl ? Edge::kLeft : Edge::kRight; -} - -} // namespace - -TabSearchContainer::TabOrganizationAnimationSession:: - TabOrganizationAnimationSession( - TabStripNudgeButton* button, - TabSearchContainer* container, - AnimationSessionType session_type, - base::OnceCallback<void()> on_animation_ended) - : button_(button), - container_(container), - expansion_animation_(container), - flat_edge_animation_(container), - opacity_animation_(container), - session_type_(session_type), - on_animation_ended_(std::move(on_animation_ended)) { - if (session_type_ == AnimationSessionType::HIDE) { - expansion_animation_.Reset(1); - flat_edge_animation_.Reset(1); - opacity_animation_.Reset(1); - } -} - -TabSearchContainer::TabOrganizationAnimationSession:: - ~TabOrganizationAnimationSession() = default; - -void TabSearchContainer::TabOrganizationAnimationSession::ApplyAnimationValue( - const gfx::Animation* animation) { - float value = animation->GetCurrentValue(); - if (animation == &expansion_animation_) { - button_->SetWidthFactor(value); - } else if (animation == &flat_edge_animation_) { - container_->tab_search_button()->SetFlatEdgeFactor(1 - value); - button_->SetFlatEdgeFactor(1 - value); - } else if (animation == &opacity_animation_) { - button_->SetOpacity(value); - } -} - -void TabSearchContainer::TabOrganizationAnimationSession::MarkAnimationDone( - const gfx::Animation* animation) { - if (animation == &expansion_animation_) { - expansion_animation_done_ = true; - } else if (animation == &flat_edge_animation_) { - flat_edge_animation_done_ = true; - } else { - opacity_animation_done_ = true; - } - - if (expansion_animation_done_ && flat_edge_animation_done_ && - opacity_animation_done_) { - if (on_animation_ended_) { - std::move(on_animation_ended_).Run(); - } - } -} - -void TabSearchContainer::TabOrganizationAnimationSession::Start() { - if (session_type_ == - TabOrganizationAnimationSession::AnimationSessionType::SHOW) { - Show(); - } else { - Hide(); - } -} - -void TabSearchContainer::TabOrganizationAnimationSession:: - ResetExpansionAnimationForTesting(double value) { - expansion_animation_.Reset(value); -} - -void TabSearchContainer::TabOrganizationAnimationSession:: - ResetOpacityAnimationForTesting(double value) { - if (opacity_animation_delay_timer_.IsRunning()) { - opacity_animation_delay_timer_.FireNow(); - } - - opacity_animation_.Reset(value); -} - -void TabSearchContainer::TabOrganizationAnimationSession:: - ResetFlatEdgeAnimationForTesting(double value) { - flat_edge_animation_.Reset(value); -} - -void TabSearchContainer::TabOrganizationAnimationSession::Hide() { - // Animate and hide existing chip. - if (session_type_ == - TabOrganizationAnimationSession::AnimationSessionType::SHOW) { - if (opacity_animation_delay_timer_.IsRunning()) { - opacity_animation_delay_timer_.FireNow(); - } - session_type_ = TabOrganizationAnimationSession::AnimationSessionType::HIDE; - } - - expansion_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100); - opacity_animation_.SetTweenType(gfx::Tween::Type::LINEAR); - flat_edge_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100); - - expansion_animation_.SetSlideDuration( - gfx::Animation::RichAnimationDuration(kExpansionOutDuration)); - - flat_edge_animation_.SetSlideDuration( - gfx::Animation::RichAnimationDuration(kFlatEdgeOutDuration)); - - opacity_animation_.SetSlideDuration( - gfx::Animation::RichAnimationDuration(kOpacityOutDuration)); - - expansion_animation_.Hide(); - flat_edge_animation_.Hide(); - opacity_animation_.Hide(); -} - -void TabSearchContainer::TabOrganizationAnimationSession:: - ShowOpacityAnimation() { - opacity_animation_.Show(); -} - -void TabSearchContainer::TabOrganizationAnimationSession::Show() { - expansion_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100); - opacity_animation_.SetTweenType(gfx::Tween::Type::LINEAR); - flat_edge_animation_.SetTweenType(gfx::Tween::Type::LINEAR); - - expansion_animation_.SetSlideDuration( - gfx::Animation::RichAnimationDuration(kExpansionInDuration)); - flat_edge_animation_.SetSlideDuration( - gfx::Animation::RichAnimationDuration(kFlatEdgeInDuration)); - opacity_animation_.SetSlideDuration( - gfx::Animation::RichAnimationDuration(kOpacityInDuration)); - - expansion_animation_.Show(); - flat_edge_animation_.Show(); - - const base::TimeDelta delay = - gfx::Animation::RichAnimationDuration(kOpacityDelay); - opacity_animation_delay_timer_.Start( - FROM_HERE, delay, this, - &TabSearchContainer::TabOrganizationAnimationSession:: - ShowOpacityAnimation); -} - -TabSearchContainer::TabSearchContainer(bool tab_search_before_chips, - View* locked_expansion_view, - TabStrip* tab_strip) - : AnimationDelegateViews(this), - locked_expansion_view_(locked_expansion_view) { - TabStripController* const tab_strip_controller = tab_strip->controller(); - browser_window_interface_ = tab_strip_controller->GetBrowserWindowInterface(); - SetProperty(views::kElementIdentifierKey, kTabSearchContainerElementId); - mouse_watcher_ = std::make_unique<views::MouseWatcher>( - std::make_unique<views::MouseWatcherViewHost>(locked_expansion_view, - gfx::Insets()), - this); - - tab_organization_service_ = TabOrganizationServiceFactory::GetForProfile( - browser_window_interface_->GetProfile()); - if (tab_organization_service_) { - tab_organization_observation_.Observe(tab_organization_service_); - } - - // Edge adjacent to new tab button should be rounded and opposite edge - // should animate to flat on chip show. - std::unique_ptr<TabSearchButton> tab_search_button = - std::make_unique<TabSearchButton>( - browser_window_interface_, Edge::kNone, - GetFlatEdge(true, tab_search_before_chips)); - tab_search_button->SetProperty(views::kCrossAxisAlignmentKey, - views::LayoutAlignment::kCenter); - tab_search_button_ = AddChildView(std::move(tab_search_button)); - - int tab_search_button_index = GetIndexOf(tab_search_button_).value(); - int index = tab_search_before_chips ? tab_search_button_index + 1 - : tab_search_button_index; - // TODO(crbug.com/40925230): Consider hiding the button when the request has - // started, vs. when the button as clicked. - auto_tab_group_button_ = - AddChildViewAt(CreateAutoTabGroupButton(tab_search_before_chips), index); - - SetupButtonProperties(auto_tab_group_button_, tab_search_before_chips); - - SetLayoutManager(std::make_unique<views::FlexLayout>()); -} - -TabSearchContainer::~TabSearchContainer() { - if (scoped_tab_strip_modal_ui_) { - scoped_tab_strip_modal_ui_.reset(); - } -} - -void TabSearchContainer::ShowTabOrganization(TabStripNudgeButton* button) { - if (locked_expansion_view_->IsMouseHovered()) { - SetLockedExpansionMode(LockedExpansionMode::kWillShow, button); - } - if (locked_expansion_mode_ == LockedExpansionMode::kNone) { - ExecuteShowTabOrganization(button); - } -} - -void TabSearchContainer::HideTabOrganization(TabStripNudgeButton* button) { - if (locked_expansion_view_->IsMouseHovered()) { - SetLockedExpansionMode(LockedExpansionMode::kWillHide, button); - } - if (locked_expansion_mode_ == LockedExpansionMode::kNone) { - ExecuteHideTabOrganization(button); - } -} - -void TabSearchContainer::SetLockedExpansionModeForTesting( - LockedExpansionMode mode, - TabStripNudgeButton* button) { - SetLockedExpansionMode(mode, button); -} - -void TabSearchContainer::OnAutoTabGroupButtonClicked() { - tab_organization_service_->OnActionUIAccepted( - browser_window_interface_->GetBrowserForMigrationOnly()); - - // Force hide the button when pressed, bypassing locked expansion mode. - ExecuteHideTabOrganization(auto_tab_group_button_); -} - -void TabSearchContainer::OnAutoTabGroupButtonDismissed() { - tab_organization_service_->OnActionUIDismissed( - browser_window_interface_->GetBrowserForMigrationOnly()); - - // Force hide the button when pressed, bypassing locked expansion mode. - ExecuteHideTabOrganization(auto_tab_group_button_); -} - -void TabSearchContainer::OnOrganizeButtonTimeout(TabStripNudgeButton* button) { - // Hide the button if not pressed. Use locked expansion mode to avoid - // disrupting the user. - HideTabOrganization(button); -} - -void TabSearchContainer::MouseMovedOutOfHost() { - SetLockedExpansionMode(LockedExpansionMode::kNone, nullptr); -} - -void TabSearchContainer::AnimationCanceled(const gfx::Animation* animation) { - AnimationEnded(animation); -} - -void TabSearchContainer::AnimationEnded(const gfx::Animation* animation) { - animation_session_->ApplyAnimationValue(animation); - animation_session_->MarkAnimationDone(animation); -} - -void TabSearchContainer::AnimationProgressed(const gfx::Animation* animation) { - animation_session_->ApplyAnimationValue(animation); -} - -void TabSearchContainer::OnToggleActionUIState(const Browser* browser, - bool should_show) { - CHECK(tab_organization_service_); - - if (locked_expansion_mode_ != LockedExpansionMode::kNone) { - return; - } - - if (should_show && browser_window_interface_ == browser) { - ShowTabOrganization(auto_tab_group_button_); - } else { - HideTabOrganization(auto_tab_group_button_); - } -} - -void TabSearchContainer::SetLockedExpansionMode(LockedExpansionMode mode, - TabStripNudgeButton* button) { - if (mode == LockedExpansionMode::kNone) { - if (locked_expansion_mode_ == LockedExpansionMode::kWillShow) { - ExecuteShowTabOrganization(locked_expansion_button_); - } else if (locked_expansion_mode_ == LockedExpansionMode::kWillHide) { - ExecuteHideTabOrganization(locked_expansion_button_); - } - locked_expansion_button_ = nullptr; - } else { - locked_expansion_button_ = button; - mouse_watcher_->Start(GetWidget()->GetNativeWindow()); - } - locked_expansion_mode_ = mode; -} - -void TabSearchContainer::ExecuteShowTabOrganization( - TabStripNudgeButton* button) { - if (browser_window_interface_ && (button == auto_tab_group_button_) && - !TabOrganizationUtils::GetInstance()->IsEnabled( - browser_window_interface_->GetProfile())) { - return; - } - - TabStripModel* const tab_strip_model = - browser_window_interface_->GetTabStripModel(); - // If the tab strip already has a modal UI showing, exit early. - if (!tab_strip_model->CanShowModalUI()) { - return; - } - - scoped_tab_strip_modal_ui_ = tab_strip_model->ShowModalUI(); - - animation_session_ = std::make_unique<TabOrganizationAnimationSession>( - button, this, TabOrganizationAnimationSession::AnimationSessionType::SHOW, - base::BindOnce(&TabSearchContainer::OnAnimationSessionEnded, - base::Unretained(this))); - animation_session_->Start(); - - hide_tab_organization_timer_.Start( - FROM_HERE, kShowDuration, - base::BindOnce(&TabSearchContainer::OnOrganizeButtonTimeout, - base::Unretained(this), button)); -} - -void TabSearchContainer::ExecuteHideTabOrganization( - TabStripNudgeButton* button) { - // Hide the current animation if the shown button is the same button. Do not - // create a new animation session. - if (animation_session_ && - animation_session_->session_type() == - TabOrganizationAnimationSession::AnimationSessionType::SHOW && - animation_session_->button() == button) { - hide_tab_organization_timer_.Stop(); - animation_session_->Hide(); - return; - } - - if (!button->GetVisible()) { - return; - } - - // Stop the timer since the chip might be getting hidden on user actions like - // dismissal or click and not timeout. - hide_tab_organization_timer_.Stop(); - animation_session_ = std::make_unique<TabOrganizationAnimationSession>( - button, this, TabOrganizationAnimationSession::AnimationSessionType::HIDE, - base::BindOnce(&TabSearchContainer::OnAnimationSessionEnded, - base::Unretained(this))); - animation_session_->Start(); -} - -void TabSearchContainer::OnAnimationSessionEnded() { - // If the button went from shown -> hidden, unblock the tab strip from - // showing other modal UIs. - if (animation_session_->session_type() == - TabOrganizationAnimationSession::AnimationSessionType::HIDE) { - scoped_tab_strip_modal_ui_.reset(); - } - - animation_session_.reset(); -} - -std::unique_ptr<TabStripNudgeButton> -TabSearchContainer::CreateAutoTabGroupButton(bool tab_search_before_chips) { - auto button = std::make_unique<TabStripNudgeButton>( - browser_window_interface_, - base::BindRepeating(&TabSearchContainer::OnAutoTabGroupButtonClicked, - base::Unretained(this)), - base::BindRepeating(&TabSearchContainer::OnAutoTabGroupButtonDismissed, - base::Unretained(this)), - l10n_util::GetStringUTF16(IDS_TAB_ORGANIZE), kAutoTabGroupButtonElementId, - GetFlatEdge(false, tab_search_before_chips), gfx::VectorIcon::EmptyIcon(), - /*show_close_button=*/true); - button->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ORGANIZE)); - button->GetViewAccessibility().SetName( - l10n_util::GetStringUTF16(IDS_ACCNAME_TAB_ORGANIZE)); - button->SetProperty(views::kCrossAxisAlignmentKey, - views::LayoutAlignment::kCenter); - return button; -} - -void TabSearchContainer::SetupButtonProperties(TabStripNudgeButton* button, - bool tab_search_before_chips) { - // Set the margins for the button - gfx::Insets margin; - if (tab_search_before_chips) { - margin.set_left(kSpaceBetweenButtons); - } else { - margin.set_right(kSpaceBetweenButtons); - } - button->SetProperty(views::kMarginsKey, margin); - - // Set opacity for the button - button->SetOpacity(0); -} - -BEGIN_METADATA(TabSearchContainer) -END_METADATA
diff --git a/chrome/browser/ui/views/tabs/tab_search_container.h b/chrome/browser/ui/views/tabs/tab_search_container.h deleted file mode 100644 index 20b5cf4..0000000 --- a/chrome/browser/ui/views/tabs/tab_search_container.h +++ /dev/null
@@ -1,178 +0,0 @@ -// Copyright 2023 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_UI_VIEWS_TABS_TAB_SEARCH_CONTAINER_H_ -#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_SEARCH_CONTAINER_H_ - -#include <memory> - -#include "base/memory/raw_ptr.h" -#include "base/timer/timer.h" -#include "chrome/browser/ui/tabs/organization/tab_organization_observer.h" -#include "ui/base/metadata/metadata_header_macros.h" -#include "ui/gfx/animation/animation.h" -#include "ui/gfx/animation/slide_animation.h" -#include "ui/views/animation/animation_delegate_views.h" -#include "ui/views/mouse_watcher.h" -#include "ui/views/view.h" - -enum class Edge; -class BrowserWindowInterface; -class TabStripNudgeButton; -class TabOrganizationService; -class TabSearchButton; -class TabStrip; -class ScopedTabStripModalUI; - -enum class LockedExpansionMode { - kNone = 0, - kWillShow, - kWillHide, -}; - -class TabSearchContainer : public views::View, - public views::AnimationDelegateViews, - public TabOrganizationObserver, - public views::MouseWatcherListener { - METADATA_HEADER(TabSearchContainer, views::View) - - public: - class TabOrganizationAnimationSession { - public: - enum class AnimationSessionType { SHOW, HIDE }; - - TabOrganizationAnimationSession( - TabStripNudgeButton* button, - TabSearchContainer* container, - AnimationSessionType session_type, - base::OnceCallback<void()> on_animation_ended); - ~TabOrganizationAnimationSession(); - void ApplyAnimationValue(const gfx::Animation* animation); - void MarkAnimationDone(const gfx::Animation* animation); - void Start(); - AnimationSessionType session_type() { return session_type_; } - - gfx::SlideAnimation* expansion_animation() { return &expansion_animation_; } - void ResetExpansionAnimationForTesting(double value); - void ResetOpacityAnimationForTesting(double value); - void ResetFlatEdgeAnimationForTesting(double value); - - void Hide(); - TabStripNudgeButton* button() { return button_; } - - private: - void ShowOpacityAnimation(); - void Show(); - raw_ptr<TabStripNudgeButton> button_; - raw_ptr<TabSearchContainer> container_; - - gfx::SlideAnimation expansion_animation_; - gfx::SlideAnimation flat_edge_animation_; - gfx::SlideAnimation opacity_animation_; - - bool expansion_animation_done_ = false; - bool flat_edge_animation_done_ = false; - bool opacity_animation_done_ = false; - AnimationSessionType session_type_; - - // Timer for initiating the opacity animation during show. - base::OneShotTimer opacity_animation_delay_timer_; - - // Callback to container after animation has ended. - base::OnceCallback<void()> on_animation_ended_; - }; - - // TODO(382097906): Pull tabslotcontroller out of tabstrip and pass - // that instead. - TabSearchContainer(bool tab_search_before_chips, - View* locked_expansion_view, - TabStrip* tab_strip); - TabSearchContainer(const TabSearchContainer&) = delete; - TabSearchContainer& operator=(const TabSearchContainer&) = delete; - ~TabSearchContainer() override; - - TabStripNudgeButton* auto_tab_group_button() { - return auto_tab_group_button_; - } - - TabSearchButton* tab_search_button() { return tab_search_button_; } - - TabOrganizationAnimationSession* animation_session_for_testing() { - return animation_session_.get(); - } - - TabOrganizationService* tab_organization_service_for_testing() { - return tab_organization_service_; - } - - void ShowTabOrganization(TabStripNudgeButton* button); - void HideTabOrganization(TabStripNudgeButton* button); - void SetLockedExpansionModeForTesting(LockedExpansionMode mode, - TabStripNudgeButton* button); - - void OnAutoTabGroupButtonClicked(); - void OnAutoTabGroupButtonDismissed(); - - void OnOrganizeButtonTimeout(TabStripNudgeButton* button); - - // views::MouseWatcherListener: - void MouseMovedOutOfHost() override; - - // views::AnimationDelegateViews - void AnimationCanceled(const gfx::Animation* animation) override; - void AnimationEnded(const gfx::Animation* animation) override; - void AnimationProgressed(const gfx::Animation* animation) override; - - // TabOrganizationObserver - void OnToggleActionUIState(const Browser* browser, bool should_show) override; - - private: - void SetLockedExpansionMode(LockedExpansionMode mode, - TabStripNudgeButton* button); - void ExecuteShowTabOrganization(TabStripNudgeButton* button); - void ExecuteHideTabOrganization(TabStripNudgeButton* button); - - void OnAnimationSessionEnded(); - - std::unique_ptr<TabStripNudgeButton> CreateAutoTabGroupButton( - bool tab_search_before_chips); - void SetupButtonProperties(TabStripNudgeButton* button, - bool tab_search_before_chips); - - // View where, if the mouse is currently over its bounds, the expansion state - // will not change. Changes will be staged until after the mouse exits the - // bounds of this View. - raw_ptr<View, DanglingUntriaged> locked_expansion_view_; - - // The button currently holding the lock to be shown/hidden. - raw_ptr<TabStripNudgeButton> locked_expansion_button_ = nullptr; - raw_ptr<TabStripNudgeButton, DanglingUntriaged> auto_tab_group_button_ = - nullptr; - raw_ptr<TabSearchButton, DanglingUntriaged> tab_search_button_ = nullptr; - raw_ptr<TabOrganizationService, DanglingUntriaged> tab_organization_service_ = - nullptr; - - raw_ptr<BrowserWindowInterface> browser_window_interface_; - - // Timer for hiding tab_organization_button_ after show. - base::OneShotTimer hide_tab_organization_timer_; - - // When locked, the container is unable to change its expanded state. Changes - // will be staged until after this is unlocked. - LockedExpansionMode locked_expansion_mode_ = LockedExpansionMode::kNone; - - // MouseWatcher is used to lock and unlock the expansion state of this - // container. - std::unique_ptr<views::MouseWatcher> mouse_watcher_; - - base::ScopedObservation<TabOrganizationService, TabOrganizationObserver> - tab_organization_observation_{this}; - - // Prevents other features from showing tabstrip-modal UI. - std::unique_ptr<ScopedTabStripModalUI> scoped_tab_strip_modal_ui_; - - std::unique_ptr<TabOrganizationAnimationSession> animation_session_; -}; - -#endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_SEARCH_CONTAINER_H_
diff --git a/chrome/browser/ui/views/tabs/tab_search_container_browsertest.cc b/chrome/browser/ui/views/tabs/tab_search_container_browsertest.cc deleted file mode 100644 index 313417c6..0000000 --- a/chrome/browser/ui/views/tabs/tab_search_container_browsertest.cc +++ /dev/null
@@ -1,471 +0,0 @@ -// Copyright 2023 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/views/tabs/tab_search_container.h" - -#include "base/feature_list.h" -#include "base/test/metrics/histogram_tester.h" -#include "base/test/scoped_feature_list.h" -#include "chrome/browser/optimization_guide/browser_test_util.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/signin/identity_manager_factory.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/tabs/organization/tab_organization_service.h" -#include "chrome/browser/ui/tabs/organization/tab_organization_utils.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/ui/ui_features.h" -#include "chrome/browser/ui/views/frame/browser_view.h" -#include "chrome/browser/ui/views/interaction/browser_elements_views.h" -#include "chrome/browser/ui/views/tabs/tab_search_button.h" -#include "chrome/common/chrome_features.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/views/chrome_views_test_base.h" -#include "components/optimization_guide/core/model_execution/model_execution_features.h" -#include "components/optimization_guide/core/optimization_guide_features.h" -#include "components/signin/public/identity_manager/identity_test_utils.h" -#include "content/public/test/browser_test.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/ui_base_features.h" -#include "ui/events/test/event_generator.h" -#include "ui/gfx/animation/slide_animation.h" -#include "ui/views/test/views_test_utils.h" -#include "ui/views/view_utils.h" - -namespace { - -using ::testing::AssertionFailure; -using ::testing::AssertionResult; -using ::testing::AssertionSuccess; - -class TabSearchContainerBrowserTest : public InProcessBrowserTest { - public: - TabSearchContainerBrowserTest() { - feature_list_.InitWithFeatures( - /*enabled_features=*/{features::kTabOrganization}, - /*disabled_features=*/{features::kGlic}); - TabOrganizationUtils::GetInstance()->SetIgnoreOptGuideForTesting(true); - } - - TabStripModel* tab_strip_model() { return browser()->tab_strip_model(); } - - BrowserView* browser_view() { - return BrowserView::GetBrowserViewForBrowser(browser()); - } - - TabSearchContainer* tab_search_container() { - return BrowserElementsViews::From(browser())->GetViewAs<TabSearchContainer>( - kTabSearchContainerElementId); - } - - // Returns an assertion result that the expansion animation is closing. - AssertionResult ExpansionAnimationIsClosing() { - if (!tab_search_container()) { - return AssertionFailure() << "tab_search_container is null."; - } - if (!tab_search_container()->animation_session_for_testing()) { - return AssertionFailure() << "animation_session_for_testing is null."; - } - if (!tab_search_container() - ->animation_session_for_testing() - ->expansion_animation()) { - return AssertionFailure() << "expansion_animation is null."; - } - return tab_search_container() - ->animation_session_for_testing() - ->expansion_animation() - ->IsClosing() - ? AssertionSuccess() - : AssertionFailure() << "expansion_animation is not closing."; - } - - protected: - void ResetAnimation(int value) { - if (tab_search_container()->animation_session_for_testing()) { - tab_search_container() - ->animation_session_for_testing() - ->ResetOpacityAnimationForTesting(value); - } - if (tab_search_container()->animation_session_for_testing()) { - tab_search_container() - ->animation_session_for_testing() - ->ResetExpansionAnimationForTesting(value); - } - if (tab_search_container()->animation_session_for_testing()) { - tab_search_container() - ->animation_session_for_testing() - ->ResetFlatEdgeAnimationForTesting(value); - } - } - - private: - base::test::ScopedFeatureList feature_list_; -}; - -// TODO(crbug.com/413441658): Flaky on Windows 10 builds. -#if BUILDFLAG(IS_WIN) -#define MAYBE_TogglesActionUIState DISABLED_TogglesActionUIState -#else -#define MAYBE_TogglesActionUIState TogglesActionUIState -#endif -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - MAYBE_TogglesActionUIState) { - ASSERT_FALSE(tab_search_container()->animation_session_for_testing()); - - TabOrganizationService* service = - tab_search_container()->tab_organization_service_for_testing(); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kNone, nullptr); - tab_strip_model()->ForceShowingModalUIForTesting(false); - service->OnTriggerOccured(browser()); - - ASSERT_TRUE(tab_search_container() - ->animation_session_for_testing() - ->expansion_animation() - ->IsShowing()); -} - -// TODO(crbug.com/413441658): Flaky on Windows 10 builds. -#if BUILDFLAG(IS_WIN) -#define MAYBE_TogglesActionUIStateOnlyInCorrectBrowser \ - DISABLED_TogglesActionUIStateOnlyInCorrectBrowser -#else -#define MAYBE_TogglesActionUIStateOnlyInCorrectBrowser \ - TogglesActionUIStateOnlyInCorrectBrowser -#endif -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - MAYBE_TogglesActionUIStateOnlyInCorrectBrowser) { - Browser* const second_browser = CreateBrowser(browser()->profile()); - TabSearchContainer* const second_search_container = - BrowserElementsViews::From(second_browser) - ->GetViewAs<TabSearchContainer>(kTabSearchContainerElementId); - - ASSERT_FALSE(second_search_container->animation_session_for_testing()); - - TabOrganizationService* service = - tab_search_container()->tab_organization_service_for_testing(); - // Same profile -> same service. - ASSERT_EQ(service, - second_search_container->tab_organization_service_for_testing()); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kNone, nullptr); - tab_strip_model()->ForceShowingModalUIForTesting(false); - second_search_container->SetLockedExpansionModeForTesting( - LockedExpansionMode::kNone, nullptr); - second_browser->tab_strip_model()->ForceShowingModalUIForTesting(false); - service->OnTriggerOccured(browser()); - - EXPECT_TRUE(tab_search_container() - ->animation_session_for_testing() - ->expansion_animation() - ->IsShowing()); - EXPECT_FALSE(second_search_container->animation_session_for_testing()); -} - -// TODO(crbug.com/413441658): Flaky on Windows 10 builds. -#if BUILDFLAG(IS_WIN) -#define MAYBE_DoesntShowIfTabStripModalUIExists \ - DISABLED_DoesntShowIfTabStripModalUIExists -#else -#define MAYBE_DoesntShowIfTabStripModalUIExists \ - DoesntShowIfTabStripModalUIExists -#endif -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - MAYBE_DoesntShowIfTabStripModalUIExists) { - ASSERT_FALSE(tab_search_container()->animation_session_for_testing()); - - tab_strip_model()->ForceShowingModalUIForTesting(true); - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kNone, nullptr); - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - - EXPECT_FALSE(tab_search_container()->animation_session_for_testing()); - - tab_strip_model()->ForceShowingModalUIForTesting(false); - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - - EXPECT_TRUE(tab_search_container() - ->animation_session_for_testing() - ->expansion_animation() - ->IsShowing()); -} - -// TODO(crbug.com/413441658): Flaky on Windows 10 builds. -#if BUILDFLAG(IS_WIN) -#define MAYBE_BlocksTabStripModalUIWhileShown \ - DISABLED_BlocksTabStripModalUIWhileShown -#else -#define MAYBE_BlocksTabStripModalUIWhileShown BlocksTabStripModalUIWhileShown -#endif -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - MAYBE_BlocksTabStripModalUIWhileShown) { - ASSERT_TRUE(browser()->tab_strip_model()->CanShowModalUI()); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kNone, nullptr); - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - - EXPECT_FALSE(browser()->tab_strip_model()->CanShowModalUI()); - - ResetAnimation(1); - - tab_search_container()->GetWidget()->LayoutRootViewIfNecessary(); - - EXPECT_FALSE(browser()->tab_strip_model()->CanShowModalUI()); - - tab_search_container()->HideTabOrganization( - tab_search_container()->auto_tab_group_button()); - - EXPECT_FALSE(browser()->tab_strip_model()->CanShowModalUI()); - - ResetAnimation(0); - - tab_search_container()->GetWidget()->LayoutRootViewIfNecessary(); - - EXPECT_TRUE(browser()->tab_strip_model()->CanShowModalUI()); -} - -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, DelaysShow) { - ASSERT_FALSE(tab_search_container()->animation_session_for_testing()); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kWillShow, - tab_search_container()->auto_tab_group_button()); - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - - ASSERT_FALSE(tab_search_container()->animation_session_for_testing()); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kNone, nullptr); - - ASSERT_TRUE(tab_search_container() - ->animation_session_for_testing() - ->expansion_animation() - ->IsShowing()); -} - -// TODO(crbug.com/413441658): Flaky on Windows 10 builds. -#if BUILDFLAG(IS_WIN) -#define MAYBE_DelaysHide DISABLED_DelaysHide -#else -#define MAYBE_DelaysHide DelaysHide -#endif -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, MAYBE_DelaysHide) { - ASSERT_FALSE(tab_search_container()->animation_session_for_testing()); - - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - ResetAnimation(1); - tab_search_container()->GetWidget()->LayoutRootViewIfNecessary(); - - ASSERT_FALSE(tab_search_container()->animation_session_for_testing()); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kWillHide, - tab_search_container()->auto_tab_group_button()); - tab_search_container()->HideTabOrganization( - tab_search_container()->auto_tab_group_button()); - - ASSERT_FALSE(tab_search_container()->animation_session_for_testing()); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kNone, nullptr); - - EXPECT_TRUE(ExpansionAnimationIsClosing()); -} - -// TODO(crbug.com/413441658): Flaky on Windows 10 builds. -#if BUILDFLAG(IS_WIN) -#define MAYBE_ImmediatelyHidesWhenOrganizeButtonClicked \ - DISABLED_ImmediatelyHidesWhenOrganizeButtonClicked -#else -#define MAYBE_ImmediatelyHidesWhenOrganizeButtonClicked \ - ImmediatelyHidesWhenOrganizeButtonClicked -#endif -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - MAYBE_ImmediatelyHidesWhenOrganizeButtonClicked) { - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - ResetAnimation(1); - tab_search_container()->GetWidget()->LayoutRootViewIfNecessary(); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kWillHide, - tab_search_container()->auto_tab_group_button()); - - tab_search_container()->OnAutoTabGroupButtonClicked(); - - EXPECT_TRUE(ExpansionAnimationIsClosing()); -} - -// TODO(crbug.com/413441658): Flaky on Windows 10 builds. -#if BUILDFLAG(IS_WIN) -#define MAYBE_ImmediatelyHidesWhenOrganizeButtonDismissed \ - DISABLED_ImmediatelyHidesWhenOrganizeButtonDismissed -#else -#define MAYBE_ImmediatelyHidesWhenOrganizeButtonDismissed \ - ImmediatelyHidesWhenOrganizeButtonDismissed -#endif -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - MAYBE_ImmediatelyHidesWhenOrganizeButtonDismissed) { - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - ResetAnimation(1); - tab_search_container()->GetWidget()->LayoutRootViewIfNecessary(); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kWillHide, - tab_search_container()->auto_tab_group_button()); - - tab_search_container()->OnAutoTabGroupButtonDismissed(); - - EXPECT_TRUE(ExpansionAnimationIsClosing()); -} - -// TODO(crbug.com/414839512): Fix flaky test. -#if BUILDFLAG(IS_WIN) -#define MAYBE_DelayedHidesWhenOrganizeButtonTimesOut \ - DISABLED_DelayedHidesWhenOrganizeButtonTimesOut -#else -#define MAYBE_DelayedHidesWhenOrganizeButtonTimesOut \ - DelayedHidesWhenOrganizeButtonTimesOut -#endif -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - MAYBE_DelayedHidesWhenOrganizeButtonTimesOut) { - // RunScheduledLayout() is needed due to widget auto-resize. - views::test::RunScheduledLayout(tab_search_container()); - - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - ResetAnimation(1); - tab_search_container()->GetWidget()->LayoutRootViewIfNecessary(); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kWillHide, - tab_search_container()->auto_tab_group_button()); - - tab_search_container()->OnOrganizeButtonTimeout( - tab_search_container()->auto_tab_group_button()); - - EXPECT_FALSE(tab_search_container()->animation_session_for_testing()); - - tab_search_container()->SetLockedExpansionModeForTesting( - LockedExpansionMode::kNone, - tab_search_container()->auto_tab_group_button()); - - EXPECT_TRUE(ExpansionAnimationIsClosing()); -} - -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - LogsSuccessWhenAutoTabGroupsButtonClicked) { - base::HistogramTester histogram_tester; - - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - ResetAnimation(1); - tab_search_container()->GetWidget()->LayoutRootViewIfNecessary(); - - TabOrganizationService* service = - tab_search_container()->tab_organization_service_for_testing(); - - service->OnTriggerOccured(browser()); - - tab_search_container()->OnAutoTabGroupButtonClicked(); -} - -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - LogsFailureWhenAutoTabGroupsButtonDismissed) { - base::HistogramTester histogram_tester; - - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - ResetAnimation(1); - tab_search_container()->GetWidget()->LayoutRootViewIfNecessary(); - - TabOrganizationService* service = - tab_search_container()->tab_organization_service_for_testing(); - - service->OnTriggerOccured(browser()); - - tab_search_container()->OnAutoTabGroupButtonDismissed(); -} - -// TODO(crbug.com/413441658): Flaky on Windows 10 builds. -#if BUILDFLAG(IS_WIN) -#define MAYBE_LogsFailureWhenAutoTabGroupsButtonTimeout \ - DISABLED_LogsFailureWhenAutoTabGroupsButtonTimeout -#else -#define MAYBE_LogsFailureWhenAutoTabGroupsButtonTimeout \ - LogsFailureWhenAutoTabGroupsButtonTimeout -#endif -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - MAYBE_LogsFailureWhenAutoTabGroupsButtonTimeout) { - base::HistogramTester histogram_tester; - - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - ResetAnimation(1); - tab_search_container()->GetWidget()->LayoutRootViewIfNecessary(); - - TabOrganizationService* service = - tab_search_container()->tab_organization_service_for_testing(); - - service->OnTriggerOccured(browser()); - - tab_search_container()->OnOrganizeButtonTimeout( - tab_search_container()->auto_tab_group_button()); -} - -// TODO(crbug.com/413441658): Flaky on Windows 10 builds. -#if BUILDFLAG(IS_WIN) -#define MAYBE_HidesAutoTabGroupButtonFromHalfway \ - DISABLED_HidesAutoTabGroupButtonFromHalfway -#else -#define MAYBE_HidesAutoTabGroupButtonFromHalfway \ - HidesAutoTabGroupButtonFromHalfway -#endif -IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, - MAYBE_HidesAutoTabGroupButtonFromHalfway) { - ASSERT_FALSE(tab_search_container()->animation_session_for_testing()); - - tab_search_container()->ShowTabOrganization( - tab_search_container()->auto_tab_group_button()); - - ASSERT_TRUE(tab_search_container() - ->animation_session_for_testing() - ->expansion_animation() - ->IsShowing()); - - gfx::SlideAnimation* expansion_animation = - tab_search_container() - ->animation_session_for_testing() - ->expansion_animation(); - - gfx::AnimationTestApi animation_api(expansion_animation); - base::TimeTicks now = base::TimeTicks::Now(); - animation_api.SetStartTime(now); - animation_api.Step(now + (expansion_animation->GetSlideDuration() / 2)); - - double expanded_value = expansion_animation->GetCurrentValue(); - tab_search_container()->GetWidget()->LayoutRootViewIfNecessary(); - - tab_search_container()->HideTabOrganization( - tab_search_container()->auto_tab_group_button()); - - EXPECT_TRUE(ExpansionAnimationIsClosing()); - - EXPECT_EQ(tab_search_container() - ->animation_session_for_testing() - ->expansion_animation() - ->GetCurrentValue(), - expanded_value); -} - -} // namespace
diff --git a/chrome/browser/ui/views/tabs/tab_search_container_unittest.cc b/chrome/browser/ui/views/tabs/tab_search_container_unittest.cc deleted file mode 100644 index 69ee145..0000000 --- a/chrome/browser/ui/views/tabs/tab_search_container_unittest.cc +++ /dev/null
@@ -1,167 +0,0 @@ -// Copyright 2023 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/views/tabs/tab_search_container.h" - -#include <memory> - -#include "base/feature_list.h" -#include "base/test/scoped_feature_list.h" -#include "chrome/browser/ui/browser_window/public/browser_window_features.h" -#include "chrome/browser/ui/browser_window/public/browser_window_interface.h" -#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h" -#include "chrome/browser/ui/tabs/organization/tab_organization_service.h" -#include "chrome/browser/ui/tabs/organization/tab_organization_utils.h" -#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" -#include "chrome/browser/ui/ui_features.h" -#include "chrome/browser/ui/views/frame/browser_view.h" -#include "chrome/browser/ui/views/tabs/fake_tab_slot_controller.h" -#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h" -#include "chrome/browser/ui/views/tabs/tab_search_button.h" -#include "chrome/browser/ui/views/tabs/tab_strip_nudge_button.h" -#include "chrome/common/chrome_features.h" -#include "chrome/test/base/testing_profile.h" -#include "chrome/test/views/chrome_views_test_base.h" -#include "components/optimization_guide/core/model_execution/model_execution_features.h" -#include "components/optimization_guide/core/optimization_guide_features.h" -#include "fake_base_tab_strip_controller.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/ui_base_features.h" -#include "ui/base/unowned_user_data/user_data_factory.h" -#include "ui/gfx/animation/animation_test_api.h" - -using ::testing::NiceMock; - -class FakeBaseTabStripControllerWithBWI : public FakeBaseTabStripController { - public: - explicit FakeBaseTabStripControllerWithBWI( - BrowserWindowInterface* browser_window_interface) - : browser_window_interface_(browser_window_interface) {} - - BrowserWindowInterface* GetBrowserWindowInterface() override { - return browser_window_interface_; - } - - private: - raw_ptr<BrowserWindowInterface> browser_window_interface_; -}; - -class TabSearchContainerTest : public ChromeViewsTestBase { - public: - void SetUp() override { - ChromeViewsTestBase::SetUp(); - - TabOrganizationUtils::GetInstance()->SetIgnoreOptGuideForTesting(true); - scoped_feature_list_.InitAndEnableFeature(features::kTabOrganization); - - tab_strip_model_ = std::make_unique<TabStripModel>( - &tab_strip_model_delegate_, profile_.get()); - - browser_window_interface_ = std::make_unique<MockBrowserWindowInterface>(); - ON_CALL(*browser_window_interface_, GetTabStripModel()) - .WillByDefault(::testing::Return(tab_strip_model_.get())); - ON_CALL(*browser_window_interface_, GetProfile()) - .WillByDefault(::testing::Return(profile_.get())); - ON_CALL(*browser_window_interface_, GetFeatures()) - .WillByDefault(testing::ReturnRef(browser_window_features_)); - ON_CALL(testing::Const(*browser_window_interface_), GetFeatures()) - .WillByDefault(testing::ReturnRef(browser_window_features_)); - ON_CALL(*browser_window_interface_, GetUnownedUserDataHost()) - .WillByDefault(testing::ReturnRef(user_data_host_)); - - tab_strip_ = std::make_unique<TabStrip>( - std::make_unique<FakeBaseTabStripControllerWithBWI>( - browser_window_interface_.get()), - std::unique_ptr<NiceMock<TabHoverCardController>>()); - - locked_expansion_view_ = std::make_unique<views::View>(); - container_before_tab_strip_ = std::make_unique<TabSearchContainer>( - true, locked_expansion_view_.get(), tab_strip_.get()); - container_after_tab_strip_ = std::make_unique<TabSearchContainer>( - false, locked_expansion_view_.get(), tab_strip_.get()); - } - - protected: - void ResetAnimation(int value) { - if (container_before_tab_strip_->animation_session_for_testing()) { - container_before_tab_strip_->animation_session_for_testing() - ->ResetOpacityAnimationForTesting(value); - } - if (container_before_tab_strip_->animation_session_for_testing()) { - container_before_tab_strip_->animation_session_for_testing() - ->ResetExpansionAnimationForTesting(value); - } - if (container_before_tab_strip_->animation_session_for_testing()) { - container_before_tab_strip_->animation_session_for_testing() - ->ResetFlatEdgeAnimationForTesting(value); - } - } - - std::unique_ptr<TestingProfile> profile_ = std::make_unique<TestingProfile>(); - base::test::ScopedFeatureList scoped_feature_list_; - ui::UnownedUserDataHost user_data_host_; - std::unique_ptr<TabStripModel> tab_strip_model_; - TestTabStripModelDelegate tab_strip_model_delegate_; - std::unique_ptr<MockBrowserWindowInterface> browser_window_interface_; - std::unique_ptr<TabStrip> tab_strip_; - std::unique_ptr<views::View> locked_expansion_view_; - std::unique_ptr<TabSearchContainer> container_before_tab_strip_; - std::unique_ptr<TabSearchContainer> container_after_tab_strip_; - - BrowserWindowFeatures browser_window_features_; - - // Some of these tests rely on animation being enabled. This forces - // animation on even if it's turned off in the OS. - gfx::AnimationTestApi::RenderModeResetter animation_mode_reset_{ - gfx::AnimationTestApi::SetRichAnimationRenderMode( - gfx::Animation::RichAnimationRenderMode::FORCE_ENABLED)}; -}; - -TEST_F(TabSearchContainerTest, OrdersButtonsCorrectly) { - ASSERT_EQ(container_before_tab_strip_->tab_search_button(), - container_before_tab_strip_->children()[0]); - ASSERT_EQ(container_before_tab_strip_->auto_tab_group_button(), - container_before_tab_strip_->children()[1]); - - ASSERT_EQ(container_after_tab_strip_->auto_tab_group_button(), - container_after_tab_strip_->children()[0]); - ASSERT_EQ(container_after_tab_strip_->tab_search_button(), - container_after_tab_strip_->children()[1]); -} - -TEST_F(TabSearchContainerTest, ButtonsHaveFlatEdges) { - ASSERT_EQ( - Edge::kRight, - container_before_tab_strip_->tab_search_button()->animated_flat_edge()); - ASSERT_EQ(Edge::kLeft, container_before_tab_strip_->auto_tab_group_button() - ->animated_flat_edge()); - - ASSERT_EQ( - Edge::kLeft, - container_after_tab_strip_->tab_search_button()->animated_flat_edge()); - ASSERT_EQ(Edge::kRight, container_after_tab_strip_->auto_tab_group_button() - ->animated_flat_edge()); -} - -TEST_F(TabSearchContainerTest, AnimatesToExpanded) { - // Should be collapsed by default - ASSERT_EQ(nullptr, - container_before_tab_strip_->animation_session_for_testing()); - - ASSERT_EQ(0, container_before_tab_strip_->auto_tab_group_button() - ->width_factor_for_testing()); - - container_before_tab_strip_->ShowTabOrganization( - container_before_tab_strip_->auto_tab_group_button()); - - ASSERT_TRUE(container_before_tab_strip_->animation_session_for_testing() - ->expansion_animation() - ->IsShowing()); - - ResetAnimation(1); - - ASSERT_EQ(1, container_before_tab_strip_->auto_tab_group_button() - ->width_factor_for_testing()); -}
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container.h b/chrome/browser/ui/views/tabs/tab_strip_action_container.h index bc069d7..973ff11 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_action_container.h +++ b/chrome/browser/ui/views/tabs/tab_strip_action_container.h
@@ -12,7 +12,6 @@ #include "chrome/browser/ui/views/glic/glic_button_interface.h" #include "chrome/browser/ui/views/tabs/glic/tab_strip_glic_actor_task_icon.h" #include "chrome/browser/ui/views/tabs/glic/tab_strip_glic_button.h" -#include "chrome/browser/ui/views/tabs/tab_search_container.h" #include "chrome/common/buildflags.h" #include "ui/gfx/animation/animation.h" #include "ui/gfx/animation/slide_animation.h" @@ -31,6 +30,12 @@ class BrowserWindowInterface; class GlicAndActorButtonsContainer; +enum class LockedExpansionMode { + kNone = 0, + kWillShow, + kWillHide, +}; + class TabStripActionContainer : public views::View, public views::AnimationDelegateViews, public views::MouseWatcherListener,
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc index 30ab389..8212f62 100644 --- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc
@@ -512,29 +512,6 @@ WaitForHide(TabHoverCardBubbleView::kHoverCardBubbleElementId)); } -IN_PROC_BROWSER_TEST_F(VerticalTabStripControllerInteractiveUiTest, - ScrollingUnpinnedContainerClosesTabGroupEditorBubble) { - RunTestSequence( - WaitForShow(kVerticalTabStripBottomContainerElementId), Do([this]() { - browser()->tab_strip_model()->ExecuteContextMenuCommand( - browser()->tab_strip_model()->active_index(), - TabStripModel::ContextMenuCommand:: - CommandAddToNewGroupFromMenuItem); - }), - WaitForShow(kTabGroupHeaderElementId), - WaitForShow(kTabGroupEditorBubbleId), Do([this]() { - views::View* tab_strip_view = - BrowserView::GetBrowserViewForBrowser(browser()) - ->vertical_tab_strip_region_view_for_testing() - ->GetTabStripView(); - VerticalTabStripView* vertical_tab_strip_view = - views::AsViewClass<VerticalTabStripView>(tab_strip_view); - vertical_tab_strip_view->unpinned_tabs_scroll_view_for_testing() - ->ScrollByOffset({0, -100}); - }), - WaitForHide(kTabGroupEditorBubbleId)); -} - #if BUILDFLAG(IS_WIN) #define MAYBE_MousePressHidesHoverCard DISABLED_MousePressHidesHoverCard #else
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.cc b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.cc index 7d5932f6..762711a 100644 --- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.cc +++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/tabs/tab_group_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h" #include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h" #include "chrome/browser/ui/views/tabs/vertical/tab_collection_node.h" #include "chrome/browser/ui/views/tabs/vertical/vertical_pinned_tab_container_view.h" @@ -28,7 +27,6 @@ #include "ui/views/background.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/separator.h" -#include "ui/views/interaction/element_tracker_views.h" #include "ui/views/layout/delegating_layout_manager.h" #include "ui/views/layout/layout_types.h" #include "ui/views/layout/proposed_layout.h" @@ -377,8 +375,8 @@ scroll_view->SetVerticalScrollBar(std::make_unique<VerticalTabStripScrollBar>( collection_node_->GetController()->GetStateController())); callback_subscriptions_.emplace_back(scroll_view->AddContentsScrolledCallback( - base::BindRepeating(&VerticalTabStripView::OnContentsScrolled, - base::Unretained(this), scroll_view))); + base::BindRepeating(&VerticalTabStripView::HideHoverCardOnScroll, + base::Unretained(this)))); } void VerticalTabStripView::ResetCollectionNode() { @@ -437,30 +435,17 @@ return GetWidget() ? GetWidget()->ShouldPaintAsActive() : true; } -void VerticalTabStripView::OnContentsScrolled(views::ScrollView* scroll_view) { +void VerticalTabStripView::HideHoverCardOnScroll() { if (!collection_node_) { return; } - // Hide the hover card when the user scrolls the tab strip. if (TabHoverCardController* hover_card_controller = collection_node_->GetController()->GetHoverCardController(); hover_card_controller && hover_card_controller->IsHoverCardVisible()) { hover_card_controller->UpdateHoverCard( nullptr, TabSlotController::HoverCardUpdateType::kAnimating); } - - // If the scroll event was from the unpinned container, also close any group - // editor bubble. - if (scroll_view == unpinned_tabs_scroll_view_) { - auto* const bubble_view = - views::ElementTrackerViews::GetInstance()->GetFirstMatchingView( - TabGroupEditorBubbleView::kTabGroupEditorBubbleViewId, - views::ElementTrackerViews::GetContextForView(this)); - if (bubble_view) { - bubble_view->GetWidget()->Close(); - } - } } BEGIN_METADATA(VerticalTabStripView)
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.h b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.h index 779e292..c28f99e 100644 --- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.h +++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.h
@@ -77,7 +77,7 @@ bool IsFrameActive() const; - void OnContentsScrolled(views::ScrollView* scroll_view); + void HideHoverCardOnScroll(); raw_ptr<TabCollectionNode> collection_node_ = nullptr; raw_ptr<views::ScrollView> pinned_tabs_scroll_view_ = nullptr;
diff --git a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc index 3201f73e..ada5a79 100644 --- a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc +++ b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc
@@ -500,4 +500,32 @@ #endif // BUILDFLAG(IS_CHROMEOS) } +// Creates a view for the detailed install dialog that contains +// WebAppIconNameAndOriginView and ImageCarouselView. +std::unique_ptr<views::View> CreateDetailedInstallDialogView( + gfx::ImageSkia icon_image, + const std::u16string& title, + const GURL& start_url, + bool is_maskable, + base::WeakPtr<WebAppScreenshotFetcher> fetcher, + const std::u16string& description) { + auto detailed_view = std::make_unique<views::BoxLayoutView>(); + detailed_view->SetOrientation(views::BoxLayout::Orientation::kVertical); + detailed_view->SetBetweenChildSpacing( + views::LayoutProvider::Get()->GetDistanceMetric( + views::DISTANCE_RELATED_CONTROL_VERTICAL)); + + detailed_view->AddChildView(WebAppIconNameAndOriginView::Create( + icon_image, title, start_url, is_maskable)); + + auto* description_label = + detailed_view->AddChildView(std::make_unique<views::Label>(description)); + description_label->SetMultiLine(true); + description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + description_label->SetTextStyle(views::style::STYLE_SECONDARY); + + detailed_view->AddChildView(std::make_unique<ImageCarouselView>(fetcher)); + return detailed_view; +} + } // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc index 9464268c..c2e135a 100644 --- a/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc +++ b/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc
@@ -184,4 +184,19 @@ g_auto_accept_diy_dialog_for_testing = auto_accept; } +// Creates a view for the DIY install dialog that contains the +// input dialog. +std::unique_ptr<views::View> CreateDiyInstallDialogView( + gfx::ImageSkia icon_image, + const std::u16string& title, + const GURL& start_url, + content::WebContents* web_contents, + base::RepeatingCallback<void(const std::u16string&)> + on_textfield_changed_callback) { + return std::make_unique<SiteIconTextAndOriginView>( + icon_image, title, + l10n_util::GetStringUTF16(IDS_DIY_APP_AX_BUBBLE_NAME_LABEL), start_url, + web_contents, std::move(on_textfield_changed_callback)); +} + } // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc b/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc index 7c5a1866..cd8ded1 100644 --- a/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc +++ b/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc
@@ -7,13 +7,17 @@ #include <memory> #include "base/check_deref.h" +#include "base/feature_list.h" #include "base/functional/callback_helpers.h" +#include "base/logging.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics_action.h" #include "base/observer_list.h" #include "base/strings/string_util.h" #include "base/task/sequenced_task_runner.h" +#include "chrome/browser/feature_engagement/tracker_factory.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/actions/chrome_action_id.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/page_action/page_action_icon_type.h" @@ -34,6 +38,7 @@ #include "content/public/browser/page.h" #include "content/public/browser/web_contents.h" #include "ui/base/interaction/element_tracker.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/interaction/element_tracker_views.h" @@ -164,6 +169,11 @@ WebAppInstallDialogDelegate::~WebAppInstallDialogDelegate() = default; +bool WebAppInstallDialogDelegate::OnOkButtonClicked() { + OnAccept(); + return true; +} + void WebAppInstallDialogDelegate::OnAccept() { MeasureAcceptUserActionsForInstallDialog(); if (iph_state_ == PwaInProductHelpState::kShown) { @@ -258,8 +268,19 @@ void WebAppInstallDialogDelegate::OnTextFieldChangedMaybeUpdateButton( const std::u16string& text_field_contents) { text_field_contents_ = text_field_contents; - ui::DialogModel::Button* ok_button = - dialog_model()->GetButtonByUniqueId(kDiyAppsDialogOkButtonId); + if (!dialog_model() || !dialog_model()->host()) { + return; + } + + ui::DialogModel::Button* ok_button = nullptr; + if (dialog_model()->HasField(kDiyAppsDialogOkButtonId)) { + ok_button = dialog_model()->GetButtonByUniqueId(kDiyAppsDialogOkButtonId); + } else if (dialog_model()->HasField(kPwaInstallDialogInstallButton)) { + // Use the kPwaInstallDialogInstallButton id for for the Flow view. + ok_button = + dialog_model()->GetButtonByUniqueId(kPwaInstallDialogInstallButton); + } + CHECK(ok_button); dialog_model()->SetButtonEnabled(ok_button, /*enabled=*/!text_field_contents.empty());
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h b/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h index 0723c106c..a058e45 100644 --- a/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h +++ b/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h
@@ -88,10 +88,12 @@ ~WebAppInstallDialogDelegate() override; - void OnAccept(); + virtual void OnAccept(); void OnCancel(); void OnClose(); + virtual bool OnOkButtonClicked(); + // This is called when the dialog has been either accepted, cancelled, closed // or destroyed without an user-action. void OnDestroyed(); @@ -112,12 +114,14 @@ // WebAppModalDialogDelegate overrides: void CloseDialogAsIgnored() override; + protected: + std::unique_ptr<WebAppInstallInfo> install_info_; + private: void MeasureIphOnDialogClose(); void MeasureAcceptUserActionsForInstallDialog(); void MeasureCancelUserActionsForInstallDialog(); - std::unique_ptr<WebAppInstallInfo> install_info_; std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker_; AppInstallationAcceptanceCallback callback_; PwaInProductHelpState iph_state_; @@ -136,7 +140,6 @@ base::WeakPtrFactory<WebAppInstallDialogDelegate> weak_ptr_factory_{this}; }; - } // namespace web_app #endif // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INSTALL_DIALOG_DELEGATE_H_
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.cc b/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.cc new file mode 100644 index 0000000..5992205 --- /dev/null +++ b/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.cc
@@ -0,0 +1,81 @@ +// Copyright 2026 The Chromium Authors +// 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/web_apps/web_app_install_dialog_flow_view.h" + +#include <memory> + +#include "chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.h" +#include "ui/views/controls/label.h" +#include "ui/views/layout/box_layout.h" + +namespace web_app { + +// A generic view that represents the installation flow. +WebAppInstallFlowView::WebAppInstallFlowView(const gfx::ImageSkia& icon_image, + const std::u16string& app_name, + const GURL& start_url, + bool is_maskable) { + SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical)); + + // kInstallDialog + auto* install_dialog_view = AddChildView(WebAppIconNameAndOriginView::Create( + icon_image, app_name, start_url, is_maskable)); + install_step_to_view_[InstallDialogStep::kInstallDialog] = + install_dialog_view; + + // kInstallerOptions + auto* options = + AddChildView(std::make_unique<views::Label>(u"Installer Options View")); + options->SetVisible(false); + install_step_to_view_[InstallDialogStep::kInstallerOptions] = options; + + // kProgress + auto* progress = + AddChildView(std::make_unique<views::Label>(u"Progress View")); + progress->SetVisible(false); + install_step_to_view_[InstallDialogStep::kProgress] = progress; + + // kSuccessful launch app button + auto* successful = + AddChildView(std::make_unique<views::Label>(u"Successful View")); + successful->SetVisible(false); + install_step_to_view_[InstallDialogStep::kSuccessful] = successful; +} + +WebAppInstallFlowView::~WebAppInstallFlowView() = default; + +// Assigns a view to the provided InstallDialogStep in the +// WebAppInstallFlowView. +void WebAppInstallFlowView::SetStepView(InstallDialogStep step, + std::unique_ptr<views::View> view) { + auto pair = install_step_to_view_.find(step); + bool was_visible = true; + if (pair != install_step_to_view_.end()) { + views::View* old_view = pair->second; + was_visible = old_view->GetVisible(); + pair->second = nullptr; + RemoveChildViewT(old_view); + } + view->SetVisible(was_visible); + install_step_to_view_[step] = AddChildView(std::move(view)); +} + +// Ensures visibility is appropriately updated for each views. +void WebAppInstallFlowView::UpdateStepVisibility( + InstallDialogStep current_step) { + for (auto const& [step, view] : install_step_to_view_) { + if (view) { + view->SetVisible(step == current_step); + } + } + PreferredSizeChanged(); +} + +base::WeakPtr<WebAppInstallFlowView> WebAppInstallFlowView::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +} // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.h b/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.h new file mode 100644 index 0000000..b572bb3f --- /dev/null +++ b/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.h
@@ -0,0 +1,42 @@ +// Copyright 2026 The Chromium Authors +// 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_WEB_APPS_WEB_APP_INSTALL_DIALOG_FLOW_VIEW_H_ +#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INSTALL_DIALOG_FLOW_VIEW_H_ + +#include <map> + +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h" +#include "chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h" +#include "ui/views/view.h" + +namespace web_app { + +// A generic view that represents the flow of the web app installation. +// It contains different views for each step of the installation process. +class WebAppInstallFlowView : public views::View { + public: + WebAppInstallFlowView(const gfx::ImageSkia& icon_image, + const std::u16string& app_name, + const GURL& start_url, + bool is_maskable); + ~WebAppInstallFlowView() override; + + base::WeakPtr<WebAppInstallFlowView> GetWeakPtr(); + + void SetStepView(InstallDialogStep step, std::unique_ptr<views::View> view); + + void UpdateStepVisibility(InstallDialogStep current_step); + + private: + std::map<InstallDialogStep, raw_ptr<views::View>> install_step_to_view_; + + base::WeakPtrFactory<WebAppInstallFlowView> weak_ptr_factory_{this}; +}; + +} // namespace web_app + +#endif // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INSTALL_DIALOG_FLOW_VIEW_H_
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.cc b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.cc new file mode 100644 index 0000000..0ca4e14d5 --- /dev/null +++ b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.cc
@@ -0,0 +1,239 @@ +// Copyright 2026 The Chromium Authors +// 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/web_apps/web_app_install_flow_dialog_delegate.h" + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/functional/bind.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/feature_engagement/tracker_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/url_identity.h" +#include "chrome/browser/ui/views/chrome_layout_provider.h" +#include "chrome/browser/ui/views/controls/site_icon_text_and_origin_view.h" +#include "chrome/browser/ui/views/page_action/page_action_icon_view.h" +#include "chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.h" +#include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h" +#include "chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.h" +#include "chrome/browser/ui/web_applications/web_app_dialogs.h" +#include "chrome/browser/ui/web_applications/web_app_info_image_source.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_install_info.h" +#include "chrome/browser/web_applications/web_app_screenshot_fetcher.h" +#include "chrome/grit/generated_resources.h" +#include "components/constrained_window/constrained_window_views.h" +#include "components/prefs/pref_service.h" +#include "components/strings/grit/components_strings.h" +#include "components/url_formatter/elide_url.h" +#include "components/webapps/browser/installable/ml_install_operation_tracker.h" +#include "components/webapps/common/constants.h" +#include "content/public/browser/web_contents.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/dialog_model.h" +#include "ui/base/mojom/dialog_button.mojom.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/text_elider.h" +#include "ui/views/bubble/bubble_dialog_model_host.h" +#include "ui/views/controls/label.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/layout/box_layout_view.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" +#include "url/origin.h" + +namespace web_app { + +WebAppInstallFlowDialogDelegate::WebAppInstallFlowDialogDelegate( + content::WebContents* web_contents, + std::unique_ptr<WebAppInstallInfo> install_info, + std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker, + AppInstallationAcceptanceCallback callback, + PwaInProductHelpState iph_state, + PrefService* prefs, + feature_engagement::Tracker* tracker, + InstallDialogType dialog_type) + : WebAppInstallDialogDelegate(web_contents, + std::move(install_info), + std::move(install_tracker), + std::move(callback), + iph_state, + prefs, + tracker, + dialog_type) {} + +WebAppInstallFlowDialogDelegate::~WebAppInstallFlowDialogDelegate() = default; + +bool WebAppInstallFlowDialogDelegate::OnOkButtonClicked() { + if (current_step_ == InstallDialogStep::kSuccessful) { + OnAccept(); + return true; + } + + // Update install dialog step. + if (current_step_ == InstallDialogStep::kInstallDialog) { + current_step_ = InstallDialogStep::kInstallerOptions; + } else if (current_step_ == InstallDialogStep::kInstallerOptions) { + current_step_ = InstallDialogStep::kProgress; + } else if (current_step_ == InstallDialogStep::kProgress) { + current_step_ = InstallDialogStep::kSuccessful; + } + + if (flow_view_) { + flow_view_->UpdateStepVisibility(current_step_); + } + + // Last dialog to show the install button. + // TODO(crbug.com/380497638): Trigger the installation earlier in the flow. + if (current_step_ == InstallDialogStep::kSuccessful && dialog_model()) { + ui::DialogModel::Button* ok_button = + dialog_model()->GetButtonByUniqueId(kPwaInstallDialogInstallButton); + if (ok_button) { + dialog_model()->SetButtonLabel(ok_button, + l10n_util::GetStringUTF16(IDS_DONE)); + } + } + + return false; +} + +// Builds and shows an install dialog flow according to the install_type. +void WebAppInstallFlowDialogDelegate::Show( + content::WebContents* web_contents, + std::unique_ptr<WebAppInstallInfo> install_info, + std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker, + AppInstallationAcceptanceCallback callback, + PwaInProductHelpState iph_state, + base::WeakPtr<WebAppScreenshotFetcher> screenshot_fetcher, + bool show_initiating_origin, + InstallDialogType install_type) { + auto* browser_context = web_contents->GetBrowserContext(); + Profile* profile = Profile::FromBrowserContext(browser_context); + PrefService* prefs = profile->GetPrefs(); + feature_engagement::Tracker* tracker = + feature_engagement::TrackerFactory::GetForBrowserContext(browser_context); + + DialogImageInfo dialog_image_info = + install_info->GetIconBitmapsForSecureSurfaces(); + gfx::ImageSkia icon_image( + std::make_unique<WebAppInfoImageSource>( + kIconSize, std::move(dialog_image_info.bitmaps)), + gfx::Size(kIconSize, kIconSize)); + + std::u16string title = install_info->title.value(); + GURL start_url = install_info->start_url(); + + auto flow_view = std::make_unique<WebAppInstallFlowView>( + icon_image, title, start_url, dialog_image_info.is_maskable); + auto flow_view_weak_ptr = flow_view->GetWeakPtr(); + + auto install_info_description = install_info->description.value(); + + auto delegate = std::make_unique<WebAppInstallFlowDialogDelegate>( + web_contents, std::move(install_info), std::move(install_tracker), + std::move(callback), std::move(iph_state), prefs, tracker, install_type); + auto* delegate_ptr = delegate.get(); + auto delegate_weak_ptr = delegate_ptr->AsWeakPtr(); + + delegate_ptr->SetFlowView(flow_view_weak_ptr); + + views::View* focusable_view = nullptr; + std::unique_ptr<views::View> step_view; + + switch (install_type) { + case InstallDialogType::kDetailed: { + const std::u16string description = gfx::TruncateString( + install_info_description, webapps::kMaximumDescriptionLength, + gfx::CHARACTER_BREAK); + step_view = CreateDetailedInstallDialogView( + icon_image, title, start_url, dialog_image_info.is_maskable, + std::move(screenshot_fetcher), description); + break; + } + case InstallDialogType::kDiy: { + if (title.empty()) { + title = UrlIdentity::CreateFromUrl(profile, start_url, + {UrlIdentity::Type::kDefault}, {}) + .name; + } + step_view = CreateDiyInstallDialogView( + icon_image, title, start_url, web_contents, + base::BindRepeating( + &WebAppInstallDialogDelegate::OnTextFieldChangedMaybeUpdateButton, + delegate_weak_ptr)); + focusable_view = static_cast<SiteIconTextAndOriginView*>(step_view.get()) + ->title_field(); + break; + } + case InstallDialogType::kSimple: + step_view = CreateSimpleInstallDialogView(icon_image, title, start_url, + dialog_image_info.is_maskable); + break; + } + + flow_view->SetStepView(InstallDialogStep::kInstallDialog, + std::move(step_view)); + + auto dialog_model_builder = ui::DialogModel::Builder(std::move(delegate)); + dialog_model_builder.SetInternalName("WebAppInstallFlowDialog") + .SetTitle( + l10n_util::GetStringUTF16(install_type == InstallDialogType::kDiy + ? IDS_DIY_APP_INSTALL_DIALOG_TITLE + : IDS_INSTALL_PWA_DIALOG_TITLE)) + .AddOkButton( + base::BindRepeating( + [](base::WeakPtr<WebAppInstallFlowDialogDelegate> delegate) { + return delegate ? delegate->OnOkButtonClicked() : true; + }, + delegate_weak_ptr), + ui::DialogModel::Button::Params().SetLabel(u"Next").SetId( + WebAppInstallDialogDelegate::kPwaInstallDialogInstallButton)) + .AddCancelButton(base::BindOnce(&WebAppInstallDialogDelegate::OnCancel, + delegate_weak_ptr)) + .SetCloseActionCallback(base::BindOnce( + &WebAppInstallDialogDelegate::OnClose, delegate_weak_ptr)) + .SetDialogDestroyingCallback(base::BindOnce( + &WebAppInstallDialogDelegate::OnDestroyed, delegate_weak_ptr)) + .OverrideDefaultButton(ui::mojom::DialogButton::kCancel) + .AddCustomField( + std::make_unique<views::BubbleDialogModelHost::CustomView>( + std::move(flow_view), + views::BubbleDialogModelHost::FieldType::kControl, + focusable_view), + WebAppInstallDialogDelegate::kDiyAppsDialogInputTextId); + + if (install_type == InstallDialogType::kDiy) { + dialog_model_builder.SetSubtitle( + l10n_util::GetStringUTF16(IDS_DIY_APP_INSTALL_DIALOG_SUBTITLE)); + dialog_model_builder.SetInitiallyFocusedField( + WebAppInstallDialogDelegate::kDiyAppsDialogInputTextId); + } + + if (show_initiating_origin) { + url::Origin initiating_origin = + web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(); + std::u16string origin_url = url_formatter::FormatOriginForSecurityDisplay( + initiating_origin, url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS); + dialog_model_builder.SetSubtitle(l10n_util::GetStringFUTF16( + IDS_INSTALL_PWA_DIALOG_ORIGIN_LABEL, origin_url)); + } + + auto dialog = views::BubbleDialogModelHost::CreateModal( + dialog_model_builder.Build(), ui::mojom::ModalType::kChild); + + views::Widget* widget = constrained_window::ShowWebModalDialogViews( + dialog.release(), web_contents); + + if (IsWidgetCurrentSizeSmallerThanPreferredSize(widget)) { + delegate_weak_ptr->CloseDialogAsIgnored(); + return; + } + delegate_weak_ptr->OnWidgetShownStartTracking(widget); +} + +} // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h new file mode 100644 index 0000000..4efd268 --- /dev/null +++ b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h
@@ -0,0 +1,79 @@ +// Copyright 2026 The Chromium Authors +// 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_WEB_APPS_WEB_APP_INSTALL_FLOW_DIALOG_DELEGATE_H_ +#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INSTALL_FLOW_DIALOG_DELEGATE_H_ + +#include <memory> + +#include "base/memory/weak_ptr.h" +#include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h" +#include "chrome/browser/ui/web_applications/web_app_dialogs.h" + +namespace content { +class WebContents; +} + +namespace webapps { +class MlInstallOperationTracker; +} + +namespace web_app { + +class WebAppScreenshotFetcher; +class WebAppInstallFlowView; +struct WebAppInstallInfo; + +enum class InstallDialogStep { + kInstallDialog = 0, + kInstallerOptions = 1, + kProgress = 2, + kSuccessful = 3, +}; + +class WebAppInstallFlowDialogDelegate : public WebAppInstallDialogDelegate { + public: + WebAppInstallFlowDialogDelegate( + content::WebContents* web_contents, + std::unique_ptr<WebAppInstallInfo> install_info, + std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker, + AppInstallationAcceptanceCallback callback, + PwaInProductHelpState iph_state, + PrefService* prefs, + feature_engagement::Tracker* tracker, + InstallDialogType dialog_type); + + ~WebAppInstallFlowDialogDelegate() override; + + static void Show( + content::WebContents* web_contents, + std::unique_ptr<WebAppInstallInfo> install_info, + std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker, + AppInstallationAcceptanceCallback callback, + PwaInProductHelpState iph_state, + base::WeakPtr<WebAppScreenshotFetcher> screenshot_fetcher, + bool show_initiating_origin, + InstallDialogType dialog_type); + + void SetFlowView(base::WeakPtr<WebAppInstallFlowView> flow_view) { + flow_view_ = std::move(flow_view); + } + + bool OnOkButtonClicked() override; + + base::WeakPtr<WebAppInstallFlowDialogDelegate> AsWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } + + protected: + InstallDialogStep current_step_ = InstallDialogStep::kInstallDialog; + base::WeakPtr<WebAppInstallFlowView> flow_view_; + + private: + base::WeakPtrFactory<WebAppInstallFlowDialogDelegate> weak_ptr_factory_{this}; +}; + +} // namespace web_app + +#endif // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INSTALL_FLOW_DIALOG_DELEGATE_H_
diff --git a/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc index 85bb5ea..e071632 100644 --- a/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc +++ b/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc
@@ -179,4 +179,15 @@ return base::AutoReset<bool>(&g_dont_close_on_deactivate, true); } +// Creates a view for the simple install dialog that contains the +// WebAppIconNameAndOriginView +std::unique_ptr<views::View> CreateSimpleInstallDialogView( + gfx::ImageSkia icon_image, + const std::u16string& title, + const GURL& start_url, + bool is_maskable) { + return WebAppIconNameAndOriginView::Create(icon_image, title, start_url, + is_maskable); +} + } // namespace web_app
diff --git a/chrome/browser/ui/web_applications/BUILD.gn b/chrome/browser/ui/web_applications/BUILD.gn index 40f5d51..b7d720cd 100644 --- a/chrome/browser/ui/web_applications/BUILD.gn +++ b/chrome/browser/ui/web_applications/BUILD.gn
@@ -199,8 +199,9 @@ "//chrome/browser/chromeos/app_mode", "//chrome/browser/ui/ash/shelf", "//chrome/browser/ui/ash/system_web_apps", - "//chrome/browser/ui/webui/ash/settings/app_management", + "//chromeos/ash/components/browser_context_helper", "//chromeos/ash/components/nonclosable_app_ui", + "//chromeos/ash/experiences/settings_ui", "//chromeos/ash/experiences/system_web_apps/types", "//chromeos/components/kiosk", "//chromeos/ui/frame",
diff --git a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc index 298f557..f0a5e83b 100644 --- a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc +++ b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
@@ -20,6 +20,7 @@ #include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/user_education/browser_user_education_interface.h" +#include "chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h" #include "chrome/browser/ui/web_applications/pwa_install_page_action.h" #include "chrome/browser/ui/web_applications/web_app_dialogs.h" #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h" @@ -34,6 +35,7 @@ #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_screenshot_fetcher.h" #include "chrome/browser/web_applications/web_app_utils.h" +#include "chrome/common/chrome_features.h" #include "components/feature_engagement/public/feature_constants.h" #include "components/webapps/browser/banners/app_banner_manager.h" #include "components/webapps/browser/banners/web_app_banner_data.h" @@ -83,6 +85,21 @@ .SetAppId(app_id)); } #endif + if (base::FeatureList::IsEnabled(features::kWebAppInstallDialog)) { + InstallDialogType install_type = kSimple; + if (screenshot_fetcher) { + install_type = kDetailed; + } else if (web_app_info->is_diy_app) { + install_type = kDiy; + } + WebAppInstallFlowDialogDelegate::Show( + initiator_web_contents, std::move(web_app_info), + std::move(install_tracker), std::move(web_app_acceptance_callback), + iph_state, std::move(screenshot_fetcher), show_initiating_origin, + install_type); + return; + } + if (screenshot_fetcher) { ShowWebAppDetailedInstallDialog( initiator_web_contents, std::move(web_app_info),
diff --git a/chrome/browser/ui/web_applications/web_app_dialogs.h b/chrome/browser/ui/web_applications/web_app_dialogs.h index 43bec84..a1a90a9 100644 --- a/chrome/browser/ui/web_applications/web_app_dialogs.h +++ b/chrome/browser/ui/web_applications/web_app_dialogs.h
@@ -22,6 +22,7 @@ #include "components/webapps/common/web_app_id.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/interaction/element_identifier.h" +#include "ui/gfx/image/image_skia.h" #include "ui/gfx/native_ui_types.h" static_assert(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || @@ -45,6 +46,10 @@ enum class WebappUninstallSource; } // namespace webapps +namespace views { +class View; +} // namespace views + namespace web_app { enum class NotSupportedReason { @@ -265,6 +270,34 @@ NotSupportedReason reason, base::OnceClosure callback); +// Creates a simple install dialog view that contains +// WebAppIconNameAndOriginView. +std::unique_ptr<views::View> CreateSimpleInstallDialogView( + gfx::ImageSkia icon_image, + const std::u16string& title, + const GURL& start_url, + bool is_maskable); + +// Creates a detailed install dialog view that contains +// a carousel. +std::unique_ptr<views::View> CreateDetailedInstallDialogView( + gfx::ImageSkia icon_image, + const std::u16string& title, + const GURL& start_url, + bool is_maskable, + base::WeakPtr<WebAppScreenshotFetcher> fetcher, + const std::u16string& description); + +// Creates a view for the DIY install dialog that contains the +// input dialog. +std::unique_ptr<views::View> CreateDiyInstallDialogView( + gfx::ImageSkia icon_image, + const std::u16string& title, + const GURL& start_url, + content::WebContents* web_contents, + base::RepeatingCallback<void(const std::u16string&)> + on_textfield_changed_callback); + } // namespace web_app #endif // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_DIALOGS_H_
diff --git a/chrome/browser/ui/web_applications/web_app_ui_utils.cc b/chrome/browser/ui/web_applications/web_app_ui_utils.cc index 932034c7..61a0c75 100644 --- a/chrome/browser/ui/web_applications/web_app_ui_utils.cc +++ b/chrome/browser/ui/web_applications/web_app_ui_utils.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/web_applications/web_app_browser_controller.h" -#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h" #include "chrome/browser/web_applications/proto/web_app_install_state.pb.h" #include "chrome/browser/web_applications/web_app_filter.h" #include "chrome/browser/web_applications/web_app_provider.h" @@ -23,6 +22,13 @@ #include "chrome/grit/generated_resources.h" #include "components/webapps/common/web_app_id.h" +#if BUILDFLAG(IS_CHROMEOS) +#include "base/check_deref.h" +#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h" +#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" +#include "components/user_manager/user.h" +#endif + namespace web_app { namespace { @@ -75,9 +81,23 @@ } #if BUILDFLAG(IS_CHROMEOS) - chrome::ShowAppManagementPage( - Profile::FromBrowserContext(web_contents->GetBrowserContext()), *app_id, - ash::settings::AppManagementEntryPoint::kPageInfoView); + const user_manager::User* user = + ash::BrowserContextHelper::Get()->GetUserByBrowserContext( + web_contents->GetBrowserContext()); + // TODO: Remove the if stmt, and replace it by CHECK(). + // This method is called only by clicking the "Site setting" or "App setting" + // option from the Page Info bubble, which is shown from the browser's + // omnibox. Theoretically a shimless RMA profile may have an app. But shimless + // RMA screen is full-screen and has no omnibox. + if (!user) { + return false; + } + ash::SettingsAppManager::Get()->Open( + *user, + ash::SettingsAppManager::OpenParams{ + .sub_page = + ash::SettingsAppManager::CreateAppManagementPagePath(*app_id), + .entry_point = ash::SettingsAppManager::EntryPoint::kPageInfoView}); return true; #else chrome::ShowWebAppSettings(chrome::FindBrowserWithTab(web_contents), *app_id, @@ -92,9 +112,24 @@ return; } #if BUILDFLAG(IS_CHROMEOS) - chrome::ShowAppManagementPage( - profile.get(), parent_app_id, - ash::settings::AppManagementEntryPoint::kSubAppsInstallPrompt); + const user_manager::User* user = + ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile.get()); + // TODO: Remove the if stmt, and replace it by CHECK(). + // The function OpenAppSettingsForParentApp is bound as a callback to the + // "Manage" link in the Sub Apps Install dialog. This dialog is only triggered + // when a parent Web App tries to install its Sub App. + // The Web App can be enabled not only on user profiles but also on shimless + // RMA profiles, so this method may get a shimless RMA profile. + if (!user) { + return; + } + ash::SettingsAppManager::Get()->Open( + *user, + ash::SettingsAppManager::OpenParams{ + .sub_page = ash::SettingsAppManager::CreateAppManagementPagePath( + parent_app_id), + .entry_point = + ash::SettingsAppManager::EntryPoint::kSubAppsInstallPrompt}); #else chrome::ShowWebAppSettings(profile.get(), parent_app_id, AppSettingsPageEntryPoint::kSubAppsInstallPrompt); @@ -104,8 +139,21 @@ void OpenAppSettingsForInstalledRelatedApp(const webapps::AppId& app_id, Profile* profile) { #if BUILDFLAG(IS_CHROMEOS) - chrome::ShowAppManagementPage( - profile, app_id, ash::settings::AppManagementEntryPoint::kSiteDataDialog); + const user_manager::User* user = + ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile); + // TODO: Remove the if stmt, and replace it by CHECK(). + // This method is called only from PageSpecificSiteDataDialog, which is + // accessed by clicking the "Site Data" or "Cookies" option from the Page Info + // bubble, which is shown from the browser's omnibox. + if (!user) { + return; + } + ash::SettingsAppManager::Get()->Open( + *user, + ash::SettingsAppManager::OpenParams{ + .sub_page = + ash::SettingsAppManager::CreateAppManagementPagePath(app_id), + .entry_point = ash::SettingsAppManager::EntryPoint::kSiteDataDialog}); #else chrome::ShowWebAppSettings(profile, app_id, AppSettingsPageEntryPoint::kSiteDataDialog);
diff --git a/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_dialog.cc b/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_dialog.cc index 072cdcb..5594d8c 100644 --- a/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_dialog.cc +++ b/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_dialog.cc
@@ -10,7 +10,6 @@ #include "base/json/json_writer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_ui.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" #include "net/base/url_util.h" #include "ui/aura/window.h"
diff --git a/chrome/browser/ui/webui/ash/in_session_password_change/confirm_password_change_handler.cc b/chrome/browser/ui/webui/ash/in_session_password_change/confirm_password_change_handler.cc index 0536529c..a8baa56 100644 --- a/chrome/browser/ui/webui/ash/in_session_password_change/confirm_password_change_handler.cc +++ b/chrome/browser/ui/webui/ash/in_session_password_change/confirm_password_change_handler.cc
@@ -10,7 +10,6 @@ #include "base/values.h" #include "chrome/browser/ash/login/saml/in_session_password_change_manager.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/login/auth/public/saml_password_attributes.h" #include "components/prefs/pref_service.h" #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ui/webui/ash/in_session_password_change/urgent_password_expiry_notification_handler.cc b/chrome/browser/ui/webui/ash/in_session_password_change/urgent_password_expiry_notification_handler.cc index 8ca580c..be941d7 100644 --- a/chrome/browser/ui/webui/ash/in_session_password_change/urgent_password_expiry_notification_handler.cc +++ b/chrome/browser/ui/webui/ash/in_session_password_change/urgent_password_expiry_notification_handler.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/ash/login/saml/in_session_password_change_manager.h" #include "chrome/browser/ash/login/saml/password_expiry_notification.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/login/auth/public/saml_password_attributes.h" #include "components/prefs/pref_service.h" #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc index 54c3f852..c2b01f85 100644 --- a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc +++ b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.h" #include "chrome/browser/ui/webui/ash/login/oobe_ui.h" #include "chrome/browser/ui/webui/metrics_handler.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/browser_resources.h" #include "chrome/grit/gaia_action_buttons_resources.h" #include "chrome/grit/gaia_action_buttons_resources_map.h"
diff --git a/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc index 842f22a..a8cd669 100644 --- a/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc +++ b/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
@@ -82,7 +82,6 @@ #include "chrome/browser/ui/webui/signin/signin_utils.h" #include "chrome/common/channel_info.h" #include "chrome/common/chrome_features.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "chrome/installer/util/google_update_settings.h" #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.cc b/chrome/browser/ui/webui/ash/login/oobe_ui.cc index 94fc9739..0bb104e 100644 --- a/chrome/browser/ui/webui/ash/login/oobe_ui.cc +++ b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
@@ -141,7 +141,6 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/browser_resources.h" #include "chrome/grit/chrome_unscaled_resources.h" #include "chrome/grit/component_extension_resources.h"
diff --git a/chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.cc index 663b633..db2d0a5 100644 --- a/chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.cc +++ b/chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.cc
@@ -15,7 +15,6 @@ #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/ash/login/oobe_ui.h" -#include "chrome/common/pref_names.h" #include "chrome/grit/branded_strings.h" #include "chrome/grit/generated_resources.h" #include "components/login/localized_values_builder.h"
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/BUILD.gn b/chrome/browser/ui/webui/ash/settings/app_management/BUILD.gn deleted file mode 100644 index 21f9bfc9..0000000 --- a/chrome/browser/ui/webui/ash/settings/app_management/BUILD.gn +++ /dev/null
@@ -1,9 +0,0 @@ -# Copyright 2024 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -assert(is_chromeos) - -static_library("app_management") { - sources = [ "app_management_uma.h" ] -}
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/DEPS b/chrome/browser/ui/webui/ash/settings/app_management/DEPS deleted file mode 100644 index c4812ae..0000000 --- a/chrome/browser/ui/webui/ash/settings/app_management/DEPS +++ /dev/null
@@ -1,9 +0,0 @@ -include_rules = [ - # ChromeOS should not depend on //chrome. See //docs/chromeos/code.md for - # details. - "-chrome", - - # Existing dependencies within //chrome. There is an active effort to - # refactor //chrome/browser/ui/ash to break these dependencies; see b/332804822. - # Whenever possible, avoid adding new //chrome dependencies to this list. -]
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA b/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA deleted file mode 100644 index bda997ea..0000000 --- a/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA +++ /dev/null
@@ -1,5 +0,0 @@ -team_email: "lt-web-apps-team@google.com" -os: CHROME_OS -buganizer: { - component_id: 1389907 -}
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/OWNERS b/chrome/browser/ui/webui/ash/settings/app_management/OWNERS deleted file mode 100644 index 75ba0e3..0000000 --- a/chrome/browser/ui/webui/ash/settings/app_management/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -file://ui/webui/resources/cr_components/app_management/OWNERS
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h b/chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h deleted file mode 100644 index 1a076c0e..0000000 --- a/chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h +++ /dev/null
@@ -1,38 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_ -#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_ - -namespace ash::settings { - -// These are used in histograms, do not remove/renumber entries. If you're -// adding to this enum with the intention that it will be logged, update the -// AppManagementEntryPoint enum listing in -// tools/metrics/histograms/metadata/apps/enums.xml. -enum class AppManagementEntryPoint { - kAppListContextMenuAppInfoArc = 0, - kAppListContextMenuAppInfoChromeApp = 1, - kAppListContextMenuAppInfoWebApp = 2, - kShelfContextMenuAppInfoArc = 3, - kShelfContextMenuAppInfoChromeApp = 4, - kShelfContextMenuAppInfoWebApp = 5, - kAppManagementMainViewArc = 6, - kAppManagementMainViewChromeApp = 7, - kAppManagementMainViewWebApp = 8, - kOsSettingsMainPage = 9, - kAppManagementMainViewPluginVm = 10, - kDBusServicePluginVm = 11, - kNotificationPluginVm = 12, - kAppManagementMainViewBorealis = 13, - kPageInfoView = 14, - kPrivacyIndicatorsNotificationSettings = 15, - kSubAppsInstallPrompt = 16, - kSiteDataDialog = 17, - kMaxValue = kSiteDataDialog, -}; - -} // namespace ash::settings - -#endif // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_
diff --git a/chrome/browser/ui/webui/ash/settings/pages/search/search_section.cc b/chrome/browser/ui/webui/ash/settings/pages/search/search_section.cc index 79677fc2..639646a 100644 --- a/chrome/browser/ui/webui/ash/settings/pages/search/search_section.cc +++ b/chrome/browser/ui/webui/ash/settings/pages/search/search_section.cc
@@ -233,9 +233,6 @@ html_source->AddLocalizedStrings(kLocalizedStrings); html_source->AddBoolean( - "quickAnswersTranslationDisabled", - chromeos::features::IsQuickAnswersV2TranslationDisabled()); - html_source->AddBoolean( "quickAnswersSubToggleEnabled", chromeos::features::IsQuickAnswersV2SettingsSubToggleEnabled()); }
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc index ae7085d8..1ee9263 100644 --- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc +++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc
@@ -117,7 +117,8 @@ pending_searchbox_handler, Profile* profile, content::WebContents* web_contents, - GetSessionHandleCallback get_session_callback) + GetSessionHandleCallback get_session_callback, + ClearSessionHandleCallback clear_session_callback) : ComposeboxHandler( std::move(pending_handler), std::move(pending_page), @@ -128,7 +129,8 @@ std::make_unique<ComposeboxOmniboxClient>(profile, web_contents, this)), - std::move(get_session_callback)) {} + std::move(get_session_callback), + std::move(clear_session_callback)) {} ComposeboxHandler::ComposeboxHandler( mojo::PendingReceiver<composebox::mojom::PageHandler> pending_handler, @@ -138,13 +140,15 @@ Profile* profile, content::WebContents* web_contents, std::unique_ptr<OmniboxController> controller, - GetSessionHandleCallback get_session_callback) + GetSessionHandleCallback get_session_callback, + ClearSessionHandleCallback clear_session_callback) : ContextualSearchboxHandler(std::move(pending_searchbox_handler), profile, web_contents, std::move(controller), std::move(get_session_callback)), web_contents_(web_contents), + clear_session_callback_(std::move(clear_session_callback)), page_{std::move(pending_page)}, handler_(this, std::move(pending_handler)) { // Set the callback for getting suggest inputs from the session. @@ -163,6 +167,12 @@ // connected/disconnected from the DOM, so this is not needed. } +void ComposeboxHandler::ClearSessionHandle() { + if (clear_session_callback_) { + clear_session_callback_.Run(); + } +} + void ComposeboxHandler::HandleLensButtonClick() { // Ignore, intentionally unimplemented for NTP. } @@ -265,3 +275,25 @@ return SearchboxHandler::AutocompleteIconToResourceName(icon); } + +void ComposeboxHandler::OpenUrl(GURL url, + const WindowOpenDisposition disposition) { + ContextualSearchboxHandler::OpenUrl(url, disposition); + // To keep the current composebox in a valid state after passing along its + // session handle and input state model, clear both of these values. This + // way the state will reset on the next use of the composebox. Clear the + // session handle before initializing the input state model, so the model + // gets a fresh handle. + ResetInputStateModel(); + ClearSessionHandle(); + InitializeInputStateModel(); + // This is technically wrong, it'll start a new session for the composebox + // even before its been opened. This is needed to re-request the cluster info + // for the composebox. + // TODO(crbug.com/491871526): Re-request cluster info when needed, not on + // navigation. + auto* contextual_session_handle = GetContextualSessionHandle(); + if (contextual_session_handle) { + contextual_session_handle->NotifySessionStarted(); + } +}
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h index 9aeb692..ab0bbaaf 100644 --- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h +++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h
@@ -27,6 +27,8 @@ class ComposeboxHandler : public composebox::mojom::PageHandler, public ContextualSearchboxHandler { public: + using ClearSessionHandleCallback = base::RepeatingClosure; + explicit ComposeboxHandler( mojo::PendingReceiver<composebox::mojom::PageHandler> pending_handler, mojo::PendingRemote<composebox::mojom::Page> pending_page, @@ -34,7 +36,8 @@ pending_searchbox_handler, Profile* profile, content::WebContents* web_contents, - GetSessionHandleCallback get_session_callback); + GetSessionHandleCallback get_session_callback, + ClearSessionHandleCallback clear_session_callback); ~ComposeboxHandler() override; // composebox::mojom::PageHandler: @@ -73,11 +76,18 @@ WindowOpenDisposition disposition, omnibox::ChromeAimEntryPoint aim_entrypoint, std::map<std::string, std::string> additional_params); - // SearchboxHandler: std::string AutocompleteIconToResourceName( const gfx::VectorIcon& icon) const override; + virtual void ClearSessionHandle(); + + protected: + void OpenUrl(GURL url, const WindowOpenDisposition disposition) override; + + FRIEND_TEST_ALL_PREFIXES(ComposeboxHandlerTest, + OpenUrl_ResetsContextControllerObserver); + protected: ComposeboxHandler( mojo::PendingReceiver<composebox::mojom::PageHandler> pending_handler, @@ -87,11 +97,14 @@ Profile* profile, content::WebContents* web_contents, std::unique_ptr<OmniboxController> omnibox_controller, - GetSessionHandleCallback get_session_callback); + GetSessionHandleCallback get_session_callback, + ClearSessionHandleCallback clear_session_callback); private: raw_ptr<content::WebContents> web_contents_; + ClearSessionHandleCallback clear_session_callback_; + // These are located at the end of the list of member variables to ensure the // WebUI page is disconnected before other members are destroyed. mojo::Remote<composebox::mojom::Page> page_;
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc index 7a8b978b..a2fd0e9 100644 --- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc +++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc
@@ -14,13 +14,16 @@ #include "base/run_loop.h" #include "base/test/bind.h" #include "base/test/gmock_move_support.h" +#include "base/test/test_future.h" #include "base/time/time.h" #include "base/unguessable_token.h" #include "base/version_info/channel.h" #include "chrome/browser/contextual_search/contextual_search_service_factory.h" +#include "chrome/browser/contextual_search/contextual_search_web_contents_helper.h" #include "chrome/browser/ui/webui/searchbox/contextual_searchbox_test_utils.h" #include "chrome/browser/ui/webui/searchbox/searchbox_test_utils.h" #include "components/contextual_search/contextual_search_service.h" +#include "components/contextual_search/mock_contextual_search_context_controller.h" #include "components/omnibox/browser/searchbox.mojom.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" @@ -98,7 +101,8 @@ mojo::PendingReceiver<searchbox::mojom::PageHandler>(), profile(), web_contents(), base::BindLambdaForTesting([&]() { return contextual_session_handle_.get(); - })); + }), + base::DoNothing()); handler_->SetPage(mock_searchbox_page_.BindAndGetRemote()); } @@ -191,6 +195,30 @@ kComposeboxFileDeleted + file_type + file_status + ".NewTabPage", 1); } +// Verifies that TakeSessionHandle transfers ownership out of the helper. +TEST_F(ComposeboxHandlerTest, TakeSessionHandle_TransfersOwnership) { + auto mock_controller = std::make_unique<testing::NiceMock< + contextual_search::MockContextualSearchContextController>>(); + ON_CALL(*mock_controller, AsWeakPtr()) + .WillByDefault(testing::Return( + base::WeakPtr< + contextual_search::ContextualSearchContextController>())); + + auto* service = ContextualSearchServiceFactory::GetForProfile(profile()); + auto handle = service->CreateSessionForTesting(std::move(mock_controller), + /*metrics_recorder=*/nullptr); + + auto* helper = ContextualSearchWebContentsHelper::GetOrCreateForWebContents( + web_contents()); + helper->SetTaskSession(/*task_id=*/std::nullopt, std::move(handle), + /*input_state_model=*/nullptr); + EXPECT_NE(helper->session_handle(), nullptr); + + auto taken_handle = helper->TakeSessionHandle(); + EXPECT_NE(taken_handle, nullptr); + EXPECT_EQ(helper->session_handle(), nullptr); +} + TEST_F(ComposeboxHandlerTest, SubmitQueryWithToolMetric) { // Submit with no tools enabled. SubmitQueryAndWaitForNavigation();
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc index 0ecac0e..8ef2b8f 100644 --- a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc +++ b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc
@@ -843,6 +843,9 @@ search_url_request_info->additional_params = additional_params; search_url_request_info->aim_entry_point = aim_entry_point; + file_info_list = + contextual_session_handle->GetController()->GetFileInfoList(); + contextual_session_handle->CreateSearchUrl( std::move(search_url_request_info), base::BindOnce( @@ -853,9 +856,6 @@ } }, weak_ptr_factory_.GetWeakPtr(), disposition)); - - file_info_list = - contextual_session_handle->GetController()->GetFileInfoList(); } #if !BUILDFLAG(IS_ANDROID) @@ -1027,7 +1027,10 @@ target_web_contents->Focus(); } } else { - content::OpenURLParams params(url, content::Referrer(), disposition, + // TODO(crbug.com/473009258): Override the window disposition for the + // ntp composebox until we correctly clear composebox input. + content::OpenURLParams params(url, content::Referrer(), + WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_LINK, false); web_contents_->OpenURL(params, std::move(navigation_handle_callback)); }
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h index 78b5a315..3ae41a1f 100644 --- a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h +++ b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h
@@ -196,6 +196,8 @@ // SearchboxHandler: omnibox::InputState GetInputState() const override; + virtual void OpenUrl(GURL url, const WindowOpenDisposition disposition); + void ComputeAndOpenQueryUrl( const std::string& query_text, WindowOpenDisposition disposition, @@ -267,8 +269,6 @@ const base::UnguessableToken& context_token, std::unique_ptr<lens::ContextualInputData> page_content_data); - void OpenUrl(GURL url, const WindowOpenDisposition disposition); - void OnPreviewReceived(GetTabPreviewCallback callback, const SkBitmap& preview_bitmap);
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom index e71f78bd..3c5e9ec 100644 --- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom +++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom
@@ -66,6 +66,9 @@ // Activates a metrics funnel for the session. ActivateMetricsFunnel(string funnel_name); + + // Changes the visibility of the action chips feature. + SetActionChipsVisibility(bool is_visible); }; // Interface for the page-side handler receiving information from the browser.
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc index bd8cba4..bcb5daa5 100644 --- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc +++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc
@@ -175,6 +175,10 @@ } } +void ActionChipsHandler::SetActionChipsVisibility(bool is_visible) { + profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, is_visible); +} + void ActionChipsHandler::SendActionChipsToUi(base::TimeTicks start_time, std::vector<ActionChipPtr> chips) { if (!page_.is_bound()) {
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h index 9109f90..f220d80 100644 --- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h +++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h
@@ -46,6 +46,7 @@ void StartActionChipsRetrieval() override; void ActivateMetricsFunnel(const std::string& funnel_name) override; + void SetActionChipsVisibility(bool is_visible) override; void OnTabStripModelChanged( TabStripModel* tab_strip_model,
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler_unittest.cc index 93a07f0..0175a25e 100644 --- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler_unittest.cc +++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler_unittest.cc
@@ -82,6 +82,8 @@ return receiver_.BindNewPipeAndPassRemote(); } + void FlushForTesting() { receiver_.FlushForTesting(); } + MOCK_METHOD(void, OnActionChipsChanged, (std::vector<ActionChipPtr> action_chips), @@ -660,12 +662,24 @@ } TEST_F(ActionChipsHandlerTest, ActionChipVisbilityChanged) { - // Set visibility to false. - profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, false); + // Set visibility to false, and this causes no call to OnActionChipsChanged. EXPECT_CALL(page_, OnActionChipsChanged(_)).Times(0); + profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, false); + page_.FlushForTesting(); + testing::Mock::VerifyAndClearExpectations(&page_); // Ensure `OnActionChipsChanged` is called when visibility changes to true. - profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, true); EXPECT_CALL(page_, OnActionChipsChanged(_)).Times(1); + profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, true); + page_.FlushForTesting(); + testing::Mock::VerifyAndClearExpectations(&page_); +} + +TEST_F(ActionChipsHandlerTest, SetActionChipsVisibility) { + EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kNtpToolChipsVisible)); + handler().SetActionChipsVisibility(false); + EXPECT_FALSE(profile_->GetPrefs()->GetBoolean(prefs::kNtpToolChipsVisible)); + handler().SetActionChipsVisibility(true); + EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kNtpToolChipsVisible)); } } // namespace
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 0274d10a..8a5e6eec 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
@@ -303,6 +303,7 @@ {"title", IDS_NEW_TAB_TITLE}, {"undo", IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE}, {"controlledSettingPolicy", IDS_CONTROLLED_SETTING_POLICY}, + {"disableSuggestion", IDS_NTP_ACTION_CHIP_DISABLE_TEXT}, // Custom Links. {"addLinkTitle", IDS_NTP_CUSTOM_LINKS_ADD_SHORTCUT_TITLE}, @@ -1265,6 +1266,8 @@ std::move(pending_page_handler), std::move(pending_page), std::move(pending_searchbox_handler), profile_, web_contents(), base::BindRepeating(&NewTabPageUI::GetOrCreateContextualSessionHandle, + base::Unretained(this)), + base::BindRepeating(&NewTabPageUI::ClearContextualSessionHandle, base::Unretained(this))); // TODO(crbug.com/435288212): Move searchbox mojom to use factory pattern. @@ -1350,6 +1353,10 @@ return shared_session_handle_.get(); } +void NewTabPageUI::ClearContextualSessionHandle() { + shared_session_handle_.reset(); +} + void NewTabPageUI::DidStartNavigation( content::NavigationHandle* navigation_handle) { if (navigation_handle->IsInPrimaryMainFrame() &&
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h index 50f6dbf..e58e658 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -252,6 +252,8 @@ contextual_search::ContextualSearchSessionHandle* GetOrCreateContextualSessionHandle(); + void ClearContextualSessionHandle(); + private: // new_tab_page::mojom::PageHandlerFactory: void CreatePageHandler(
diff --git a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc index 1dffe7b..e596e62d 100644 --- a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc +++ b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
@@ -282,6 +282,8 @@ std::move(pending_searchbox_handler), profile_, web_ui()->GetWebContents(), base::BindRepeating(&OmniboxPopupUI::GetOrCreateContextualSessionHandle, + base::Unretained(this)), + base::BindRepeating(&OmniboxPopupUI::ClearContextualSessionHandle, base::Unretained(this))); // TODO(crbug.com/435288212): Move searchbox mojom to use factory pattern. @@ -298,7 +300,13 @@ omnibox::CreateQueryControllerConfigParams(), contextual_search::ContextualSearchSource::kOmnibox, lens::LensOverlayInvocationSource::kOmniboxContextualQuery); + shared_session_handle_->CheckSearchContentSharingSettings( + Profile::FromWebUI(web_ui())->GetPrefs()); } } return shared_session_handle_.get(); } + +void OmniboxPopupUI::ClearContextualSessionHandle() { + shared_session_handle_.reset(); +}
diff --git a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h index 1ab9395..c88ac15 100644 --- a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h +++ b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h
@@ -105,6 +105,8 @@ contextual_search::ContextualSearchSessionHandle* GetOrCreateContextualSessionHandle(); + void ClearContextualSessionHandle(); + private: raw_ptr<Profile> profile_;
diff --git a/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc b/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc index dc0db88..4a86b45 100644 --- a/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc +++ b/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc
@@ -380,7 +380,7 @@ model_broker_state.usage_tracker() .WasOnDeviceEligibleFeatureRecentlyUsed(feature); feature_adaptation_info->version = - model_broker_state.service_controller() + model_broker_state.base_model_controller() .GetFeatureMetadata(feature) .transform( &optimization_guide::OnDeviceModelAdaptationMetadata::version) @@ -391,7 +391,7 @@ if (debug_state.state_) { auto performance_hint = - model_broker_state.service_controller().GetPerformanceHint(); + model_broker_state.base_model_controller().GetPerformanceHint(); base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock()}, base::BindOnce(&GetBaseModelInfo, *debug_state.state_,
diff --git a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc index e804c532..26b2876 100644 --- a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc +++ b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc
@@ -83,7 +83,8 @@ pending_searchbox_handler, Profile* profile, content::WebContents* web_contents, - GetSessionHandleCallback get_session_callback) + GetSessionHandleCallback get_session_callback, + ClearSessionHandleCallback clear_session_callback) : ComposeboxHandler( std::move(pending_handler), std::move(pending_page), @@ -94,7 +95,8 @@ std::make_unique<OmniboxPopupComposeboxClient>(profile, web_contents, this)), - std::move(get_session_callback)) { + std::move(get_session_callback), + std::move(clear_session_callback)) { auto* aim_eligibility_service = AimEligibilityServiceFactory::GetForProfile(profile); if (aim_eligibility_service) {
diff --git a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h index 9ed0a37..57a1327 100644 --- a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h +++ b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h
@@ -24,7 +24,8 @@ pending_searchbox_handler, Profile* profile, content::WebContents* web_contents, - GetSessionHandleCallback get_session_callback); + GetSessionHandleCallback get_session_callback, + ClearSessionHandleCallback clear_session_callback); ~OmniboxComposeboxHandler() override;
diff --git a/chrome/browser/ui/webui/searchbox/webui_omnibox_interactive_uitest.cc b/chrome/browser/ui/webui/searchbox/webui_omnibox_interactive_uitest.cc index 912eca2e..e6df60bd 100644 --- a/chrome/browser/ui/webui/searchbox/webui_omnibox_interactive_uitest.cc +++ b/chrome/browser/ui/webui/searchbox/webui_omnibox_interactive_uitest.cc
@@ -320,7 +320,8 @@ OmniboxAimWebUiInteractiveTest() { feature_list_.InitWithFeaturesAndParameters( GetEnabledFeatures(/*enable_aim_popup=*/true), - {omnibox::kAimServerEligibilityEnabled}); + {omnibox::kAimServerEligibilityEnabled, + omnibox::kAimFuseboxEligibilityCheckEnabled}); } std::unique_ptr<content::ScopedAccessibilityMode> scoped_accessibility_mode_; @@ -416,7 +417,8 @@ feature_list_.InitWithFeaturesAndParameters( GetEnabledFeatures(/*enable_aim_popup=*/true, /*auto_submit_voice=*/false), - {omnibox::kAimServerEligibilityEnabled}); + {omnibox::kAimServerEligibilityEnabled, + omnibox::kAimFuseboxEligibilityCheckEnabled}); } private: @@ -488,7 +490,8 @@ feature_list_.InitWithFeaturesAndParameters( GetEnabledFeatures(/*enable_aim_popup=*/true, /*auto_submit_voice=*/GetParam().auto_submit_voice), - {omnibox::kAimServerEligibilityEnabled}); + {omnibox::kAimServerEligibilityEnabled, + omnibox::kAimFuseboxEligibilityCheckEnabled}); } private:
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 787f8222..0af39bb 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1773259189-78ef215e9b54dc470ad476466ceae3bdabc70dbd-2eb1f882764dba3081c5d2a17446c3ded6dfb084.profdata +chrome-mac-arm-main-1773273244-9b0106b221d2d53ce72b4c7a8ae54018f5266587-1dd3629908fff5c8506f0928ccc540eb6237b9c8.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index c58539d..6103a3b 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1773251990-42c5e1537efae1128a28f5ed086151420c63c198-5c1fe9b9d7403e32315a21e5f946ac3452b4506d.profdata +chrome-win32-main-1773262601-892339f4a5a45b58626cea48b18afcb1dac3f25c-4ca3578a044af4063ef242decf4c9e9d07bfd2a3.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 8d176666..0399e76 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1773251990-bc327efd82c920287c6d17dfd23b719e10a149ce-5c1fe9b9d7403e32315a21e5f946ac3452b4506d.profdata +chrome-win64-main-1773262601-d35286c8e32846462156f3a266595984d63675f3-4ca3578a044af4063ef242decf4c9e9d07bfd2a3.profdata
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 9bb8cc1..2560524 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h
@@ -940,16 +940,6 @@ // system volume, and higher than 1.0 is louder. inline constexpr char kTextToSpeechVolume[] = "settings.tts.speech_volume"; -// A dictionary containing the latest Time Limits override authorized by parent -// access code. -inline constexpr char kTimeLimitLocalOverride[] = "screen_time.local_override"; - -// A dictionary preference holding the usage time limit definitions for a user. -inline constexpr char kUsageTimeLimit[] = "screen_time.limit"; - -// Last state of the screen time limit. -inline constexpr char kScreenTimeLastState[] = "screen_time.last_state"; - // Boolean pref indicating whether the message displayed on the login screen for // the managed guest session should be the full warning or not. // True means the full warning should be displayed. @@ -982,9 +972,6 @@ inline constexpr char kNetBiosShareDiscoveryEnabled[] = "network_file_shares.netbios_discovery.enabled"; -// Amount of screen time that a child user has used in the current day. -inline constexpr char kChildScreenTimeMilliseconds[] = "child_screen_time"; - // Last time the kChildScreenTimeMilliseconds was saved. inline constexpr char kLastChildScreenTimeSaved[] = "last_child_screen_time_saved"; @@ -1008,52 +995,6 @@ inline constexpr char kNTLMShareAuthenticationEnabled[] = "network_file_shares.ntlm_share_authentication.enabled"; -// Dictionary pref containing configuration used to verify Parent Access Code. -// Controlled by ParentAccessCodeConfig policy. -inline constexpr char kParentAccessCodeConfig[] = - "child_user.parent_access_code.config"; - -// List pref containing app activity and state for each application. -inline constexpr char kPerAppTimeLimitsAppActivities[] = - "child_user.per_app_time_limits.app_activities"; - -// Int64 to specify the last timestamp the AppActivityRegistry was reset. -inline constexpr char kPerAppTimeLimitsLastResetTime[] = - "child_user.per_app_time_limits.last_reset_time"; - -// Int64 to specify the last timestamp the app activity has been successfully -// reported. -inline constexpr char kPerAppTimeLimitsLastSuccessfulReportTime[] = - "child_user.per_app_time_limits.last_successful_report_time"; - -// Int64 to specify the latest AppLimit update timestamp from. -inline constexpr char kPerAppTimeLimitsLatestLimitUpdateTime[] = - "child_user.per_app_time_limits.latest_limit_update_time"; - -// Dictionary pref containing the per-app time limits configuration for -// child user. Controlled by PerAppTimeLimits policy. -inline constexpr char kPerAppTimeLimitsPolicy[] = - "child_user.per_app_time_limits.policy"; - -// Dictionary pref containing the allowed urls, schemes and applications -// that would not be blocked by per app time limits. -inline constexpr char kPerAppTimeLimitsAllowlistPolicy[] = - "child_user.per_app_time_limits.allowlist"; - -// Integer pref to record the day id (number of days since origin of time) when -// family user metrics were last recorded. -inline constexpr char kFamilyUserMetricsDayId[] = "family_user.metrics.day_id"; - -// TimeDelta pref to record the accumulated user session duration for family -// user metrics. -inline constexpr char kFamilyUserMetricsSessionEngagementDuration[] = - "family_user.metrics.session_engagement_duration"; - -// TimeDelta pref to record the accumulated Chrome browser app usage for family -// user metrics. -inline constexpr char kFamilyUserMetricsChromeBrowserEngagementDuration[] = - "family_user.metrics.chrome_browser_engagement_duration"; - // List of preconfigured network file shares. inline constexpr char kNetworkFileSharesPreconfiguredShares[] = "network_file_shares.preconfigured_shares"; @@ -2964,11 +2905,6 @@ inline constexpr char kRemoveUsersRemoteCommand[] = "remove_users_remote_command"; -// Dictionary pref containing the configuration used to verify Parent Access -// Code. The data is sent through the ParentAccessCodeConfig policy, which is -// set for child users only, and kept on the known user storage. -inline constexpr char kKnownUserParentAccessCodeConfig[] = - "child_user.parent_access_code.config"; #endif // BUILDFLAG(IS_CHROMEOS) // String which specifies where to store the disk cache.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index dbbc58d..edceb57 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2583,6 +2583,7 @@ "//chrome/browser/content_settings:content_settings_util", "//chrome/browser/custom_handlers", "//chrome/browser/data_saver", + "//chrome/browser/data_sharing:browser_tests", "//chrome/browser/desktop_to_mobile_promos:utils", "//chrome/browser/device_api:browser_tests", "//chrome/browser/devtools", @@ -2621,6 +2622,7 @@ "//chrome/browser/image_fetcher", "//chrome/browser/importer", "//chrome/browser/importer:browser_tests", + "//chrome/browser/indigo/onboarding:browser_tests", "//chrome/browser/language_detection:browser_tests", "//chrome/browser/launch_time_navigation_signal:browser_tests", "//chrome/browser/lifetime:termination_notification", @@ -3501,8 +3503,6 @@ "../browser/custom_handlers/protocol_handler_registry_browsertest.cc", "../browser/data_saver/data_saver_browsertest.cc", "../browser/data_saver/data_saver_webapis_browsertest.cc", - "../browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop_browsertest.cc", - "../browser/data_sharing/desktop/data_sharing_service_browsertest.cc", "../browser/devtools/device/adb/adb_client_socket_browsertest.cc", "../browser/devtools/device/devtools_android_bridge_browsertest.cc", "../browser/devtools/device/port_forwarding_browsertest.cc", @@ -3929,7 +3929,6 @@ "../browser/ui/views/tabs/tab/alert_indicator_button_browsertest.cc", "../browser/ui/views/tabs/tab_hover_card_controller_browsertest.cc", "../browser/ui/views/tabs/tab_search_button_browsertest.cc", - "../browser/ui/views/tabs/tab_search_container_browsertest.cc", "../browser/ui/views/tabs/tab_strip_action_container_browsertest.cc", "../browser/ui/views/tabs/tab_strip_browsertest.cc", "../browser/ui/views/user_education/browser_help_bubble_browsertest.cc", @@ -6618,9 +6617,6 @@ "../browser/component_updater/privacy_sandbox_attestations_component_installer_unittest.cc", "../browser/component_updater/subresource_filter_component_installer_unittest.cc", "../browser/custom_handlers/chrome_protocol_handler_registry_unittest.cc", - "../browser/data_sharing/data_sharing_navigation_throttle_unittest.cc", - "../browser/data_sharing/data_sharing_service_factory_unittest.cc", - "../browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory_unittest.cc", "../browser/download/chrome_download_manager_delegate_unittest.cc", "../browser/download/deferred_client_wrapper_unittest.cc", "../browser/download/download_core_service_impl_unittest.cc", @@ -6903,7 +6899,6 @@ "../browser/devtools/device/usb/android_rsa_unittest.cc", "../browser/devtools/devtools_availability_checker_unittest.cc", "../browser/metrics/power/battery_discharge_reporter_unittest.cc", - "../browser/page_info/merchant_trust_service_delegate_unittest.cc", "../browser/page_load_metrics/observers/non_tab_webui_page_load_metrics_observer_unittest.cc", "../browser/performance_manager/metrics/metrics_provider_desktop_unittest.cc", "../browser/performance_manager/policies/transient_keep_alive_policy_unittest.cc", @@ -7109,6 +7104,7 @@ "//chrome/browser/custom_handlers", "//chrome/browser/data_saver", "//chrome/browser/data_sharing", + "//chrome/browser/data_sharing:unit_tests", "//chrome/browser/devtools", "//chrome/browser/diagnostics:unit_tests", "//chrome/browser/digital_credentials:unit_tests", @@ -8284,7 +8280,6 @@ "../browser/component_updater/soda_component_installer_unittest.cc", "../browser/component_updater/soda_language_pack_component_installer_unittest.cc", "../browser/component_updater/zxcvbn_data_component_installer_unittest.cc", - "../browser/data_sharing/desktop/data_sharing_conversion_utils_unittest.cc", "../browser/devtools/device/android_device_manager_unittest.cc", "../browser/devtools/protocol/cast_handler_unittest.cc", "../browser/devtools/serialize_host_descriptions_unittest.cc", @@ -8567,6 +8562,7 @@ "//chrome/browser/indigo:unit_tests", "//chrome/browser/lookalikes:unit_tests", "//chrome/browser/media:access_handler", + "//chrome/browser/page_info:unit_tests", "//chrome/browser/password_manager/password_change", "//chrome/browser/password_manager/password_change:unit_tests", "//chrome/browser/predictors:unit_tests", @@ -8661,6 +8657,7 @@ "//chrome/utility:unit_tests", "//components/autofill/core/browser", "//components/contextual_search:public", + "//components/contextual_search:test_support", "//components/contextual_search/internal:test_support", "//components/data_sharing:test_support", "//components/ntp_tiles:pref_names", @@ -10829,7 +10826,6 @@ "../browser/ui/views/tabs/tab_container_unittest.cc", "../browser/ui/views/tabs/tab_group_views_unittest.cc", "../browser/ui/views/tabs/tab_hover_card_bubble_view_unittest.cc", - "../browser/ui/views/tabs/tab_search_container_unittest.cc", "../browser/ui/views/tabs/tab_strip_control_button_unittest.cc", "../browser/ui/views/tabs/tab_strip_layout_unittest.cc", "../browser/ui/views/tabs/tab_strip_nudge_button_unittest.cc", @@ -11651,6 +11647,7 @@ "//chrome/browser/content_settings:content_settings_factory", "//chrome/browser/contextual_cueing", "//chrome/browser/contextual_tasks:interactive_ui_tests", + "//chrome/browser/data_sharing", "//chrome/browser/desktop_to_mobile_promos:utils", "//chrome/browser/devtools", "//chrome/browser/devtools:test_support", @@ -11866,7 +11863,6 @@ deps += [ "//chrome/browser:flags", "//chrome/browser/custom_handlers", - "//chrome/browser/data_sharing", "//chrome/browser/history", "//chrome/browser/privacy_sandbox:test_support", "//chrome/browser/ui/location_bar",
diff --git a/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json b/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json index 176335f..7167205b 100644 --- a/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json +++ b/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json
@@ -1,10 +1,9 @@ { "name": "chrome.extension", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for some chrome.extension API: cognito mode.", "background": { - "scripts": ["test.js"], - "persistent": true + "service_worker": "test.js" } }
diff --git a/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json b/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json index 176335f..7167205b 100644 --- a/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json +++ b/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json
@@ -1,10 +1,9 @@ { "name": "chrome.extension", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for some chrome.extension API: cognito mode.", "background": { - "scripts": ["test.js"], - "persistent": true + "service_worker": "test.js" } }
diff --git a/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json b/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json index 0d304812..c2f4801d 100644 --- a/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json +++ b/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json
@@ -1,10 +1,9 @@ { "name": "chrome.extension", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for some chrome.extension API: incognito mode", "background": { - "scripts": ["test.js"], - "persistent": true + "service_worker": "test.js" } }
diff --git a/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json b/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json index 0d304812..c2f4801d 100644 --- a/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json +++ b/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json
@@ -1,10 +1,9 @@ { "name": "chrome.extension", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for some chrome.extension API: incognito mode", "background": { - "scripts": ["test.js"], - "persistent": true + "service_worker": "test.js" } }
diff --git a/chrome/test/data/extensions/api_test/history/incognito/manifest.json b/chrome/test/data/extensions/api_test/history/incognito/manifest.json index e0065826a..63cd382 100644 --- a/chrome/test/data/extensions/api_test/history/incognito/manifest.json +++ b/chrome/test/data/extensions/api_test/history/incognito/manifest.json
@@ -1,12 +1,11 @@ { "name": "chrome.history", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for chrome.history API", "permissions": ["history"], "incognito": "split", "background": { - "scripts": ["incognito.js"], - "persistent": true + "service_worker": "incognito.js" } }
diff --git a/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json b/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json index 5a7217f..fa5c52a 100644 --- a/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json +++ b/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json
@@ -1,11 +1,10 @@ { "name": "chrome.history", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for chrome.history API", "permissions": ["history"], "background": { - "scripts": ["delete.js"], - "persistent": true + "service_worker": "delete.js" } }
diff --git a/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json b/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json index 64b163b..4a7caa0 100644 --- a/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json +++ b/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json
@@ -1,11 +1,10 @@ { "name": "chrome.history", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for chrome.history API", "permissions": ["history"], "background": { - "scripts": ["delete_prohibited.js"], - "persistent": true + "service_worker": "delete_prohibited.js" } }
diff --git a/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json b/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json index 98229a35..2b74a63 100644 --- a/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json +++ b/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json
@@ -1,11 +1,10 @@ { "name": "chrome.history", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for chrome.history API", "permissions": ["history"], "background": { - "scripts": ["get_visits.js"], - "persistent": true + "service_worker": "get_visits.js" } }
diff --git a/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json b/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json index bb05630..f57dc61 100644 --- a/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json +++ b/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json
@@ -1,11 +1,10 @@ { "name": "chrome.history", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for chrome.history API", "permissions": ["history"], "background": { - "scripts": ["misc_search.js"], - "persistent": true + "service_worker": "misc_search.js" } }
diff --git a/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json b/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json index b23e61c..83453e9 100644 --- a/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json +++ b/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json
@@ -1,11 +1,10 @@ { "name": "chrome.history", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for chrome.history API", "permissions": ["history"], "background": { - "scripts": ["search_after_add.js"], - "persistent": true + "service_worker": "search_after_add.js" } }
diff --git a/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json b/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json index 892da398..463eb1b 100644 --- a/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json +++ b/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json
@@ -1,11 +1,10 @@ { "name": "chrome.history", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for chrome.history API", "permissions": ["history"], "background": { - "scripts": ["timed_search.js"], - "persistent": true + "service_worker": "timed_search.js" } }
diff --git a/chrome/test/data/extensions/api_test/management/basics/manifest.json b/chrome/test/data/extensions/api_test/management/basics/manifest.json index 113f49f..65e64db 100644 --- a/chrome/test/data/extensions/api_test/management/basics/manifest.json +++ b/chrome/test/data/extensions/api_test/management/basics/manifest.json
@@ -1,10 +1,9 @@ { "name": "Extension Management API Test", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "background": { - "scripts": ["basics.js"], - "persistent": true + "service_worker": "basics.js" }, "permissions": ["management"] }
diff --git a/chrome/test/data/extensions/api_test/management/create_app_shortcut/manifest.json b/chrome/test/data/extensions/api_test/management/create_app_shortcut/manifest.json index 62e21c0..dee940b9 100644 --- a/chrome/test/data/extensions/api_test/management/create_app_shortcut/manifest.json +++ b/chrome/test/data/extensions/api_test/management/create_app_shortcut/manifest.json
@@ -1,10 +1,9 @@ { "name": "Extension Management API Test", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "background": { - "scripts": ["createAppShortcut.js"], - "persistent": true + "service_worker": "createAppShortcut.js" }, "permissions": ["management"] }
diff --git a/chrome/test/data/extensions/api_test/management/enabled_extension/manifest.json b/chrome/test/data/extensions/api_test/management/enabled_extension/manifest.json index b20c2d8d..142c148 100644 --- a/chrome/test/data/extensions/api_test/management/enabled_extension/manifest.json +++ b/chrome/test/data/extensions/api_test/management/enabled_extension/manifest.json
@@ -1,7 +1,7 @@ { "name": "enabled_extension", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "icons": { "128": "icon_128.png", "48": "icon_48.png",
diff --git a/chrome/test/data/extensions/api_test/management/generate_app_for_link/manifest.json b/chrome/test/data/extensions/api_test/management/generate_app_for_link/manifest.json index f626d46..fb4978c 100644 --- a/chrome/test/data/extensions/api_test/management/generate_app_for_link/manifest.json +++ b/chrome/test/data/extensions/api_test/management/generate_app_for_link/manifest.json
@@ -1,10 +1,9 @@ { "name": "Extension Management API Test", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "background": { - "scripts": ["generateAppForLink.js"], - "persistent": true + "service_worker": "generateAppForLink.js" }, "permissions": ["management"] }
diff --git a/chrome/test/data/extensions/api_test/management/launch_on_install/manifest.json b/chrome/test/data/extensions/api_test/management/launch_on_install/manifest.json index caa659a3..b5d3162 100644 --- a/chrome/test/data/extensions/api_test/management/launch_on_install/manifest.json +++ b/chrome/test/data/extensions/api_test/management/launch_on_install/manifest.json
@@ -1,10 +1,9 @@ { "name": "Launch App on Install (test extension)", "version": "1", - "manifest_version": 2, + "manifest_version": 3, "permissions": ["management"], "background": { - "scripts": ["background.js"], - "persistent": true + "service_worker": "background.js" } }
diff --git a/chrome/test/data/extensions/api_test/management/launch_type/manifest.json b/chrome/test/data/extensions/api_test/management/launch_type/manifest.json index 9837b1b..8648f77 100644 --- a/chrome/test/data/extensions/api_test/management/launch_type/manifest.json +++ b/chrome/test/data/extensions/api_test/management/launch_type/manifest.json
@@ -1,10 +1,9 @@ { "name": "Extension Management API Test", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "background": { - "scripts": ["launchType.js"], - "persistent": true + "service_worker": "launchType.js" }, "permissions": ["management"] }
diff --git a/chrome/test/data/extensions/api_test/management/management_policy/manifest.json b/chrome/test/data/extensions/api_test/management/management_policy/manifest.json index be9a795..462599eb 100644 --- a/chrome/test/data/extensions/api_test/management/management_policy/manifest.json +++ b/chrome/test/data/extensions/api_test/management/management_policy/manifest.json
@@ -1,10 +1,9 @@ { "name": "Enable/disable policy controlled extension test", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "background": { - "scripts": ["background.js"], - "persistent": true + "service_worker": "background.js" }, "permissions": ["management"] }
diff --git a/chrome/test/data/extensions/api_test/management/no_permission/manifest.json b/chrome/test/data/extensions/api_test/management/no_permission/manifest.json index 927b237..a1514fd 100644 --- a/chrome/test/data/extensions/api_test/management/no_permission/manifest.json +++ b/chrome/test/data/extensions/api_test/management/no_permission/manifest.json
@@ -1,9 +1,8 @@ { "name": "Test that permissionless management APIs work without permission", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "background": { - "scripts": ["test.js"], - "persistent": true + "service_worker": "test.js" } }
diff --git a/chrome/test/data/extensions/api_test/management/permissions/manifest.json b/chrome/test/data/extensions/api_test/management/permissions/manifest.json index b430c82..209532e 100644 --- a/chrome/test/data/extensions/api_test/management/permissions/manifest.json +++ b/chrome/test/data/extensions/api_test/management/permissions/manifest.json
@@ -1,6 +1,12 @@ { "name": "permissions", "version": "0.1", - "manifest_version": 2, - "permissions": ["unlimitedStorage", "notifications", "http://*/*"] + "manifest_version": 3, + "permissions": [ + "unlimitedStorage", + "notifications" + ], + "host_permissions": [ + "http://*/*" + ] }
diff --git a/chrome/test/data/extensions/api_test/management/uninstall/manifest.json b/chrome/test/data/extensions/api_test/management/uninstall/manifest.json index 25a81212..ed2d39d 100644 --- a/chrome/test/data/extensions/api_test/management/uninstall/manifest.json +++ b/chrome/test/data/extensions/api_test/management/uninstall/manifest.json
@@ -1,10 +1,9 @@ { "name": "Extension Management API Test", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "background": { - "scripts": ["uninstall.js"], - "persistent": true + "service_worker": "uninstall.js" }, "permissions": ["management"] }
diff --git a/chrome/test/data/extensions/api_test/metrics/manifest.json b/chrome/test/data/extensions/api_test/metrics/manifest.json index c844a9a..927f7d99 100644 --- a/chrome/test/data/extensions/api_test/metrics/manifest.json +++ b/chrome/test/data/extensions/api_test/metrics/manifest.json
@@ -2,11 +2,10 @@ "key": "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDJ5+2VmV02HP3jkMhxbp2yuyzf4K4JsffgCf8zsPljLVV2A+JXGj0TbvzvDP3RDCKC5qPMprQkoYlKVW2b6H0kQ8dNmZsjxMqEz/ZDx4Z6/VvbMaz8pP+dENs5Io5XlG5Op2nsJF+y+LqbX6qbff9D/s4fTWyqKillpJN+48qs0wIBIw==", "name": "chrome.metrics", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for chrome.metricsPrivate API", "background": { - "scripts": ["test.js"], - "persistent": true + "service_worker": "test.js" }, "permissions": ["metricsPrivate"] }
diff --git a/chrome/test/data/extensions/api_test/native_messaging/lazy/manifest.json b/chrome/test/data/extensions/api_test/native_messaging/lazy/manifest.json deleted file mode 100644 index 9e7f7415..0000000 --- a/chrome/test/data/extensions/api_test/native_messaging/lazy/manifest.json +++ /dev/null
@@ -1,15 +0,0 @@ -{ - // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik - "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB", - "version": "1.0.0.0", - "manifest_version": 2, - "name": "native messaging test", - "description": "Tests one time messaging functionality in event page or SW based extension.", - "background": { - "scripts": ["test.js"], - "persistent": false - }, - "permissions": [ - "nativeMessaging" - ] -}
diff --git a/chrome/test/data/extensions/api_test/native_messaging/manifest.json b/chrome/test/data/extensions/api_test/native_messaging/manifest.json index 2180db2..3fc3b70 100644 --- a/chrome/test/data/extensions/api_test/native_messaging/manifest.json +++ b/chrome/test/data/extensions/api_test/native_messaging/manifest.json
@@ -2,12 +2,11 @@ // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB", "version": "1.0.0.0", - "manifest_version": 2, + "manifest_version": 3, "name": "native messaging test", - "description": "Test the basic functionality of passing native messages from a persistent extension.", + "description": "Test the basic functionality of passing native messages from an extension.", "background": { - "scripts": ["lazy/test.js"], - "persistent": true + "service_worker": "test.js" }, "permissions": [ "nativeMessaging"
diff --git a/chrome/test/data/extensions/api_test/native_messaging/lazy/test.js b/chrome/test/data/extensions/api_test/native_messaging/test.js similarity index 100% rename from chrome/test/data/extensions/api_test/native_messaging/lazy/test.js rename to chrome/test/data/extensions/api_test/native_messaging/test.js
diff --git a/chrome/test/data/extensions/api_test/native_messaging_connect/lazy/manifest.json b/chrome/test/data/extensions/api_test/native_messaging_connect/lazy/manifest.json deleted file mode 100644 index ee65939..0000000 --- a/chrome/test/data/extensions/api_test/native_messaging_connect/lazy/manifest.json +++ /dev/null
@@ -1,15 +0,0 @@ -{ - // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik - "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB", - "version": "1.0.0.0", - "manifest_version": 2, - "name": "native messaging test", - "description": "Tests long lived native messaging functionality in event page or SW based extension.", - "background": { - "scripts": ["test.js"], - "persistent": false - }, - "permissions": [ - "nativeMessaging" - ] -}
diff --git a/chrome/test/data/extensions/api_test/native_messaging_connect/manifest.json b/chrome/test/data/extensions/api_test/native_messaging_connect/manifest.json index a3b4f38cf..36ade20 100644 --- a/chrome/test/data/extensions/api_test/native_messaging_connect/manifest.json +++ b/chrome/test/data/extensions/api_test/native_messaging_connect/manifest.json
@@ -2,12 +2,11 @@ // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB", "version": "1.0.0.0", - "manifest_version": 2, + "manifest_version": 3, "name": "native messaging test", - "description": "Tests long lived native messaging functionality in persistent extension", + "description": "Tests long lived native messaging functionality", "background": { - "scripts": ["lazy/test.js"], - "persistent": true + "service_worker": "test.js" }, "permissions": [ "nativeMessaging"
diff --git a/chrome/test/data/extensions/api_test/native_messaging_connect/lazy/test.js b/chrome/test/data/extensions/api_test/native_messaging_connect/test.js similarity index 100% rename from chrome/test/data/extensions/api_test/native_messaging_connect/lazy/test.js rename to chrome/test/data/extensions/api_test/native_messaging_connect/test.js
diff --git a/chrome/test/data/extensions/api_test/omnibox/manifest.json b/chrome/test/data/extensions/api_test/omnibox/manifest.json index 8bc5397..41fbe5ed 100644 --- a/chrome/test/data/extensions/api_test/omnibox/manifest.json +++ b/chrome/test/data/extensions/api_test/omnibox/manifest.json
@@ -1,10 +1,10 @@ { "name": "chrome.extension.omnibox", "version": "0.1", - "manifest_version": 2, + "manifest_version": 3, "description": "end-to-end browser test for chrome.omnibox API", "background": { - "scripts": ["test.js"] + "service_worker": "test.js" }, "incognito": "split", "omnibox": { "keyword": "kw" }
diff --git a/chrome/test/data/extensions/api_test/printer_provider/request_capability/manifest.json b/chrome/test/data/extensions/api_test/printer_provider/request_capability/manifest.json index 4107676..c56564b 100644 --- a/chrome/test/data/extensions/api_test/printer_provider/request_capability/manifest.json +++ b/chrome/test/data/extensions/api_test/printer_provider/request_capability/manifest.json
@@ -1,11 +1,10 @@ { "name": "Test printer provider", "description": "Test extension for chrome.printerProvider.onGetCapabilityRequested event", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "background": { - "scripts": ["test.js"], - "persistent": false + "service_worker": "test.js" }, "permissions": [ "printerProvider"
diff --git a/chrome/test/data/extensions/api_test/printer_provider/request_print/manifest.json b/chrome/test/data/extensions/api_test/printer_provider/request_print/manifest.json index 783b5ab..473810c 100644 --- a/chrome/test/data/extensions/api_test/printer_provider/request_print/manifest.json +++ b/chrome/test/data/extensions/api_test/printer_provider/request_print/manifest.json
@@ -1,11 +1,10 @@ { "name": "Test printer provider", "description": "Test extension for chrome.printerProvider.onPrintRequested event", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "background": { - "scripts": ["test.js"], - "persistent": false + "service_worker": "test.js" }, "permissions": [ "printerProvider"
diff --git a/chrome/test/data/extensions/api_test/printer_provider/request_printers/manifest.json b/chrome/test/data/extensions/api_test/printer_provider/request_printers/manifest.json index 293f4b5..1394c3d 100644 --- a/chrome/test/data/extensions/api_test/printer_provider/request_printers/manifest.json +++ b/chrome/test/data/extensions/api_test/printer_provider/request_printers/manifest.json
@@ -1,11 +1,10 @@ { "name": "Test printer provider", "description": "Test extension for chrome.printerProvider.onGetPrintersRequested event", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "background": { - "scripts": ["test.js"], - "persistent": false + "service_worker": "test.js" }, "permissions": [ "printerProvider"
diff --git a/chrome/test/data/extensions/api_test/printer_provider/request_printers_second/manifest.json b/chrome/test/data/extensions/api_test/printer_provider/request_printers_second/manifest.json index 293f4b5..1394c3d 100644 --- a/chrome/test/data/extensions/api_test/printer_provider/request_printers_second/manifest.json +++ b/chrome/test/data/extensions/api_test/printer_provider/request_printers_second/manifest.json
@@ -1,11 +1,10 @@ { "name": "Test printer provider", "description": "Test extension for chrome.printerProvider.onGetPrintersRequested event", - "manifest_version": 2, + "manifest_version": 3, "version": "0.1", "background": { - "scripts": ["test.js"], - "persistent": false + "service_worker": "test.js" }, "permissions": [ "printerProvider"
diff --git a/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts b/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts index ac5ea33..58cb0ba 100644 --- a/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts +++ b/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts
@@ -12,6 +12,7 @@ import {PageCallbackRouter as ComposeboxPageCallbackRouter, PageHandlerRemote as ComposeboxPageHandlerRemote} from 'chrome://resources/cr_components/composebox/composebox.mojom-webui.js'; import {ComposeboxProxyImpl} from 'chrome://resources/cr_components/composebox/composebox_proxy.js'; import {ContextUploadStatus, ToolMode as ComposeboxToolMode} from 'chrome://resources/cr_components/composebox/composebox_query.mojom-webui.js'; +import type {ComposeboxVoiceSearchElement} from 'chrome://resources/cr_components/composebox/composebox_voice_search.js'; import type {ComposeboxFileCarouselElement} from 'chrome://resources/cr_components/composebox/file_carousel.js'; import type {ComposeboxFileThumbnailElement} from 'chrome://resources/cr_components/composebox/file_thumbnail.js'; import {WindowProxy} from 'chrome://resources/cr_components/composebox/window_proxy.js'; @@ -23,10 +24,10 @@ import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js'; import {MockTimer} from 'chrome://webui-test/mock_timer.js'; import {TestMock} from 'chrome://webui-test/test_mock.js'; -import {microtasksFinished} from 'chrome://webui-test/test_util.js'; +import {$$, microtasksFinished} from 'chrome://webui-test/test_util.js'; import {TestContextualTasksBrowserProxy} from './test_contextual_tasks_browser_proxy.js'; -import {ADD_FILE_CONTEXT_FN, ADD_TAB_CONTEXT_FN, assertStyle, deleteLastFile, fixtureUrl, FAKE_TOKEN_STRING, installMock, mockInputState} from './test_utils.js'; +import {ADD_FILE_CONTEXT_FN, ADD_TAB_CONTEXT_FN, assertStyle, deleteLastFile, FAKE_TOKEN_STRING, fixtureUrl, installMock, mockInputState} from './test_utils.js'; async function dispatchDragAndDropEvent(dropZone: Element, files: File[]) { if (!dropZone) { @@ -201,6 +202,7 @@ removeListener() {}, removeEventListener() {}, })); + windowProxy.setResultFor('hasWebkitSpeechRecognition', true); window.webkitSpeechRecognition = MockSpeechRecognition as unknown as typeof SpeechRecognition; @@ -350,7 +352,8 @@ assertTrue(mockSpeechRecognition.voiceSearchInProgress); assertStyle(composeboxDiv, 'opacity', '0'); - assertStyle(composebox.$.voiceSearch, 'display', 'inline'); + assertStyle( + $$(composebox, 'cr-composebox-voice-search'), 'display', 'inline'); assertEquals(composebox.animationState, GlowAnimationState.LISTENING); assertEquals( 1, @@ -565,20 +568,18 @@ await microtasksFinished(); await composebox.updateComplete; - const voiceSearchElement = composebox.$.voiceSearch; - const errorContainer = - voiceSearchElement.shadowRoot.querySelector<HTMLElement>( - '#error-container'); - const inputElement = - voiceSearchElement.shadowRoot.querySelector<HTMLTextAreaElement>( - '#input'); + const voiceSearchElement = $$(composebox, 'cr-composebox-voice-search'); + assertTrue(!!voiceSearchElement); + const errorContainer = $$(voiceSearchElement, '#error-container'); + const inputElement = $$(voiceSearchElement, '#input'); assertTrue(!!errorContainer); assertFalse(errorContainer.hidden); assertFalse(errorContainer.hidden, 'Error container should not be hidden'); assertTrue(inputElement!.hidden); assertStyle(composeboxDiv, 'opacity', '0'); - assertStyle(composebox.$.voiceSearch, 'display', 'inline'); + assertStyle( + $$(composebox, 'cr-composebox-voice-search'), 'display', 'inline'); assertEquals(composebox.animationState, GlowAnimationState.LISTENING); mockSpeechRecognition.onend!(); @@ -597,7 +598,10 @@ const composeboxDiv = contextualTasksApp.$.composebox.$.composebox.$.composebox; const composebox = contextualTasksApp.$.composebox.$.composebox; - composebox.$.voiceSearch.start(); + const voiceSearch = $$<ComposeboxVoiceSearchElement>( + composebox, 'cr-composebox-voice-search'); + assertTrue(!!voiceSearch); + voiceSearch.start(); await microtasksFinished(); assertEquals( 0, @@ -611,10 +615,9 @@ await composebox.updateComplete; await microtasksFinished(); - const voiceSearchElement = composebox.$.voiceSearch; - const errorContainer = - voiceSearchElement.shadowRoot.querySelector<HTMLElement>( - '#error-container'); + const voiceSearchElement = $$(composebox, 'cr-composebox-voice-search'); + assertTrue(!!voiceSearchElement); + const errorContainer = $$(voiceSearchElement, '#error-container'); assertTrue(!!errorContainer); assertTrue(errorContainer.hidden); @@ -640,8 +643,10 @@ contextualTasksApp.$.composebox.$.composebox.$.composebox; const composebox = contextualTasksApp.$.composebox.$.composebox; const voiceSearchButton = getVoiceSearchButton(composebox); - const voiceSearchElement = composebox.$.voiceSearch; - voiceSearchButton!.click(); + const voiceSearchElement = $$<ComposeboxVoiceSearchElement>( + composebox, 'cr-composebox-voice-search'); + assertTrue(!!voiceSearchButton && !!voiceSearchElement); + voiceSearchButton.click(); await microtasksFinished(); const result = createResults(2); @@ -680,31 +685,27 @@ await microtasksFinished(); await composebox.updateComplete; - await contextEntrypoint.dispatchEvent( - new CustomEvent('tool-click', { - detail: {toolMode: ComposeboxToolMode.kDeepSearch}, - bubbles: true, - composed: true, - })); + await contextEntrypoint.dispatchEvent(new CustomEvent('tool-click', { + detail: {toolMode: ComposeboxToolMode.kDeepSearch}, + bubbles: true, + composed: true, + })); await microtasksFinished(); await composebox.updateComplete; assertEquals( - ComposeboxToolMode.kDeepSearch, - composebox.activeToolMode, + ComposeboxToolMode.kDeepSearch, composebox.activeToolMode, 'Active tool should be Deep Search after clicking tool'); - await contextEntrypoint.dispatchEvent( - new CustomEvent('tool-click', { - detail: {toolMode: ComposeboxToolMode.kDeepSearch}, - bubbles: true, - composed: true, - })); + await contextEntrypoint.dispatchEvent(new CustomEvent('tool-click', { + detail: {toolMode: ComposeboxToolMode.kDeepSearch}, + bubbles: true, + composed: true, + })); await microtasksFinished(); await composebox.updateComplete; assertEquals( - ComposeboxToolMode.kUnspecified, - composebox.activeToolMode, + ComposeboxToolMode.kUnspecified, composebox.activeToolMode, 'Active tool should be unspecified after clicking tool twice'); }); @@ -717,34 +718,31 @@ await microtasksFinished(); await composebox.updateComplete; - await contextEntrypoint.dispatchEvent( - new CustomEvent( - 'create-image-click', - { - bubbles: true, - composed: true, - }, - )); + await contextEntrypoint.dispatchEvent(new CustomEvent( + 'create-image-click', + { + bubbles: true, + composed: true, + }, + )); await microtasksFinished(); await composebox.updateComplete; assertEquals( ComposeboxToolMode.kImageGen, composebox.activeToolMode, 'Active tool should be nano after clicking tool'); - await contextEntrypoint.dispatchEvent( - new CustomEvent( - 'create-image-click', - { - bubbles: true, - composed: true, - }, - )); + await contextEntrypoint.dispatchEvent(new CustomEvent( + 'create-image-click', + { + bubbles: true, + composed: true, + }, + )); await microtasksFinished(); await composebox.updateComplete; assertEquals( - ComposeboxToolMode.kUnspecified, - composebox.activeToolMode, + ComposeboxToolMode.kUnspecified, composebox.activeToolMode, 'Active tool should be unspecified after clicking tool twice'); }); @@ -754,6 +752,10 @@ // context menu mode, we just call the underlying function that responds to // both `tool-click` and individual `deep-search-click` events. composebox.onToolClickForTesting(ComposeboxToolMode.kDeepSearch); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kDeepSearch, + }); await composebox.updateComplete; await microtasksFinished(); @@ -785,6 +787,11 @@ test('Image tool is not reset after submitting a query', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kImageGen); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kImageGen, + }); + await composebox.updateComplete; await microtasksFinished(); @@ -813,6 +820,11 @@ test('Canvas tool is not reset after submitting a query', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kCanvas); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kCanvas, + }); + await composebox.updateComplete; await microtasksFinished(); @@ -839,6 +851,10 @@ test('Deepsearch mode: cancel resets mode', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kDeepSearch); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kDeepSearch, + }); await composebox.updateComplete; await microtasksFinished(); @@ -848,6 +864,10 @@ assertTrue(!!deepSearchChip, 'Deep search chip should be present'); // Simulate cancel button click without having to fully render button. composebox.onCancelClick_(); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kUnspecified, + }); await composebox.updateComplete; await microtasksFinished(); @@ -858,6 +878,11 @@ test('Image mode: cancel resets mode', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kImageGen); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kImageGen, + }); + await composebox.updateComplete; await microtasksFinished(); @@ -866,6 +891,10 @@ assertTrue(!!imageChip, 'Nano banana chip should be present'); // Simulate cancel button click without having to fully render button. composebox.onCancelClick_(); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kUnspecified, + }); await composebox.updateComplete; await microtasksFinished(); @@ -876,6 +905,11 @@ test('canvas mode: cancel resets mode', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kCanvas); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kCanvas, + }); + await composebox.updateComplete; await microtasksFinished(); @@ -885,6 +919,10 @@ assertTrue(!!canvasChip, 'Canvas chip should be present'); // Simulate cancel button click without having to fully render button. composebox.onCancelClick_(); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kUnspecified, + }); await composebox.updateComplete; await microtasksFinished(); @@ -895,6 +933,11 @@ test('Deepsearch mode: esc resets mode', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kDeepSearch); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kDeepSearch, + }); + await composebox.updateComplete; await microtasksFinished(); @@ -902,6 +945,10 @@ assertTrue(!!deepSearchChip, 'Deep search chip should be present'); composebox.handleEscapeKeyLogic(); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kUnspecified, + }); await composebox.updateComplete; await microtasksFinished(); @@ -912,6 +959,11 @@ test('Image mode: esc resets mode', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kImageGen); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kImageGen, + }); + await composebox.updateComplete; await microtasksFinished(); @@ -919,7 +971,10 @@ assertTrue(!!imageChip, 'Nano banana chip should be present'); composebox.handleEscapeKeyLogic(); - + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kUnspecified, + }); await composebox.updateComplete; await microtasksFinished(); @@ -929,6 +984,10 @@ test('canvas mode: esc resets mode', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kCanvas); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kCanvas, + }); await composebox.updateComplete; await microtasksFinished(); @@ -936,6 +995,10 @@ assertTrue(!!canvasChip, 'Canvas chip should be present'); composebox.handleEscapeKeyLogic(); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kUnspecified, + }); await composebox.updateComplete; await microtasksFinished();
diff --git a/chrome/test/data/webui/contextual_tasks/composebox_test.ts b/chrome/test/data/webui/contextual_tasks/composebox_test.ts index 216b5406e..f469764 100644 --- a/chrome/test/data/webui/contextual_tasks/composebox_test.ts +++ b/chrome/test/data/webui/contextual_tasks/composebox_test.ts
@@ -9,7 +9,7 @@ import type {ComposeboxFile} from 'chrome://resources/cr_components/composebox/common.js'; import {PageCallbackRouter as ComposeboxPageCallbackRouter, PageHandlerRemote as ComposeboxPageHandlerRemote} from 'chrome://resources/cr_components/composebox/composebox.mojom-webui.js'; import {ComposeboxProxyImpl} from 'chrome://resources/cr_components/composebox/composebox_proxy.js'; -import {ContextUploadStatus} from 'chrome://resources/cr_components/composebox/composebox_query.mojom-webui.js'; +import {ContextUploadStatus, ToolMode} from 'chrome://resources/cr_components/composebox/composebox_query.mojom-webui.js'; import {createAutocompleteMatch, createAutocompleteResultForTesting} from 'chrome://resources/cr_components/searchbox/searchbox_browser_proxy.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {PageCallbackRouter as SearchboxPageCallbackRouter, PageHandlerRemote as SearchboxPageHandlerRemote, type PageRemote as SearchboxPageRemote} from 'chrome://resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js'; @@ -562,6 +562,78 @@ assertEquals(1, composeboxElement.numberOfTimesTooltipShownForTesting); }); + test('ToolChipVisibilityBasedOnInputState', async () => { + const innerComposebox = contextualTasksApp.$.composebox.$.composebox; + + const getChip = (id: string) => + innerComposebox.shadowRoot?.querySelector<HTMLElement>(`#${id}`); + + // Initial state: No tool active. + let newInputState = { + ...mockInputState, + activeTool: ToolMode.kUnspecified, + }; + searchboxCallbackRouterRemote.onInputStateChanged(newInputState); + await microtasksFinished(); + await innerComposebox.updateComplete; + + assertFalse(isVisible(getChip('deepSearchChip'))); + assertFalse(isVisible(getChip('nanoBananaChip'))); + assertFalse(isVisible(getChip('canvasChip'))); + + // Activate Deep Search. + newInputState = { + ...mockInputState, + activeTool: ToolMode.kDeepSearch, + }; + searchboxCallbackRouterRemote.onInputStateChanged(newInputState); + await microtasksFinished(); + await innerComposebox.updateComplete; + + assertTrue(isVisible(getChip('deepSearchChip'))); + assertFalse(isVisible(getChip('nanoBananaChip'))); + assertFalse(isVisible(getChip('canvasChip'))); + + // Activate Image Gen (nanoBananaChip). + newInputState = { + ...mockInputState, + activeTool: ToolMode.kImageGen, + }; + searchboxCallbackRouterRemote.onInputStateChanged(newInputState); + await microtasksFinished(); + await innerComposebox.updateComplete; + + assertFalse(isVisible(getChip('deepSearchChip'))); + assertTrue(isVisible(getChip('nanoBananaChip'))); + assertFalse(isVisible(getChip('canvasChip'))); + + // Activate Canvas. + newInputState = { + ...mockInputState, + activeTool: ToolMode.kCanvas, + }; + searchboxCallbackRouterRemote.onInputStateChanged(newInputState); + await microtasksFinished(); + await innerComposebox.updateComplete; + + assertFalse(isVisible(getChip('deepSearchChip'))); + assertFalse(isVisible(getChip('nanoBananaChip'))); + assertTrue(isVisible(getChip('canvasChip'))); + + // Back to Unspecified. + newInputState = { + ...mockInputState, + activeTool: ToolMode.kUnspecified, + }; + searchboxCallbackRouterRemote.onInputStateChanged(newInputState); + await microtasksFinished(); + await innerComposebox.updateComplete; + + assertFalse(isVisible(getChip('deepSearchChip'))); + assertFalse(isVisible(getChip('nanoBananaChip'))); + assertFalse(isVisible(getChip('canvasChip'))); + }); + test('EnterKeyAfterSubmitDoesNotAddNewLine', async () => { mockTimer.install(); const TEST_QUERY = 'test query';
diff --git a/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts b/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts index 1329896..1f549d2 100644 --- a/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts +++ b/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts
@@ -723,6 +723,10 @@ test('deep search: thread change resets input', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kDeepSearch); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kDeepSearch, + }); await composebox.updateComplete; await microtasksFinished(); @@ -748,6 +752,10 @@ test('image gen: thread change resets input', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kImageGen); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kImageGen, + }); await composebox.updateComplete; await microtasksFinished(); @@ -774,6 +782,10 @@ test('canvas: thread change resets input', async () => { composebox.onToolClickForTesting(ComposeboxToolMode.kCanvas); + searchboxCallbackRouterRemote.onInputStateChanged({ + ...mockInputState, + activeTool: ComposeboxToolMode.kCanvas, + }); await composebox.updateComplete; await microtasksFinished();
diff --git a/chrome/test/data/webui/cr_components/composebox/composebox_voice_search_test.ts b/chrome/test/data/webui/cr_components/composebox/composebox_voice_search_test.ts index de76cf7..b23ef32 100644 --- a/chrome/test/data/webui/cr_components/composebox/composebox_voice_search_test.ts +++ b/chrome/test/data/webui/cr_components/composebox/composebox_voice_search_test.ts
@@ -4,12 +4,12 @@ import 'chrome://new-tab-page/strings.m.js'; import 'chrome://resources/cr_components/composebox/composebox.js'; +import 'chrome://resources/cr_components/composebox/composebox_voice_search.js'; import type {ComposeboxElement} from 'chrome://resources/cr_components/composebox/composebox.js'; -// import {VoiceSearchAction} from -// 'chrome://resources/cr_components/composebox/composebox.js'; import {PageCallbackRouter, PageHandlerRemote} from 'chrome://resources/cr_components/composebox/composebox.mojom-webui.js'; import {ComposeboxProxyImpl} from 'chrome://resources/cr_components/composebox/composebox_proxy.js'; +import type {ComposeboxVoiceSearchElement} from 'chrome://resources/cr_components/composebox/composebox_voice_search.js'; import {WindowProxy} from 'chrome://resources/cr_components/composebox/window_proxy.js'; import type {AudioWaveElement} from 'chrome://resources/cr_components/search/audio_wave.js'; import {GlowAnimationState} from 'chrome://resources/cr_components/search/constants.js'; @@ -17,7 +17,7 @@ import {PageCallbackRouter as SearchboxPageCallbackRouter, PageHandlerRemote as SearchboxPageHandlerRemote} from 'chrome://resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js'; import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import type {TestMock} from 'chrome://webui-test/test_mock.js'; -import {microtasksFinished} from 'chrome://webui-test/test_util.js'; +import {$$, microtasksFinished} from 'chrome://webui-test/test_util.js'; import {assertStyle, installMock} from './composebox_test_utils.js'; @@ -83,7 +83,7 @@ } as unknown as SpeechRecognitionEvent; } -suite('Composebox voice search', () => { +suite('ComposeboxVoiceSearch', () => { let composeboxElement: ComposeboxElement; let handler: TestMock<PageHandlerRemote>; let searchboxHandler: TestMock<SearchboxPageHandlerRemote>; @@ -131,6 +131,7 @@ removeListener() {}, removeEventListener() {}, })); + windowProxy.setResultFor('hasWebkitSpeechRecognition', true); composeboxElement = document.createElement('cr-composebox'); document.body.appendChild(composeboxElement); @@ -144,6 +145,14 @@ '#voiceSearchButton'); } + function getVoiceSearchElement(composeboxElement: ComposeboxElement): + ComposeboxVoiceSearchElement { + const voiceSearchElement = $$<ComposeboxVoiceSearchElement>( + composeboxElement, 'cr-composebox-voice-search'); + assertTrue(!!voiceSearchElement); + return voiceSearchElement; + } + test('voice search button does not show when disabled', async () => { loadTimeData.overrideValues({ steadyComposeboxShowVoiceSearch: false, @@ -154,7 +163,7 @@ document.body.appendChild(composeboxElement); await microtasksFinished(); - const voiceSearchButton = await getVoiceSearchButton(composeboxElement); + const voiceSearchButton = getVoiceSearchButton(composeboxElement); assertFalse(!!voiceSearchButton); // Restore. @@ -182,7 +191,8 @@ // Clicking the voice search button should start speech recognition. assertTrue(mockSpeechRecognition.voiceSearchInProgress); assertStyle(composeboxElement.$.composebox, 'opacity', '0'); - assertStyle(composeboxElement.$.voiceSearch, 'display', 'inline'); + assertStyle( + getVoiceSearchElement(composeboxElement), 'display', 'inline'); assertEquals( composeboxElement.animationState, GlowAnimationState.LISTENING); }); @@ -196,7 +206,7 @@ mockSpeechRecognition.onresult!(result); await microtasksFinished(); - const voiceSearchInput = composeboxElement.$.voiceSearch.$.input; + const voiceSearchInput = getVoiceSearchElement(composeboxElement).$.input; assertEquals('helloworld', voiceSearchInput.value); @@ -234,13 +244,14 @@ await microtasksFinished(); await showPromise; await composeboxElement.updateComplete; - await composeboxElement.$.voiceSearch.updateComplete; + const voiceSearchElement = getVoiceSearchElement(composeboxElement); + await voiceSearchElement.updateComplete; // Assert. assertFalse(mockSpeechRecognition.voiceSearchInProgress); assertEquals(searchboxHandler.getCallCount('submitQuery'), 0); assertStyle(composeboxElement.$.composebox, 'display', 'flex'); - assertStyle(composeboxElement.$.voiceSearch, 'display', 'none'); + assertStyle(voiceSearchElement, 'display', 'none'); assertEquals(composeboxElement.animationState, GlowAnimationState.NONE); }); @@ -265,13 +276,13 @@ await microtasksFinished(); await showPromise; await composeboxElement.updateComplete; - await composeboxElement.$.voiceSearch.updateComplete; + await getVoiceSearchElement(composeboxElement).updateComplete; // Assert. assertEquals(searchboxHandler.getCallCount('submitQuery'), 1); assertStyle(composeboxElement.$.composebox, 'display', 'flex'); - assertStyle(composeboxElement.$.voiceSearch, 'display', 'none'); + assertStyle(getVoiceSearchElement(composeboxElement), 'display', 'none'); }); test( @@ -295,25 +306,24 @@ // Act. mockSpeechRecognition.onresult!(result); - assertEquals( - (composeboxElement.$.voiceSearch as any).interimResult_, 'hello'); - assertEquals( - (composeboxElement.$.voiceSearch as any).finalResult_, 'world'); - assertEquals( - (composeboxElement.$.voiceSearch as any).transcript_, 'helloworld'); + const voiceSearchElement = + getVoiceSearchElement(composeboxElement) as any; + assertEquals(voiceSearchElement.interimResult_, 'hello'); + assertEquals(voiceSearchElement.finalResult_, 'world'); + assertEquals(voiceSearchElement.transcript_, 'helloworld'); const [callback] = await windowProxy.whenCalled('setTimeout'); callback(); await microtasksFinished(); await showPromise; await composeboxElement.updateComplete; - await composeboxElement.$.voiceSearch.updateComplete; + await voiceSearchElement.updateComplete; // Assert. assertEquals(searchboxHandler.getCallCount('submitQuery'), 1); assertStyle(composeboxElement.$.composebox, 'display', 'flex'); - assertStyle(composeboxElement.$.voiceSearch, 'display', 'none'); + assertStyle(voiceSearchElement, 'display', 'none'); }); test('idle timeout with final result submits query', async () => { @@ -333,22 +343,21 @@ // Act. mockSpeechRecognition.onresult!(result); - assertEquals( - (composeboxElement.$.voiceSearch as any).finalResult_, 'helloworld'); - assertEquals( - (composeboxElement.$.voiceSearch as any).transcript_, 'helloworld'); + const voiceSearchElement = getVoiceSearchElement(composeboxElement) as any; + assertEquals(voiceSearchElement.finalResult_, 'helloworld'); + assertEquals(voiceSearchElement.transcript_, 'helloworld'); const [callback] = await windowProxy.whenCalled('setTimeout'); callback(); await microtasksFinished(); await showPromise; await composeboxElement.updateComplete; - await composeboxElement.$.voiceSearch.updateComplete; + await voiceSearchElement.updateComplete; // Assert. assertEquals(searchboxHandler.getCallCount('submitQuery'), 1); assertStyle(composeboxElement.$.composebox, 'display', 'flex'); - assertStyle(composeboxElement.$.voiceSearch, 'display', 'none'); + assertStyle(voiceSearchElement, 'display', 'none'); }); test( @@ -377,10 +386,12 @@ await microtasksFinished(); await showPromise; await composeboxElement.updateComplete; - await composeboxElement.$.voiceSearch.updateComplete; - assertEquals((composeboxElement.$.voiceSearch as any).finalResult_, ''); + const voiceSearchElement = + getVoiceSearchElement(composeboxElement) as any; + await voiceSearchElement.updateComplete; + assertEquals(voiceSearchElement.finalResult_, ''); assertEquals( - (composeboxElement.$.voiceSearch as any).transcript_, 'helloworld', + voiceSearchElement.transcript_, 'helloworld', 'transcript should be set after result is' + 'processed but not finalized'); @@ -388,15 +399,16 @@ callback(); await microtasksFinished(); - assertEquals((composeboxElement.$.voiceSearch as any).finalResult_, ''); + assertEquals(voiceSearchElement.finalResult_, ''); assertEquals( - (composeboxElement.$.voiceSearch as any).transcript_, '', + voiceSearchElement.transcript_, '', 'transcript should be cleared after onend with no final result'); // Assert. assertEquals(searchboxHandler.getCallCount('submitQuery'), 1); assertStyle(composeboxElement.$.composebox, 'display', 'flex'); - assertStyle(composeboxElement.$.voiceSearch, 'display', 'none'); + assertStyle( + getVoiceSearchElement(composeboxElement), 'display', 'none'); assertEquals( composeboxElement.animationState, GlowAnimationState.SUBMITTING); }); @@ -419,32 +431,33 @@ const showPromise = getTransitionEndPromise(composeboxElement.$.composebox, 'opacity'); - assertEquals((composeboxElement.$.voiceSearch as any).finalResult_, ''); + const voiceSearchElement = getVoiceSearchElement(composeboxElement) as any; + assertEquals(voiceSearchElement.finalResult_, ''); assertEquals( - (composeboxElement.$.voiceSearch as any).transcript_, 'helloworld', + voiceSearchElement.transcript_, 'helloworld', 'transcript should be set after result is processed'); await showPromise; await composeboxElement.updateComplete; - await composeboxElement.$.voiceSearch.updateComplete; + await getVoiceSearchElement(composeboxElement).updateComplete; const [callback] = await windowProxy.whenCalled('setTimeout'); callback(); await microtasksFinished(); assertEquals( - (composeboxElement.$.voiceSearch as any).finalResult_, '', + voiceSearchElement.finalResult_, '', 'finalResult should be empty as no final result was received'); assertEquals( - (composeboxElement.$.voiceSearch as any).interimResult_, '', + voiceSearchElement.interimResult_, '', 'interimResult should be cleared after idle timeout'); assertEquals( - (composeboxElement.$.voiceSearch as any).transcript_, '', + voiceSearchElement.transcript_, '', 'transcript should be cleared after idle timeout'); assertEquals(searchboxHandler.getCallCount('submitQuery'), 1); assertStyle(composeboxElement.$.composebox, 'display', 'flex'); - assertStyle(composeboxElement.$.voiceSearch, 'display', 'none'); + assertStyle(getVoiceSearchElement(composeboxElement), 'display', 'none'); assertEquals( composeboxElement.animationState, GlowAnimationState.SUBMITTING); }); @@ -463,21 +476,17 @@ await microtasksFinished(); await hidePromise; await composeboxElement.updateComplete; - await composeboxElement.$.voiceSearch.updateComplete; + await getVoiceSearchElement(composeboxElement).updateComplete; - const voiceSearchElement = composeboxElement.$.voiceSearch; - const errorContainer = - voiceSearchElement.shadowRoot.querySelector<HTMLElement>( - '#error-container'); - const inputElement = - voiceSearchElement.shadowRoot.querySelector<HTMLTextAreaElement>( - '#input'); + const voiceSearchElement = getVoiceSearchElement(composeboxElement); + const errorContainer = $$(voiceSearchElement, '#error-container'); + const inputElement = $$(voiceSearchElement, '#input'); assertTrue(!!errorContainer); assertFalse(errorContainer.hidden); assertTrue(inputElement!.hidden); assertStyle(composeboxElement.$.composebox, 'opacity', '0'); - assertStyle(composeboxElement.$.voiceSearch, 'display', 'inline'); + assertStyle(getVoiceSearchElement(composeboxElement), 'display', 'inline'); assertEquals( composeboxElement.animationState, GlowAnimationState.LISTENING); }); @@ -497,18 +506,14 @@ await microtasksFinished(); await showPromise; await composeboxElement.updateComplete; - await composeboxElement.$.voiceSearch.updateComplete; + await getVoiceSearchElement(composeboxElement).updateComplete; const [callback] = await windowProxy.whenCalled('setTimeout'); callback(); - const voiceSearchElement = composeboxElement.$.voiceSearch; - const errorContainer = - voiceSearchElement.shadowRoot.querySelector<HTMLElement>( - '#error-container'); - const inputElement = - voiceSearchElement.shadowRoot.querySelector<HTMLTextAreaElement>( - '#input'); + const voiceSearchElement = getVoiceSearchElement(composeboxElement); + const errorContainer = $$(voiceSearchElement, '#error-container'); + const inputElement = $$(voiceSearchElement, '#input'); // The error container should be hidden because it's not a NOT_ALLOWED // error, and the voice search should close. @@ -516,7 +521,7 @@ assertFalse(inputElement!.hidden); assertStyle(composeboxElement.$.composebox, 'display', 'flex'); - assertStyle(composeboxElement.$.voiceSearch, 'display', 'none'); + assertStyle(getVoiceSearchElement(composeboxElement), 'display', 'none'); assertEquals(composeboxElement.animationState, GlowAnimationState.NONE); }); @@ -555,4 +560,26 @@ searchAnimatedGlow!.shadowRoot.querySelector('audio-wave'); assertTrue(!!audioWave); }); + + test( + 'voice search container is empty without webkitSpeechRecognition API', + async () => { + // Temporarily remove API + windowProxy.setResultFor('hasWebkitSpeechRecognition', false); + await microtasksFinished(); + + composeboxElement = document.createElement('cr-composebox'); + document.body.appendChild(composeboxElement); + await microtasksFinished(); + + // Query the DOM directly instead of using the `getVoiceSearchElement` + // helper, because the helper internally asserts that the element exists + // (assertTrue), which would cause this test to fail prematurely. + const voiceSearchElement = $$<ComposeboxVoiceSearchElement>( + composeboxElement, 'cr-composebox-voice-search'); + assertFalse(!!voiceSearchElement); + + // Restore API + windowProxy.setResultFor('hasWebkitSpeechRecognition', true); + }); });
diff --git a/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts b/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts index 7641456..35bced7 100644 --- a/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts +++ b/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts
@@ -62,8 +62,8 @@ <li>List item 2</li> </ul> <span style="display: block;">Span text</span> - <help-bubble-mixin-container id="container-element"> - </help-bubble-mixin-container> + <help-bubble-mixin-test-container id="container-element"> + </help-bubble-mixin-test-container> </div>`; } @@ -90,7 +90,7 @@ // HelpBubbleMixinTestContainerElement export class HelpBubbleMixinTestContainerElement extends CrLitElement { static get is() { - return 'help-bubble-mixin-container'; + return 'help-bubble-mixin-test-container'; } override render() {
diff --git a/chrome/test/data/webui/intro/dice_app_test.ts b/chrome/test/data/webui/intro/dice_app_test.ts index fb738042..d86c817b 100644 --- a/chrome/test/data/webui/intro/dice_app_test.ts +++ b/chrome/test/data/webui/intro/dice_app_test.ts
@@ -5,14 +5,14 @@ import 'chrome://intro/dice_app.js'; import {IntroBrowserProxyImpl} from 'chrome://intro/browser_proxy.js'; -import type {DiceAppElement} from 'chrome://intro/dice_app.js'; +import type {IntroAppElement} from 'chrome://intro/dice_app.js'; import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js'; import {TestIntroBrowserProxy} from './test_intro_browser_proxy.js'; suite('DiceAppTest', function() { - let testElement: DiceAppElement; + let testElement: IntroAppElement; let testBrowserProxy: TestIntroBrowserProxy; setup(function() {
diff --git a/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts b/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts index 14d1694..0f044e76 100644 --- a/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts +++ b/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts
@@ -11,8 +11,9 @@ import {WindowProxy} from 'chrome://new-tab-page/new_tab_page.js'; import type {TabUpload} from 'chrome://resources/cr_components/composebox/common.js'; import {TabUploadOrigin} from 'chrome://resources/cr_components/composebox/common.js'; +import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; -import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js'; import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js'; import type {TestMock} from 'chrome://webui-test/test_mock.js'; @@ -426,7 +427,8 @@ await initializeChips({actionChips}); assertTrue(!!chips); const allChips = Array.from<HTMLButtonElement>( - chips.shadowRoot.querySelectorAll<HTMLButtonElement>('button')); + chips.shadowRoot.querySelectorAll<HTMLButtonElement>( + '.action-chip')); assertEquals(3, allChips.length); assertTrue(allChips.every((e: HTMLButtonElement) => !!e)); @@ -470,7 +472,7 @@ await initializeChips({}); assertTrue(!!chips); const allChips = Array.from<HTMLButtonElement>( - chips.shadowRoot.querySelectorAll<HTMLButtonElement>('button')); + chips.shadowRoot.querySelectorAll<HTMLButtonElement>('.action-chip')); assertEquals(3, allChips.length); assertDeepEquals( @@ -576,6 +578,84 @@ chips.shadowRoot.querySelectorAll<HTMLButtonElement>('.action-chip'); assertEquals(2, allChips.length); }); + + test( + 'shows context menu on container when conditions are met', async () => { + loadTimeData.overrideValues({ + ntpNextShowSimplificationUIEnabled: true, + }); + await initializeChips({}); + chips.showBackground = true; + await microtasksFinished(); + + const container = chips.shadowRoot.querySelector<HTMLElement>( + '.action-chips-container'); + assertTrue(!!container); + + const actionMenu = + chips.shadowRoot.querySelector<CrActionMenuElement>( + '#actionMenu'); + assertTrue(!!actionMenu); + + container.dispatchEvent( + new MouseEvent('contextmenu', {cancelable: true})); + await microtasksFinished(); + + assertTrue(actionMenu.open); + }); + + test( + 'does not show context menu on container when conditions are not met', + async () => { + loadTimeData.overrideValues({ + ntpNextShowSimplificationUIEnabled: false, + }); + await initializeChips({}); + chips.showBackground = false; + await microtasksFinished(); + + const container = chips.shadowRoot.querySelector<HTMLElement>( + '.action-chips-container'); + assertTrue(!!container); + + const actionMenu = + chips.shadowRoot.querySelector<CrActionMenuElement>( + '#actionMenu'); + assertTrue(!!actionMenu); + + container.dispatchEvent( + new MouseEvent('contextmenu', {cancelable: true})); + await microtasksFinished(); + + assertFalse(actionMenu.open); + }); + + test( + 'disables action chips when context menu option is clicked', + async () => { + await initializeChips({}); + const allChips = chips.shadowRoot.querySelectorAll<HTMLButtonElement>( + '.action-chip'); + assertEquals(3, allChips.length); + + const firstChip = allChips[0]!; + const actionMenu = + chips.shadowRoot.querySelector<HTMLElement>('#actionMenu'); + assertTrue(!!actionMenu); + + firstChip.dispatchEvent( + new MouseEvent('contextmenu', {cancelable: true})); + await microtasksFinished(); + + const disableButton = + actionMenu.querySelector<HTMLButtonElement>('.dropdown-item'); + assertTrue(!!disableButton); + disableButton.click(); + await microtasksFinished(); + + assertEquals(1, handler.getCallCount('setActionChipsVisibility')); + assertEquals(false, handler.getArgs('setActionChipsVisibility')[0]); + }); }); suite('ReducedMotion', () => {
diff --git a/chrome/test/data/webui/new_tab_page/composebox/composebox_autocomplete_test.ts b/chrome/test/data/webui/new_tab_page/composebox/composebox_autocomplete_test.ts index 73d06b82..adfe46d 100644 --- a/chrome/test/data/webui/new_tab_page/composebox/composebox_autocomplete_test.ts +++ b/chrome/test/data/webui/new_tab_page/composebox/composebox_autocomplete_test.ts
@@ -3,13 +3,13 @@ // found in the LICENSE file. import {ComposeboxElement, VoiceSearchAction} from 'chrome://new-tab-page/lazy_load.js'; -import {$$} from 'chrome://new-tab-page/new_tab_page.js'; import {InputType} from 'chrome://resources/cr_components/composebox/composebox_query.mojom-webui.js'; +import type {ComposeboxVoiceSearchElement} from 'chrome://resources/cr_components/composebox/composebox_voice_search.js'; import {createAutocompleteResultForTesting, createSearchMatchForTesting} from 'chrome://resources/cr_components/searchbox/searchbox_browser_proxy.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import type {TabInfo} from 'chrome://resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js'; import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; -import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js'; +import {$$, eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js'; import {assertStyle} from '../test_support.js'; @@ -1190,7 +1190,10 @@ testProxy.searchboxHandler.reset(); const voiceQuery = 'hello'; - testProxy.element.$.voiceSearch.dispatchEvent(new CustomEvent( + const voiceSearchElement = $$<ComposeboxVoiceSearchElement>( + testProxy.element, 'cr-composebox-voice-search'); + assertTrue(!!voiceSearchElement); + voiceSearchElement.dispatchEvent(new CustomEvent( 'voice-search-final-result', {detail: voiceQuery, bubbles: true, composed: true})); await microtasksFinished(); @@ -1255,7 +1258,10 @@ const voiceSearchActionPromise = eventToPromise('voice-search-action', testProxy.element); const voiceQuery = 'hello'; - testProxy.element.$.voiceSearch.dispatchEvent(new CustomEvent( + const voiceSearchElement = $$<ComposeboxVoiceSearchElement>( + testProxy.element, 'cr-composebox-voice-search'); + assertTrue(!!voiceSearchElement); + voiceSearchElement.dispatchEvent(new CustomEvent( 'voice-search-final-result', {detail: voiceQuery, bubbles: true, composed: true}));
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index c2ad332..134db74 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -16612.0.0-1075871 \ No newline at end of file +16613.0.0-1075889 \ No newline at end of file
diff --git a/chromeos/ash/experiences/settings_ui/DEPS b/chromeos/ash/experiences/settings_ui/DEPS index 925893f..7c7bc6c 100644 --- a/chromeos/ash/experiences/settings_ui/DEPS +++ b/chromeos/ash/experiences/settings_ui/DEPS
@@ -1,3 +1,3 @@ include_rules = [ - "+ash/webui/settings/public/constants/setting.mojom-shared.h", + "+ash/webui/settings/public/constants", ]
diff --git a/chromeos/ash/experiences/settings_ui/settings_app_manager.cc b/chromeos/ash/experiences/settings_ui/settings_app_manager.cc index 0d20780f..bec1a3173 100644 --- a/chromeos/ash/experiences/settings_ui/settings_app_manager.cc +++ b/chromeos/ash/experiences/settings_ui/settings_app_manager.cc
@@ -4,8 +4,10 @@ #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" +#include "ash/webui/settings/public/constants/routes.mojom.h" #include "base/check.h" #include "base/check_op.h" +#include "base/strings/strcat.h" namespace ash { namespace { @@ -13,6 +15,13 @@ } // namespace // static +std::string SettingsAppManager::CreateAppManagementPagePath( + std::string_view app_id) { + return base::StrCat( + {chromeos::settings::mojom::kAppDetailsSubpagePath, "?id=", app_id}); +} + +// static SettingsAppManager* SettingsAppManager::Get() { return g_instance; }
diff --git a/chromeos/ash/experiences/settings_ui/settings_app_manager.h b/chromeos/ash/experiences/settings_ui/settings_app_manager.h index e889b313..147c5f8 100644 --- a/chromeos/ash/experiences/settings_ui/settings_app_manager.h +++ b/chromeos/ash/experiences/settings_ui/settings_app_manager.h
@@ -21,6 +21,35 @@ // This (its implementation class) can be alive at most once at a time. class COMPONENT_EXPORT(SETTINGS_UI) SettingsAppManager { public: + // These are used in histograms, do not remove/renumber entries. If you're + // adding to this enum with the intention that it will be logged, update the + // EntryPoint enum listing in + // tools/metrics/histograms/metadata/apps/enums.xml. + enum class EntryPoint { + kAppListContextMenuAppInfoArc = 0, + kAppListContextMenuAppInfoChromeApp = 1, + kAppListContextMenuAppInfoWebApp = 2, + kShelfContextMenuAppInfoArc = 3, + kShelfContextMenuAppInfoChromeApp = 4, + kShelfContextMenuAppInfoWebApp = 5, + kAppManagementMainViewArc = 6, + kAppManagementMainViewChromeApp = 7, + kAppManagementMainViewWebApp = 8, + kOsSettingsMainPage = 9, + kAppManagementMainViewPluginVm = 10, + kDBusServicePluginVm = 11, + kNotificationPluginVm = 12, + kAppManagementMainViewBorealis = 13, + kPageInfoView = 14, + kPrivacyIndicatorsNotificationSettings = 15, + kSubAppsInstallPrompt = 16, + kSiteDataDialog = 17, + kMaxValue = kSiteDataDialog, + }; + + // Helper method to create the path. + static std::string CreateAppManagementPagePath(std::string_view app_id); + // Returns the singleton instance static SettingsAppManager* Get(); @@ -31,10 +60,13 @@ // If specified, opens the subpage, instead of settings top page. std::string_view sub_page; std::optional<chromeos::settings::mojom::Setting> setting_id; + // If specified, used to record AppManagement.EntryPoints histogram. + std::optional<EntryPoint> entry_point; // ID of the display to be used, if given. int64_t display_id = display::kInvalidDisplayId; }; + // Opens the settings app of the given `user`. virtual void Open(const user_manager::User& user, OpenParams params) = 0;
diff --git a/chromeos/components/quick_answers/understanding/intent_generator.cc b/chromeos/components/quick_answers/understanding/intent_generator.cc index a41ed84..35ab2ad 100644 --- a/chromeos/components/quick_answers/understanding/intent_generator.cc +++ b/chromeos/components/quick_answers/understanding/intent_generator.cc
@@ -322,8 +322,7 @@ const QuickAnswersRequest& request) { DCHECK(complete_callback_); - if (!QuickAnswersState::IsIntentEligible(Intent::kTranslation) || - chromeos::features::IsQuickAnswersV2TranslationDisabled()) { + if (!QuickAnswersState::IsIntentEligible(Intent::kTranslation)) { std::move(complete_callback_) .Run(IntentInfo(request.selected_text, IntentType::kUnknown)); return;
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc index a164691..3d7245c 100644 --- a/chromeos/constants/chromeos_features.cc +++ b/chromeos/constants/chromeos_features.cc
@@ -50,10 +50,6 @@ // Enables data migration. BASE_FEATURE(kDataMigration, base::FEATURE_DISABLED_BY_DEFAULT); -// Disables translation services of the Quick Answers V2. -BASE_FEATURE(kDisableQuickAnswersV2Translation, - base::FEATURE_DISABLED_BY_DEFAULT); - // Enables Essential Search in Omnibox for both launcher and browser. BASE_FEATURE(kEssentialSearch, base::FEATURE_ENABLED_BY_DEFAULT); @@ -406,10 +402,6 @@ return base::FeatureList::IsEnabled(kQuickAnswersMaterialNextUI); } -bool IsQuickAnswersV2TranslationDisabled() { - return base::FeatureList::IsEnabled(kDisableQuickAnswersV2Translation); -} - bool IsQuickAnswersRichCardEnabled() { return base::FeatureList::IsEnabled(kQuickAnswersRichCard); }
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h index 78cf0c9..888be09 100644 --- a/chromeos/constants/chromeos_features.h +++ b/chromeos/constants/chromeos_features.h
@@ -33,9 +33,6 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kDataControlsFileAccessDefaultDeny); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kDataMigration); -COMPONENT_EXPORT(CHROMEOS_CONSTANTS) -COMPONENT_EXPORT(CHROMEOS_CONSTANTS) -BASE_DECLARE_FEATURE(kDisableQuickAnswersV2Translation); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kEssentialSearch); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kExternalDisplayEventTelemetry); @@ -171,7 +168,6 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsOrcaEnabled(); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsOrcaUseL10nStringsEnabled(); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsOrcaInternationalizeEnabled(); -COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersV2TranslationDisabled(); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersV2SettingsSubToggleEnabled(); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersMaterialNextUIEnabled();
diff --git a/components/bookmarks/browser/bookmark_model_unittest.cc b/components/bookmarks/browser/bookmark_model_unittest.cc index f1dceafe..340560d 100644 --- a/components/bookmarks/browser/bookmark_model_unittest.cc +++ b/components/bookmarks/browser/bookmark_model_unittest.cc
@@ -9,7 +9,6 @@ #include <algorithm> #include <array> -#include <optional> #include <set> #include <string> #include <string_view> @@ -303,75 +302,21 @@ class BookmarkModelTest : public testing::Test, public BookmarkModelObserver { public: - struct ObserverDetails { - ObserverDetails() { - Set(nullptr, nullptr, static_cast<size_t>(-1), static_cast<size_t>(-1), - false, FROM_HERE); - } - - void Set(const BookmarkNode* node1, - const BookmarkNode* node2, - size_t index1, - size_t index2, - bool added_by_user, - const base::Location& location) { - node1_ = node1; - node2_ = node2; - index1_ = index1; - index2_ = index2; - added_by_user_ = added_by_user; - location_ = location; - } - - void Reset() { - Set(nullptr, nullptr, static_cast<size_t>(-1), static_cast<size_t>(-1), - false, FROM_HERE); - } - - void ExpectEquals(const BookmarkNode* node1, - const BookmarkNode* node2, - size_t index1, - size_t index2, - bool added_by_user, - std::optional<base::Location> location = std::nullopt) { - EXPECT_EQ(node1_, node1); - EXPECT_EQ(node2_, node2); - EXPECT_EQ(index1_, index1); - EXPECT_EQ(index2_, index2); - EXPECT_EQ(added_by_user_, added_by_user); - if (location.has_value()) { - EXPECT_EQ(location_, *location); - } - } - - private: - raw_ptr<const BookmarkNode> node1_; - raw_ptr<const BookmarkNode> node2_; - size_t index1_; - size_t index2_; - bool added_by_user_; - base::Location location_; - }; - - struct AllNodesRemovedDetail { - explicit AllNodesRemovedDetail(const std::set<GURL>& removed_urls) - : removed_urls(removed_urls) {} - - bool operator==(const AllNodesRemovedDetail& other) const { - return removed_urls == other.removed_urls; - } - - std::set<GURL> removed_urls; - }; - BookmarkModelTest() : model_(TestBookmarkClient::CreateModelWithClient( std::make_unique<TestBookmarkClientWithUndo>())) { + // TODO(crbug.com/380282512): Remove this legacy observer/counting + // mechanism once the remaining tests are migrated to + // MockBookmarkModelObserver. model_->AddObserver(this); + model_->AddObserver(&mock_observer_); ClearCounts(); } - ~BookmarkModelTest() override { model_->RemoveObserver(this); } + ~BookmarkModelTest() override { + model_->RemoveObserver(&mock_observer_); + model_->RemoveObserver(this); + } BookmarkModelTest(const BookmarkModelTest&) = delete; BookmarkModelTest& operator=(const BookmarkModelTest&) = delete; @@ -386,8 +331,6 @@ const BookmarkNode* new_parent, size_t new_index) override { ++before_moved_count_; - before_observer_details_.Set(old_parent, new_parent, old_index, new_index, - false, FROM_HERE); } void BookmarkNodeMoved(const BookmarkNode* old_parent, @@ -395,16 +338,12 @@ const BookmarkNode* new_parent, size_t new_index) override { ++moved_count_; - observer_details_.Set(old_parent, new_parent, old_index, new_index, false, - FROM_HERE); } void BookmarkNodeAdded(const BookmarkNode* parent, size_t index, bool added_by_user) override { ++added_count_; - observer_details_.Set(parent, nullptr, index, static_cast<size_t>(-1), - added_by_user, FROM_HERE); } void OnWillRemoveBookmarks(const BookmarkNode* parent, @@ -412,8 +351,6 @@ const BookmarkNode* node, const base::Location& location) override { ++before_remove_count_; - before_observer_details_.Set(parent, nullptr, old_index, - static_cast<size_t>(-1), false, location); } void BookmarkNodeRemoved(const BookmarkNode* parent, @@ -422,20 +359,14 @@ const std::set<GURL>& removed_urls, const base::Location& location) override { ++removed_count_; - observer_details_.Set(parent, nullptr, old_index, static_cast<size_t>(-1), - false, location); } void BookmarkNodeChanged(const BookmarkNode* node) override { ++changed_count_; - observer_details_.Set(node, nullptr, static_cast<size_t>(-1), - static_cast<size_t>(-1), false, FROM_HERE); } void OnWillChangeBookmarkNode(const BookmarkNode* node) override { ++before_change_count_; - before_observer_details_.Set(node, nullptr, static_cast<size_t>(-1), - static_cast<size_t>(-1), false, FROM_HERE); } void BookmarkNodeChildrenReordered(const BookmarkNode* node) override { @@ -462,13 +393,10 @@ void BookmarkAllUserNodesRemoved(const std::set<GURL>& removed_urls, const base::Location& location) override { ++all_bookmarks_removed_; - all_bookmarks_removed_details_.emplace_back(removed_urls); } void OnWillRemoveAllUserBookmarks(const base::Location& location) override { ++before_remove_all_count_; - observer_details_.Reset(); - before_observer_details_.Reset(); } void GroupedBookmarkChangesBeginning() override { @@ -534,9 +462,8 @@ EXPECT_EQ(grouped_changes_ended_count, grouped_changes_ended_count_); } - int AllNodesRemovedObserverCount() const { return all_bookmarks_removed_; } - BookmarkPermanentNode* ReloadModelWithManagedNode() { + model_->RemoveObserver(&mock_observer_); model_->RemoveObserver(this); auto client = std::make_unique<TestBookmarkClient>(); @@ -544,6 +471,7 @@ model_ = TestBookmarkClient::CreateModelWithClient(std::move(client)); model_->AddObserver(this); + model_->AddObserver(&mock_observer_); ClearCounts(); if (!model_->root_node()->GetIndexOf(managed_node).has_value()) { @@ -586,16 +514,15 @@ } base::HistogramTester* histogram_tester() { return &histogram_tester_; } + MockBookmarkModelObserver& mock_observer() { return mock_observer_; } base::UserActionTester* user_action_tester() { return &user_action_tester_; } protected: base::test::ScopedFeatureList features_{ switches::kSyncEnableBookmarksInTransportMode}; std::unique_ptr<BookmarkModel> model_; - ObserverDetails observer_details_; - ObserverDetails before_observer_details_; - std::vector<AllNodesRemovedDetail> all_bookmarks_removed_details_; base::HistogramTester histogram_tester_; + testing::NiceMock<MockBookmarkModelObserver> mock_observer_; base::UserActionTester user_action_tester_; private: @@ -642,11 +569,10 @@ const std::u16string kTitle(u"foo"); const GURL kUrl("http://foo.com"); + EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, false)); + const BookmarkNode* new_node = model_->AddURL(bookmark_bar_node, 0, kTitle, kUrl); - AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0); - observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0, - static_cast<size_t>(-1), false); ASSERT_EQ(1u, bookmark_bar_node->children().size()); ASSERT_EQ(kTitle, new_node->GetTitle()); @@ -666,11 +592,10 @@ const std::u16string kTitle(u"foo"); const GURL kUrl("http://foo.com"); + EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, true)); + const BookmarkNode* new_node = model_->AddNewURL(bookmark_bar_node, 0, kTitle, kUrl); - AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0); - observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0, - static_cast<size_t>(-1), true); ASSERT_EQ(1u, bookmark_bar_node->children().size()); ASSERT_EQ(kTitle, new_node->GetTitle()); @@ -756,11 +681,10 @@ u"\u767e\u5ea6\u4e00\u4e0b\uff0c\u4f60\u5c31\u77e5\u9053"); const GURL kUrl("https://www.baidu.com/"); + EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, false)); + const BookmarkNode* new_node = model_->AddURL(bookmark_bar_node, 0, kTitle, kUrl); - AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0); - observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0, - static_cast<size_t>(-1), false); ASSERT_EQ(1u, bookmark_bar_node->children().size()); ASSERT_EQ(kTitle, new_node->GetTitle()); @@ -799,11 +723,10 @@ BookmarkNode::MetaInfoMap meta_info; meta_info["foo"] = "bar"; + EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, false)); + const BookmarkNode* new_node = model_->AddURL(bookmark_bar_node, 0, kTitle, kUrl, &meta_info, kTime); - AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0); - observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0, - static_cast<size_t>(-1), false); ASSERT_EQ(1u, bookmark_bar_node->children().size()); ASSERT_EQ(kTitle, new_node->GetTitle()); @@ -836,14 +759,13 @@ } TEST_F(BookmarkModelTest, AddURLToMobileBookmarks) { - const BookmarkNode* mobile_node = model_->mobile_node(); + const BookmarkPermanentNode* mobile_node = model_->mobile_node(); const std::u16string kTitle(u"foo"); const GURL kUrl("http://foo.com"); + EXPECT_CALL(mock_observer(), BookmarkNodeAdded(mobile_node, 0, false)); + const BookmarkNode* new_node = model_->AddURL(mobile_node, 0, kTitle, kUrl); - AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0); - observer_details_.ExpectEquals(mobile_node, nullptr, 0, - static_cast<size_t>(-1), false); ASSERT_EQ(1u, mobile_node->children().size()); ASSERT_EQ(kTitle, new_node->GetTitle()); @@ -861,11 +783,10 @@ const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node(); const std::u16string kTitle(u"foo"); + EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, false)); + const BookmarkNode* new_node = model_->AddFolder(bookmark_bar_node, 0, kTitle); - AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0); - observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0, - static_cast<size_t>(-1), false); ASSERT_EQ(1u, bookmark_bar_node->children().size()); ASSERT_EQ(kTitle, new_node->GetTitle()); @@ -877,12 +798,11 @@ testing::Ne(model_->other_node()->id()), testing::Ne(model_->mobile_node()->id()))); + ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_observer())); + // Add another folder, just to make sure folder_ids are incremented correctly. - ClearCounts(); + EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, false)); model_->AddFolder(bookmark_bar_node, 0, kTitle); - AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0); - observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0, - static_cast<size_t>(-1), false); } TEST_F(BookmarkModelTest, AddFolderWithCreationTime) { @@ -931,19 +851,21 @@ const GURL kUrl("http://foo.com"); const base::Location kLocation = FROM_HERE; - model_->AddURL(bookmark_bar_node, 0, kTitle, kUrl); - ClearCounts(); + const BookmarkNode* node = model_->AddURL(bookmark_bar_node, 0, kTitle, kUrl); - model_->Remove(bookmark_bar_node->children().front().get(), - bookmarks::metrics::BookmarkEditSource::kOther, kLocation); + testing::InSequence seq; + EXPECT_CALL(mock_observer(), + OnWillRemoveBookmarks(bookmark_bar_node, 0, node, kLocation)); + EXPECT_CALL(mock_observer(), + BookmarkNodeRemoved(bookmark_bar_node, 0, node, _, kLocation)); + + model_->Remove(node, bookmarks::metrics::BookmarkEditSource::kOther, + kLocation); ASSERT_EQ(0u, bookmark_bar_node->children().size()); histogram_tester()->ExpectTotalCount("Bookmarks.RemovedSource", 1); histogram_tester()->ExpectBucketCount( "Bookmarks.RemovedSource", static_cast<int>(metrics::BookmarkEditSource::kOther), 1); - AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0, 0); - observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0, - static_cast<size_t>(-1), false, kLocation); // Make sure there is no mapping for the URL. ASSERT_EQ(model_->GetMostRecentlyAddedUserNodeForURL(kUrl), nullptr); @@ -953,26 +875,27 @@ const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node(); const BookmarkNode* folder = model_->AddFolder(bookmark_bar_node, 0, u"foo"); - ClearCounts(); - // Add a URL as a child. const std::u16string kTitle(u"foo"); const GURL kUrl("http://foo.com"); model_->AddURL(folder, 0, kTitle, kUrl); - ClearCounts(); + const base::Location kLocation = FROM_HERE; + + testing::InSequence seq; + EXPECT_CALL(mock_observer(), + OnWillRemoveBookmarks(bookmark_bar_node, 0, folder, kLocation)); + EXPECT_CALL(mock_observer(), + BookmarkNodeRemoved(bookmark_bar_node, 0, folder, _, kLocation)); // Now remove the folder. - model_->Remove(bookmark_bar_node->children().front().get(), - bookmarks::metrics::BookmarkEditSource::kOther, FROM_HERE); + model_->Remove(folder, bookmarks::metrics::BookmarkEditSource::kOther, + kLocation); ASSERT_EQ(0u, bookmark_bar_node->children().size()); histogram_tester()->ExpectTotalCount("Bookmarks.RemovedSource", 1); histogram_tester()->ExpectBucketCount( "Bookmarks.RemovedSource", static_cast<int>(metrics::BookmarkEditSource::kOther), 1); - AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0, 0); - observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0, - static_cast<size_t>(-1), false); // Make sure there is no mapping for the URL. ASSERT_EQ(model_->GetMostRecentlyAddedUserNodeForURL(kUrl), nullptr); @@ -987,8 +910,15 @@ const base::Location kLocation = FROM_HERE; model_->AddURL(bookmark_bar_node, 0, kTitle1, kUrl1); - model_->AddURL(bookmark_bar_node, 1, kTitle2, kUrl2); - ClearCounts(); + + const BookmarkNode* node2 = + model_->AddURL(bookmark_bar_node, 1, kTitle2, kUrl2); + + testing::InSequence seq; + EXPECT_CALL(mock_observer(), + OnWillRemoveBookmarks(bookmark_bar_node, 1, node2, kLocation)); + EXPECT_CALL(mock_observer(), + BookmarkNodeRemoved(bookmark_bar_node, 1, node2, _, kLocation)); model_->RemoveLastChild(bookmark_bar_node, bookmarks::metrics::BookmarkEditSource::kOther, @@ -998,9 +928,6 @@ histogram_tester()->ExpectBucketCount( "Bookmarks.RemovedSource", static_cast<int>(metrics::BookmarkEditSource::kOther), 1); - AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0, 0); - observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 1, - static_cast<size_t>(-1), false, kLocation); ASSERT_NE(nullptr, model_->GetMostRecentlyAddedUserNodeForURL(kUrl1)); // Make sure there is no mapping for the removed URL. @@ -1010,8 +937,6 @@ TEST_F(BookmarkModelTest, RemoveAllUserBookmarks) { const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node(); - ClearCounts(); - // Add a url to bookmark bar. const std::u16string kTitle(u"foo"); const GURL kUrl("http://foo.com"); @@ -1021,25 +946,27 @@ const BookmarkNode* folder = model_->AddFolder(bookmark_bar_node, 0, kTitle); model_->AddURL(folder, 0, kTitle, kUrl); - AssertObserverCount(3, 0, 0, 0, 0, 0, 0, 0, 0, 0); - ClearCounts(); + const base::Location kLocation = FROM_HERE; size_t permanent_node_count = model_->root_node()->children().size(); - model_->RemoveAllUserBookmarks(FROM_HERE); + EXPECT_CALL(mock_observer(), BookmarkNodeRemoved).Times(0); + + testing::InSequence seq; + EXPECT_CALL(mock_observer(), OnWillRemoveAllUserBookmarks(kLocation)); + EXPECT_CALL(mock_observer(), ExtensiveBookmarkChangesBeginning()); + EXPECT_CALL(mock_observer(), ExtensiveBookmarkChangesEnded()); + EXPECT_CALL(mock_observer(), + BookmarkAllUserNodesRemoved( + testing::ContainerEq(std::set<GURL>{kUrl}), kLocation)); + EXPECT_CALL(mock_observer(), GroupedBookmarkChangesBeginning()); + EXPECT_CALL(mock_observer(), GroupedBookmarkChangesEnded()); + + model_->RemoveAllUserBookmarks(kLocation); EXPECT_EQ(0u, bookmark_bar_node->children().size()); // No permanent node should be removed. EXPECT_EQ(permanent_node_count, model_->root_node()->children().size()); - // No individual BookmarkNodeRemoved events are fired, so removed count - // should be 0. - AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 0, 1, 0); - AssertExtensiveChangesObserverCount(1, 1); - AssertGroupedChangesObserverCount(1, 1); - EXPECT_EQ(1, AllNodesRemovedObserverCount()); - EXPECT_EQ(1, AllNodesRemovedObserverCount()); - EXPECT_THAT(all_bookmarks_removed_details_, - ElementsAre(AllNodesRemovedDetail(/*removed_urls=*/{kUrl}))); } TEST_F(BookmarkModelTest, UpdateLastUsedTimeInRange) { @@ -1149,13 +1076,12 @@ const BookmarkNode* node = model_->AddURL(bookmark_bar_node, 0, kOriginalTitle, kUrl); - ClearCounts(); + testing::InSequence seq; + EXPECT_CALL(mock_observer(), OnWillChangeBookmarkNode(node)); + EXPECT_CALL(mock_observer(), BookmarkNodeChanged(node)); const std::u16string kNewTitle(u"goo"); model_->SetTitle(node, kNewTitle, metrics::BookmarkEditSource::kOther); - AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0, 0); - observer_details_.ExpectEquals(node, nullptr, static_cast<size_t>(-1), - static_cast<size_t>(-1), false); EXPECT_EQ(kNewTitle, node->GetTitle()); // Should update the index. @@ -1192,7 +1118,9 @@ const GURL kUrl("http://foo.com"); const BookmarkNode* node = model_->AddURL(folder, 0, kTitle, kUrl); - ClearCounts(); + testing::InSequence seq; + EXPECT_CALL(mock_observer(), OnWillChangeBookmarkNode(folder)); + EXPECT_CALL(mock_observer(), BookmarkNodeChanged(folder)); model_->SetTitle(folder, u"golder", metrics::BookmarkEditSource::kOther); @@ -1223,13 +1151,12 @@ const BookmarkNode* node = model_->AddURL(bookmark_bar_node, 0, kTitle, kOriginalUrl); - ClearCounts(); + testing::InSequence seq; + EXPECT_CALL(mock_observer(), OnWillChangeBookmarkNode(node)); + EXPECT_CALL(mock_observer(), BookmarkNodeChanged(node)); const GURL kNewUrl("http://foo2.com"); model_->SetURL(node, kNewUrl, metrics::BookmarkEditSource::kOther); - AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0, 0); - observer_details_.ExpectEquals(node, nullptr, static_cast<size_t>(-1), - static_cast<size_t>(-1), false); EXPECT_EQ(kNewUrl, node->url()); histogram_tester()->ExpectBucketCount("Bookmarks.EditURLSource", metrics::BookmarkEditSource::kOther, 1); @@ -1258,15 +1185,16 @@ const BookmarkNode* folder1 = model_->AddFolder(bookmark_bar_node, 0, u"folder"); - ClearCounts(); + { + testing::InSequence seq; + EXPECT_CALL(mock_observer(), + OnWillMoveBookmarkNode(bookmark_bar_node, 1, folder1, 0)); + EXPECT_CALL(mock_observer(), + BookmarkNodeMoved(bookmark_bar_node, 1, folder1, 0)); + } model_->Move(node, folder1, 0); - AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0, - /*before_moved_count=*/1); - before_observer_details_.ExpectEquals(bookmark_bar_node, folder1, 1, 0, - false); - observer_details_.ExpectEquals(bookmark_bar_node, folder1, 1, 0, false); EXPECT_EQ(folder1, node->parent()); EXPECT_EQ(1u, bookmark_bar_node->children().size()); EXPECT_EQ(folder1, bookmark_bar_node->children().front().get()); @@ -1277,13 +1205,19 @@ u"folder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT); EXPECT_EQ(matches[0].node, node); + ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_observer())); + // And remove the folder. - ClearCounts(); - model_->Remove(bookmark_bar_node->children().front().get(), - bookmarks::metrics::BookmarkEditSource::kOther, FROM_HERE); - AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0, 0); - observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0, - static_cast<size_t>(-1), false); + const base::Location kLocation = FROM_HERE; + { + testing::InSequence seq; + EXPECT_CALL(mock_observer(), OnWillRemoveBookmarks(bookmark_bar_node, 0, + folder1, kLocation)); + EXPECT_CALL(mock_observer(), BookmarkNodeRemoved(bookmark_bar_node, 0, + folder1, _, kLocation)); + } + model_->Remove(folder1, bookmarks::metrics::BookmarkEditSource::kOther, + kLocation); EXPECT_EQ(model_->GetMostRecentlyAddedUserNodeForURL(kUrl), nullptr); EXPECT_EQ(0u, bookmark_bar_node->children().size()); @@ -1305,37 +1239,44 @@ PopulateBookmarkNode(&abc, model_.get(), parent); // Move to current_index - 1 moves to the left: [a, b, c] -> [b, a, c] - ClearCounts(); - model_->Move(parent->children()[1].get(), parent, 0); + { + testing::InSequence seq; + EXPECT_CALL(mock_observer(), OnWillMoveBookmarkNode(parent, 1, parent, 0)); + EXPECT_CALL(mock_observer(), BookmarkNodeMoved(parent, 1, parent, 0)); + + model_->Move(parent->children()[1].get(), parent, 0); + } VerifyModelMatchesNode(&bac, parent); - AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0, - /*before_moved_count=*/1); - before_observer_details_.ExpectEquals(parent, parent, 1, 0, false); - observer_details_.ExpectEquals(parent, parent, 1, 0, false); + ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_observer())); // Move the current_index is a no-op: [b, a, c] -> [b, a, c] - ClearCounts(); - model_->Move(parent->children()[1].get(), parent, 1); + { + EXPECT_CALL(mock_observer(), OnWillMoveBookmarkNode).Times(0); + EXPECT_CALL(mock_observer(), BookmarkNodeMoved).Times(0); + model_->Move(parent->children()[1].get(), parent, 1); + } VerifyModelMatchesNode(&bac, parent); - AssertObserverCount(0, /*moved_count=*/0, 0, 0, 0, 0, 0, 0, 0, - /*before_moved_count=*/0); + ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_observer())); // Move to current_index + 1 is a no-op: [b, a, c] -> [b, a, c] - ClearCounts(); - model_->Move(parent->children()[1].get(), parent, 2); + { + EXPECT_CALL(mock_observer(), OnWillMoveBookmarkNode).Times(0); + EXPECT_CALL(mock_observer(), BookmarkNodeMoved).Times(0); + model_->Move(parent->children()[1].get(), parent, 2); + } VerifyModelMatchesNode(&bac, parent); - AssertObserverCount(0, /*moved_count=*/0, 0, 0, 0, 0, 0, 0, 0, - /*before_moved_count=*/0); + ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_observer())); // Move to current_index + 2 moves 1 to the right: [b, a, c] -> [b, c, a] // Note: this tests moving to an index that is one past the end of the list. - ClearCounts(); - model_->Move(parent->children()[1].get(), parent, 3); + { + testing::InSequence seq; + EXPECT_CALL(mock_observer(), OnWillMoveBookmarkNode(parent, 1, parent, 2)); + EXPECT_CALL(mock_observer(), BookmarkNodeMoved(parent, 1, parent, 2)); + + model_->Move(parent->children()[1].get(), parent, 3); + } VerifyModelMatchesNode(&bca, parent); - AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0, - /*before_moved_count=*/1); - before_observer_details_.ExpectEquals(parent, parent, 1, 2, false); - observer_details_.ExpectEquals(parent, parent, 1, 2, false); } TEST_F(BookmarkModelTest, NonMovingMoveCall) { @@ -1365,15 +1306,16 @@ const GURL kUrl("http://foo.com"); const BookmarkNode* node = model_->AddURL(folder1, 0, kTitle, kUrl); - ClearCounts(); + { + testing::InSequence seq; + EXPECT_CALL(mock_observer(), + OnWillMoveBookmarkNode(folder1, 0, folder2, 0)); + EXPECT_CALL(mock_observer(), BookmarkNodeMoved(folder1, 0, folder2, 0)); + } model_->Move(node, folder2, 0); // Should update the hierarchy. - AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0, - /*before_moved_count=*/1); - before_observer_details_.ExpectEquals(folder1, folder2, 0, 0, false); - observer_details_.ExpectEquals(folder1, folder2, 0, 0, false); EXPECT_EQ(bookmark_bar_node->children().size(), 2u); EXPECT_EQ(folder1->children().size(), 0u); EXPECT_EQ(folder2->children().size(), 1u); @@ -1388,14 +1330,15 @@ matches.clear(); // Move back. - ClearCounts(); + { + testing::InSequence seq; + EXPECT_CALL(mock_observer(), + OnWillMoveBookmarkNode(folder2, 0, folder1, 0)); + EXPECT_CALL(mock_observer(), BookmarkNodeMoved(folder2, 0, folder1, 0)); + } model_->Move(node, folder1, 0); // Should update the hierarchy. - AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0, - /*before_moved_count=*/1); - before_observer_details_.ExpectEquals(folder2, folder1, 0, 0, false); - observer_details_.ExpectEquals(folder2, folder1, 0, 0, false); EXPECT_EQ(bookmark_bar_node->children().size(), 2u); EXPECT_EQ(folder1->children().size(), 1u); EXPECT_EQ(folder2->children().size(), 0u); @@ -1421,15 +1364,13 @@ const GURL kUrl("http://foo.com"); const BookmarkNode* node = model_->AddURL(folder3, 0, kTitle, kUrl); - ClearCounts(); + testing::InSequence seq; + EXPECT_CALL(mock_observer(), OnWillMoveBookmarkNode(folder1, 0, folder2, 0)); + EXPECT_CALL(mock_observer(), BookmarkNodeMoved(folder1, 0, folder2, 0)); model_->Move(folder3, folder2, 0); // Should update the hierarchy. - AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0, - /*before_moved_count=*/1); - before_observer_details_.ExpectEquals(folder1, folder2, 0, 0, false); - observer_details_.ExpectEquals(folder1, folder2, 0, 0, false); EXPECT_EQ(bookmark_bar_node->children().size(), 2u); EXPECT_EQ(bookmark_bar_node->children()[0].get(), folder1); EXPECT_EQ(bookmark_bar_node->children()[1].get(), folder2); @@ -1471,17 +1412,16 @@ /*meta_info=*/nullptr, /*creation_time=*/std::nullopt, kInitialUuid); - ClearCounts(); + testing::InSequence seq; + EXPECT_CALL(mock_observer(), + OnWillMoveBookmarkNode(bookmark_bar_node, 0, + account_bookmark_bar_node, 0)); + EXPECT_CALL(mock_observer(), BookmarkNodeMoved(bookmark_bar_node, 0, + account_bookmark_bar_node, 0)); model_->Move(folder1, account_bookmark_bar_node, 0); // Should update the hierarchy. - AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0, - /*before_moved_count=*/1); - before_observer_details_.ExpectEquals(bookmark_bar_node, - account_bookmark_bar_node, 0, 0, false); - observer_details_.ExpectEquals(bookmark_bar_node, account_bookmark_bar_node, - 0, 0, false); EXPECT_EQ(bookmark_bar_node->children().size(), 0u); EXPECT_EQ(account_bookmark_bar_node->children().size(), 2u); @@ -1525,19 +1465,18 @@ /*meta_info=*/nullptr, /*creation_time=*/std::nullopt, kInitialUuid); - ClearCounts(); + testing::InSequence seq; + EXPECT_CALL(mock_observer(), + OnWillMoveBookmarkNode(bookmark_bar_node, 0, + account_bookmark_bar_node, 0)); + EXPECT_CALL(mock_observer(), BookmarkNodeMoved(bookmark_bar_node, 0, + account_bookmark_bar_node, 0)); // Move `folder1`, which also includes the nested `folder2`, to account // storage. model_->Move(folder1, account_bookmark_bar_node, 0); // Should update the hierarchy. - AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0, - /*before_moved_count=*/1); - before_observer_details_.ExpectEquals(bookmark_bar_node, - account_bookmark_bar_node, 0, 0, false); - observer_details_.ExpectEquals(bookmark_bar_node, account_bookmark_bar_node, - 0, 0, false); EXPECT_EQ(bookmark_bar_node->children().size(), 0u); EXPECT_EQ(account_bookmark_bar_node->children().size(), 2u); @@ -1913,7 +1852,7 @@ // Expect first a callback that the folder is visible, then that the child // bookmark is added. - testing::InSequence enforce_callback_order; + testing::InSequence seq; EXPECT_CALL(observer, BookmarkPermanentNodeVisibilityChanged(permanent_folder)) .WillOnce([](const BookmarkPermanentNode* node) { @@ -1946,7 +1885,7 @@ // Expect first a callback that the folder is visible, then that the child // bookmark is added. - testing::InSequence enforce_callback_order; + testing::InSequence seq; EXPECT_CALL(observer, BookmarkPermanentNodeVisibilityChanged(model_->other_node())) .WillOnce([this](const BookmarkPermanentNode* node) { @@ -1997,7 +1936,7 @@ // Expect first a callback that the folder is visible, then that the child // bookmark is added. - testing::InSequence enforce_callback_order; + testing::InSequence seq; EXPECT_CALL(observer, BookmarkPermanentNodeVisibilityChanged(model_->mobile_node())) .WillOnce([this](const BookmarkPermanentNode* node) { @@ -2065,7 +2004,7 @@ // Expect first a callback that the folder is visible, then that the child // bookmark is added. - testing::InSequence enforce_callback_order; + testing::InSequence seq; EXPECT_CALL(observer, OnWillRemoveBookmarks(_, _, bookmark, _)); EXPECT_CALL(observer, BookmarkNodeRemoved(_, _, bookmark, _, _)) .WillOnce(WithArg<2>( @@ -2101,7 +2040,7 @@ // Expect first a callback that the folder is visible, then that the child // bookmark is added. - testing::InSequence enforce_callback_order; + testing::InSequence seq; EXPECT_CALL(observer, OnWillMoveBookmarkNode(source_folder, 0, model_->bookmark_bar_node(), 0)); EXPECT_CALL(observer, BookmarkNodeMoved(source_folder, 0, @@ -2136,7 +2075,7 @@ // Expect first a callback that the folder is visible, then that the child // bookmark is added. - testing::InSequence enforce_callback_order; + testing::InSequence seq; EXPECT_CALL(observer, OnWillMoveBookmarkNode(source_folder, 0, destination_folder, 0)); EXPECT_CALL(observer, @@ -2184,7 +2123,7 @@ // Expect first a callback that the folder is visible, then that the child // bookmark is added. - testing::InSequence enforce_callback_order; + testing::InSequence seq; EXPECT_CALL(observer, OnWillMoveBookmarkNode(source_folder, 0, destination_folder, 0)); EXPECT_CALL(observer, @@ -2219,7 +2158,7 @@ base::ScopedObservation<BookmarkModel, BookmarkModelObserver> observation( &observer); observation.Observe(model_.get()); - testing::InSequence enforce_callback_order; + testing::InSequence seq; // First expect callbacks for the previously-visible local permanent folders. EXPECT_CALL(observer, BookmarkPermanentNodeVisibilityChanged( @@ -2290,7 +2229,7 @@ base::ScopedObservation<BookmarkModel, BookmarkModelObserver> observation( &observer); observation.Observe(model_.get()); - testing::InSequence enforce_callback_order; + testing::InSequence seq; // Then expect callbacks for the previously-invisible local permanent // folders. @@ -2352,9 +2291,12 @@ base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( bookmarks::kAllBookmarksBaselineFolderVisibility); + model_->RemoveObserver(&mock_observer()); model_->RemoveObserver(this); model_ = TestBookmarkClient::CreateModelWithClient( std::make_unique<TestBookmarkClientWithUndo>()); + model_->AddObserver(this); + model_->AddObserver(&mock_observer()); #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) EXPECT_FALSE(model_->bookmark_bar_node()->IsVisible()); #else
diff --git a/components/bookmarks/test/mock_bookmark_model_observer.h b/components/bookmarks/test/mock_bookmark_model_observer.h index 1f622e2e..8771da6 100644 --- a/components/bookmarks/test/mock_bookmark_model_observer.h +++ b/components/bookmarks/test/mock_bookmark_model_observer.h
@@ -61,6 +61,12 @@ BookmarkAllUserNodesRemoved, (const std::set<GURL>&, const base::Location&)); + MOCK_METHOD(void, OnWillRemoveAllUserBookmarks, (const base::Location&)); + + MOCK_METHOD(void, ExtensiveBookmarkChangesBeginning, ()); + + MOCK_METHOD(void, ExtensiveBookmarkChangesEnded, ()); + MOCK_METHOD(void, GroupedBookmarkChangesBeginning, ()); MOCK_METHOD(void, GroupedBookmarkChangesEnded, ());
diff --git a/components/collaboration/internal/android/collaboration_service_android.cc b/components/collaboration/internal/android/collaboration_service_android.cc index d6eadfb..7d53493a 100644 --- a/components/collaboration/internal/android/collaboration_service_android.cc +++ b/components/collaboration/internal/android/collaboration_service_android.cc
@@ -22,7 +22,6 @@ #include "components/collaboration/public/core_jni_headers/ServiceStatus_jni.h" using base::android::AttachCurrentThread; -using base::android::ConvertJavaStringToUTF8; using base::android::JavaRef; using base::android::ScopedJavaGlobalRef; using base::android::ScopedJavaLocalRef; @@ -84,11 +83,11 @@ void CollaborationServiceAndroid::StartShareOrManageFlow( JNIEnv* env, int64_t delegateNativePtr, - const JavaRef<jstring>& j_sync_group_id, + const std::string& sync_group_id, const JavaRef<jobject>& j_local_group_id, int32_t entry) { tab_groups::EitherGroupID either_id = - tab_groups::JavaSyncOrLocalGroupIdToEitherGroupId(env, j_sync_group_id, + tab_groups::JavaSyncOrLocalGroupIdToEitherGroupId(env, sync_group_id, j_local_group_id); collaboration_service_->StartShareOrManageFlow( @@ -99,11 +98,11 @@ void CollaborationServiceAndroid::StartLeaveOrDeleteFlow( JNIEnv* env, int64_t delegateNativePtr, - const JavaRef<jstring>& j_sync_group_id, + const std::string& sync_group_id, const JavaRef<jobject>& j_local_group_id, int32_t entry) { tab_groups::EitherGroupID either_id = - tab_groups::JavaSyncOrLocalGroupIdToEitherGroupId(env, j_sync_group_id, + tab_groups::JavaSyncOrLocalGroupIdToEitherGroupId(env, sync_group_id, j_local_group_id); collaboration_service_->StartLeaveOrDeleteFlow( @@ -123,19 +122,18 @@ int32_t CollaborationServiceAndroid::GetCurrentUserRoleForGroup( JNIEnv* env, - const JavaRef<jstring>& group_id) { + const std::string& group_id) { data_sharing::MemberRole role = - collaboration_service_->GetCurrentUserRoleForGroup( - GroupId(ConvertJavaStringToUTF8(env, group_id))); + collaboration_service_->GetCurrentUserRoleForGroup(GroupId(group_id)); return static_cast<int32_t>(role); } jni_zero::ScopedJavaLocalRef<jobject> CollaborationServiceAndroid::GetGroupData( JNIEnv* env, - const base::android::JavaRef<jstring>& group_id) { - const std::optional<GroupData> data = collaboration_service_->GetGroupData( - GroupId(ConvertJavaStringToUTF8(env, group_id))); + const std::string& group_id) { + const std::optional<GroupData> data = + collaboration_service_->GetGroupData(GroupId(group_id)); if (!data.has_value()) { return nullptr; } @@ -145,20 +143,20 @@ void CollaborationServiceAndroid::LeaveGroup( JNIEnv* env, - const JavaRef<jstring>& group_id, + const std::string& group_id, const JavaRef<jobject>& j_callback) { collaboration_service_->LeaveGroup( - GroupId(ConvertJavaStringToUTF8(env, group_id)), + GroupId(group_id), base::BindOnce(&base::android::RunBooleanCallbackAndroid, ScopedJavaGlobalRef<jobject>(j_callback))); } void CollaborationServiceAndroid::DeleteGroup( JNIEnv* env, - const JavaRef<jstring>& group_id, + const std::string& group_id, const JavaRef<jobject>& j_callback) { collaboration_service_->DeleteGroup( - GroupId(ConvertJavaStringToUTF8(env, group_id)), + GroupId(group_id), base::BindOnce(&base::android::RunBooleanCallbackAndroid, ScopedJavaGlobalRef<jobject>(j_callback))); }
diff --git a/components/collaboration/internal/android/collaboration_service_android.h b/components/collaboration/internal/android/collaboration_service_android.h index fa5c8d6..19fe16b3 100644 --- a/components/collaboration/internal/android/collaboration_service_android.h +++ b/components/collaboration/internal/android/collaboration_service_android.h
@@ -5,6 +5,8 @@ #ifndef COMPONENTS_COLLABORATION_INTERNAL_ANDROID_COLLABORATION_SERVICE_ANDROID_H_ #define COMPONENTS_COLLABORATION_INTERNAL_ANDROID_COLLABORATION_SERVICE_ANDROID_H_ +#include <string> + #include "base/android/jni_android.h" #include "base/android/scoped_java_ref.h" #include "base/memory/raw_ptr.h" @@ -30,27 +32,25 @@ void StartShareOrManageFlow( JNIEnv* env, int64_t delegate, - const base::android::JavaRef<jstring>& j_sync_group_id, + const std::string& sync_group_id, const base::android::JavaRef<jobject>& j_local_group_id, int32_t entry); void StartLeaveOrDeleteFlow( JNIEnv* env, int64_t delegate, - const base::android::JavaRef<jstring>& j_sync_group_id, + const std::string& sync_group_id, const base::android::JavaRef<jobject>& j_local_group_id, int32_t entry); base::android::ScopedJavaLocalRef<jobject> GetServiceStatus(JNIEnv* env); - int32_t GetCurrentUserRoleForGroup( - JNIEnv* env, - const base::android::JavaRef<jstring>& group_id); + int32_t GetCurrentUserRoleForGroup(JNIEnv* env, const std::string& group_id); jni_zero::ScopedJavaLocalRef<jobject> GetGroupData( JNIEnv* env, - const base::android::JavaRef<jstring>& group_id); + const std::string& group_id); void LeaveGroup(JNIEnv* env, - const base::android::JavaRef<jstring>& group_id, + const std::string& group_id, const base::android::JavaRef<jobject>& j_callback); void DeleteGroup(JNIEnv* env, - const base::android::JavaRef<jstring>& group_id, + const std::string& group_id, const base::android::JavaRef<jobject>& j_callback); // Returns the CollaborationServiceImpl java object.
diff --git a/components/collaboration/internal/android/comments/comments_service_bridge.cc b/components/collaboration/internal/android/comments/comments_service_bridge.cc index eb012b5..6281e2b 100644 --- a/components/collaboration/internal/android/comments/comments_service_bridge.cc +++ b/components/collaboration/internal/android/comments/comments_service_bridge.cc
@@ -14,7 +14,7 @@ #include "base/memory/ptr_util.h" #include "base/uuid.h" #include "components/collaboration/public/comments/comments_service.h" -#include "components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h" +#include "third_party/jni_zero/default_conversions.h" #include "url/android/gurl_android.h" #include "url/gurl.h" @@ -22,7 +22,6 @@ #include "components/collaboration/internal/comments_jni_headers/CommentsServiceBridge_jni.h" using base::android::AttachCurrentThread; -using base::android::ConvertJavaStringToUTF8; using base::android::JavaRef; using base::android::RunBooleanCallbackAndroid; using base::android::ScopedJavaGlobalRef; @@ -70,70 +69,51 @@ return ScopedJavaLocalRef<jobject>(java_ref_); } -bool CommentsServiceBridge::IsInitialized(JNIEnv* env, - const JavaRef<jobject>& j_caller) { +bool CommentsServiceBridge::IsInitialized(JNIEnv* env) { return service_->IsInitialized(); } -bool CommentsServiceBridge::IsEmptyService(JNIEnv* env, - const JavaRef<jobject>& j_caller) { +bool CommentsServiceBridge::IsEmptyService(JNIEnv* env) { return service_->IsEmptyService(); } -ScopedJavaLocalRef<jstring> CommentsServiceBridge::AddComment( +base::Uuid CommentsServiceBridge::AddComment( JNIEnv* env, - const JavaRef<jobject>& j_caller, - const JavaRef<jstring>& j_collaboration_id, - const JavaRef<jobject>& j_url, - const JavaRef<jstring>& j_content, - const JavaRef<jstring>& j_parent_comment_id, + const std::string& collaboration_id, + const GURL& url, + const std::string& content, + const std::optional<base::Uuid>& parent_comment_id, const JavaRef<jobject>& j_success_callback) { - std::optional<base::Uuid> parent_comment_id; - if (j_parent_comment_id) { - parent_comment_id = tab_groups::JavaStringToUuid(env, j_parent_comment_id); - } - auto callback = base::BindOnce(&RunBooleanCallbackAndroid, ScopedJavaGlobalRef<jobject>(j_success_callback)); - base::Uuid new_comment_id = service_->AddComment( - GroupId(ConvertJavaStringToUTF8(env, j_collaboration_id)), - url::GURLAndroid::ToNativeGURL(env, j_url), - ConvertJavaStringToUTF8(env, j_content), parent_comment_id, - std::move(callback)); - - return tab_groups::UuidToJavaString(env, new_comment_id); + return service_->AddComment(GroupId(collaboration_id), url, content, + parent_comment_id, std::move(callback)); } void CommentsServiceBridge::EditComment( JNIEnv* env, - const JavaRef<jobject>& j_caller, - const JavaRef<jstring>& j_comment_id, - const JavaRef<jstring>& j_new_content, + const base::Uuid& comment_id, + const std::string& new_content, const JavaRef<jobject>& j_success_callback) { auto callback = base::BindOnce(&RunBooleanCallbackAndroid, ScopedJavaGlobalRef<jobject>(j_success_callback)); - service_->EditComment(tab_groups::JavaStringToUuid(env, j_comment_id), - ConvertJavaStringToUTF8(env, j_new_content), - std::move(callback)); + service_->EditComment(comment_id, new_content, std::move(callback)); } void CommentsServiceBridge::DeleteComment( JNIEnv* env, - const JavaRef<jobject>& j_caller, - const JavaRef<jstring>& j_comment_id, + const base::Uuid& comment_id, const JavaRef<jobject>& j_success_callback) { auto callback = base::BindOnce(&RunBooleanCallbackAndroid, ScopedJavaGlobalRef<jobject>(j_success_callback)); - service_->DeleteComment(tab_groups::JavaStringToUuid(env, j_comment_id), - std::move(callback)); + service_->DeleteComment(comment_id, std::move(callback)); } void CommentsServiceBridge::QueryComments( JNIEnv* env, - const JavaRef<jobject>& j_caller, const JavaRef<jobject>& j_filter_criteria, const JavaRef<jobject>& j_pagination_criteria, const JavaRef<jobject>& j_callback) { @@ -142,14 +122,12 @@ void CommentsServiceBridge::AddObserver( JNIEnv* env, - const JavaRef<jobject>& j_caller, const JavaRef<jobject>& j_observer, const JavaRef<jobject>& j_filter_criteria) { // TODO(crbug.com/435005417): Implement this. } void CommentsServiceBridge::RemoveObserver(JNIEnv* env, - const JavaRef<jobject>& j_caller, const JavaRef<jobject>& j_observer) { // TODO(crbug.com/435005417): Implement this. }
diff --git a/components/collaboration/internal/android/comments/comments_service_bridge.h b/components/collaboration/internal/android/comments/comments_service_bridge.h index 20e7d164..6d95f22 100644 --- a/components/collaboration/internal/android/comments/comments_service_bridge.h +++ b/components/collaboration/internal/android/comments/comments_service_bridge.h
@@ -7,6 +7,7 @@ #include "base/android/scoped_java_ref.h" #include "base/supports_user_data.h" +#include "base/uuid.h" #include "components/collaboration/public/comments/comments_service.h" namespace collaboration::comments::android { @@ -25,39 +26,31 @@ CommentsService* service); // Methods called from Java via JNI. - bool IsInitialized(JNIEnv* env, - const base::android::JavaRef<jobject>& j_caller); - bool IsEmptyService(JNIEnv* env, - const base::android::JavaRef<jobject>& j_caller); - base::android::ScopedJavaLocalRef<jstring> AddComment( + bool IsInitialized(JNIEnv* env); + bool IsEmptyService(JNIEnv* env); + base::Uuid AddComment( JNIEnv* env, - const base::android::JavaRef<jobject>& j_caller, - const base::android::JavaRef<jstring>& j_collaboration_id, - const base::android::JavaRef<jobject>& j_url, - const base::android::JavaRef<jstring>& j_content, - const base::android::JavaRef<jstring>& j_parent_comment_id, + const std::string& collaboration_id, + const GURL& url, + const std::string& content, + const std::optional<base::Uuid>& parent_comment_id, const base::android::JavaRef<jobject>& j_success_callback); void EditComment(JNIEnv* env, - const base::android::JavaRef<jobject>& j_caller, - const base::android::JavaRef<jstring>& j_comment_id, - const base::android::JavaRef<jstring>& j_new_content, + const base::Uuid& comment_id, + const std::string& new_content, const base::android::JavaRef<jobject>& j_success_callback); void DeleteComment(JNIEnv* env, - const base::android::JavaRef<jobject>& j_caller, - const base::android::JavaRef<jstring>& j_comment_id, + const base::Uuid& comment_id, const base::android::JavaRef<jobject>& j_success_callback); void QueryComments( JNIEnv* env, - const base::android::JavaRef<jobject>& j_caller, const base::android::JavaRef<jobject>& j_filter_criteria, const base::android::JavaRef<jobject>& j_pagination_criteria, const base::android::JavaRef<jobject>& j_callback); void AddObserver(JNIEnv* env, - const base::android::JavaRef<jobject>& j_caller, const base::android::JavaRef<jobject>& j_observer, const base::android::JavaRef<jobject>& j_filter_criteria); void RemoveObserver(JNIEnv* env, - const base::android::JavaRef<jobject>& j_caller, const base::android::JavaRef<jobject>& j_observer); // Returns the companion Java object for this bridge.
diff --git a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java index 7c383a8..847e359 100644 --- a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java +++ b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java
@@ -6,6 +6,7 @@ import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.base.Callback; @@ -141,29 +142,35 @@ void startShareOrManageFlow( long nativeCollaborationServiceAndroid, long delegateNativePtr, - @Nullable String syncId, + @JniType("std::string") @Nullable String syncId, @Nullable LocalTabGroupId localId, int entry); void startLeaveOrDeleteFlow( long nativeCollaborationServiceAndroid, long delegateNativePtr, - @Nullable String syncId, + @JniType("std::string") @Nullable String syncId, @Nullable LocalTabGroupId localId, int entry); ServiceStatus getServiceStatus(long nativeCollaborationServiceAndroid); int getCurrentUserRoleForGroup( - long nativeCollaborationServiceAndroid, @Nullable String collaborationId); + long nativeCollaborationServiceAndroid, + @JniType("std::string") @Nullable String collaborationId); GroupData getGroupData( - long nativeCollaborationServiceAndroid, @Nullable String collaborationId); + long nativeCollaborationServiceAndroid, + @JniType("std::string") @Nullable String collaborationId); void leaveGroup( - long nativeCollaborationServiceAndroid, String groupId, Callback<Boolean> callback); + long nativeCollaborationServiceAndroid, + @JniType("std::string") String groupId, + Callback<Boolean> callback); void deleteGroup( - long nativeCollaborationServiceAndroid, String groupId, Callback<Boolean> callback); + long nativeCollaborationServiceAndroid, + @JniType("std::string") String groupId, + Callback<Boolean> callback); } }
diff --git a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/comments/CommentsServiceBridge.java b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/comments/CommentsServiceBridge.java index 923d301d..06bc4a6 100644 --- a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/comments/CommentsServiceBridge.java +++ b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/comments/CommentsServiceBridge.java
@@ -6,6 +6,7 @@ import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.base.Callback; @@ -38,12 +39,12 @@ @Override public boolean isInitialized() { - return CommentsServiceBridgeJni.get().isInitialized(mNativeCommentsServiceBridge, this); + return CommentsServiceBridgeJni.get().isInitialized(mNativeCommentsServiceBridge); } @Override public boolean isEmptyService() { - return CommentsServiceBridgeJni.get().isEmptyService(mNativeCommentsServiceBridge, this); + return CommentsServiceBridgeJni.get().isEmptyService(mNativeCommentsServiceBridge); } @Override @@ -53,17 +54,16 @@ String content, @Nullable UUID parentCommentId, Callback<Boolean> successCallback) { - String uuid = + String uuidString = CommentsServiceBridgeJni.get() .addComment( mNativeCommentsServiceBridge, - this, collaborationId, url, content, - parentCommentId == null ? "" : parentCommentId.toString(), + parentCommentId == null ? null : parentCommentId.toString(), successCallback); - return UUID.fromString(uuid); + return UUID.fromString(uuidString); } @Override @@ -71,7 +71,6 @@ CommentsServiceBridgeJni.get() .editComment( mNativeCommentsServiceBridge, - this, commentId.toString(), newContent, successCallback); @@ -80,8 +79,7 @@ @Override public void deleteComment(UUID commentId, Callback<Boolean> successCallback) { CommentsServiceBridgeJni.get() - .deleteComment( - mNativeCommentsServiceBridge, this, commentId.toString(), successCallback); + .deleteComment(mNativeCommentsServiceBridge, commentId.toString(), successCallback); } @Override @@ -91,11 +89,7 @@ Callback<CommentsService.QueryResult> callback) { CommentsServiceBridgeJni.get() .queryComments( - mNativeCommentsServiceBridge, - this, - filterCriteria, - paginationCriteria, - callback); + mNativeCommentsServiceBridge, filterCriteria, paginationCriteria, callback); } @Override @@ -103,58 +97,52 @@ CommentsService.CommentsObserver observer, CommentsService.FilterCriteria filterCriteria) { CommentsServiceBridgeJni.get() - .addObserver(mNativeCommentsServiceBridge, this, observer, filterCriteria); + .addObserver(mNativeCommentsServiceBridge, observer, filterCriteria); } @Override public void removeObserver(CommentsService.CommentsObserver observer) { - CommentsServiceBridgeJni.get().removeObserver(mNativeCommentsServiceBridge, this, observer); + CommentsServiceBridgeJni.get().removeObserver(mNativeCommentsServiceBridge, observer); } @NativeMethods interface Natives { - boolean isInitialized(long nativeCommentsServiceBridge, CommentsServiceBridge caller); + boolean isInitialized(long nativeCommentsServiceBridge); - boolean isEmptyService(long nativeCommentsServiceBridge, CommentsServiceBridge caller); + boolean isEmptyService(long nativeCommentsServiceBridge); + @JniType("base::Uuid") String addComment( long nativeCommentsServiceBridge, - CommentsServiceBridge caller, - String collaborationId, - GURL url, - String content, - String parentCommentId, + @JniType("std::string") String collaborationId, + @JniType("GURL") GURL url, + @JniType("std::string") String content, + @JniType("std::optional<base::Uuid>") @Nullable String parentCommentId, Callback<Boolean> successCallback); void editComment( long nativeCommentsServiceBridge, - CommentsServiceBridge caller, - String commentId, - String newContent, + @JniType("base::Uuid") String commentId, + @JniType("std::string") String newContent, Callback<Boolean> successCallback); void deleteComment( long nativeCommentsServiceBridge, - CommentsServiceBridge caller, - String commentId, + @JniType("base::Uuid") String commentId, Callback<Boolean> successCallback); void queryComments( long nativeCommentsServiceBridge, - CommentsServiceBridge caller, CommentsService.FilterCriteria filterCriteria, CommentsService.PaginationCriteria paginationCriteria, Callback<CommentsService.QueryResult> callback); void addObserver( long nativeCommentsServiceBridge, - CommentsServiceBridge caller, CommentsService.CommentsObserver observer, CommentsService.FilterCriteria filterCriteria); void removeObserver( - long nativeCommentsServiceBridge, - CommentsServiceBridge caller, - CommentsService.CommentsObserver observer); + long nativeCommentsServiceBridge, CommentsService.CommentsObserver observer); } }
diff --git a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/messaging/MessagingBackendServiceBridge.java b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/messaging/MessagingBackendServiceBridge.java index ddecadd..07f92567 100644 --- a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/messaging/MessagingBackendServiceBridge.java +++ b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/messaging/MessagingBackendServiceBridge.java
@@ -6,6 +6,7 @@ import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.base.ObserverList; @@ -206,30 +207,32 @@ List<PersistentMessage> getMessagesForTab( long nativeMessagingBackendServiceBridge, int localTabId, - @Nullable String syncTabId, + @JniType("std::optional<base::Uuid>") @Nullable String syncTabId, @PersistentNotificationType int type); List<PersistentMessage> getMessagesForGroup( long nativeMessagingBackendServiceBridge, @Nullable LocalTabGroupId localGroupId, - @Nullable String syncGroupId, + @JniType("std::optional<base::Uuid>") @Nullable String syncGroupId, @PersistentNotificationType int type); List<PersistentMessage> getMessages( long nativeMessagingBackendServiceBridge, @PersistentNotificationType int type); List<ActivityLogItem> getActivityLog( - long nativeMessagingBackendServiceBridge, String collaborationId); + long nativeMessagingBackendServiceBridge, + @JniType("std::string") String collaborationId); void clearDirtyTabMessagesForGroup( - long nativeMessagingBackendServiceBridge, String collaborationId); + long nativeMessagingBackendServiceBridge, + @JniType("std::string") String collaborationId); void runInstantaneousMessageSuccessCallback( long nativeMessagingBackendServiceBridge, long callback, boolean success); void clearPersistentMessage( long nativeMessagingBackendServiceBridge, - String messageId, + @JniType("base::Uuid") String messageId, @PersistentNotificationType int type); } }
diff --git a/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.cc b/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.cc index d70a26c8..a77b89b 100644 --- a/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.cc +++ b/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.cc
@@ -9,7 +9,6 @@ #include <set> #include "base/android/jni_android.h" -#include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "base/functional/callback.h" @@ -21,12 +20,11 @@ #include "components/collaboration/public/messaging/message.h" #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_bridge.h" #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h" +#include "third_party/jni_zero/default_conversions.h" // Must come after all headers that specialize FromJniType() / ToJniType(). #include "components/collaboration/internal/messaging_jni_headers/MessagingBackendServiceBridge_jni.h" -using base::android::ConvertJavaStringToUTF16; - namespace collaboration::messaging::android { namespace { const char kMessagingBackendServiceBridgeUserDataKey[] = @@ -89,22 +87,19 @@ MessagingBackendServiceBridge::GetMessagesForTab( JNIEnv* env, int32_t j_local_tab_id, - const base::android::JavaRef<jstring>& j_sync_tab_id, + const std::optional<base::Uuid>& sync_tab_id, int32_t j_type) { auto type = static_cast<PersistentNotificationType>(j_type); if (j_local_tab_id != kInvalidTabId) { - CHECK(!j_sync_tab_id); + CHECK(!sync_tab_id); tab_groups::LocalTabID tab_id = tab_groups::FromJavaTabId(j_local_tab_id); auto messages = service_->GetMessagesForTab(tab_id, type); return PersistentMessagesToJava(env, messages); } - if (j_sync_tab_id) { + if (sync_tab_id) { CHECK(j_local_tab_id == kInvalidTabId); - std::string sync_tab_id_str = - base::android::ConvertJavaStringToUTF8(env, j_sync_tab_id); - auto tab_id = base::Uuid::ParseLowercase(sync_tab_id_str); - auto messages = service_->GetMessagesForTab(tab_id, type); + auto messages = service_->GetMessagesForTab(*sync_tab_id, type); return PersistentMessagesToJava(env, messages); } @@ -115,24 +110,21 @@ MessagingBackendServiceBridge::GetMessagesForGroup( JNIEnv* env, const base::android::JavaRef<jobject>& j_local_group_id, - const base::android::JavaRef<jstring>& j_sync_group_id, + const std::optional<base::Uuid>& sync_group_id, int32_t j_type) { auto type = static_cast<PersistentNotificationType>(j_type); if (j_local_group_id) { - CHECK(!j_sync_group_id); + CHECK(!sync_group_id); auto group_id = tab_groups::TabGroupSyncConversionsBridge::FromJavaTabGroupId( env, j_local_group_id); auto messages = service_->GetMessagesForGroup(group_id, type); return PersistentMessagesToJava(env, messages); } - if (j_sync_group_id) { + if (sync_group_id) { CHECK(!j_local_group_id); - std::string sync_group_id_str = - base::android::ConvertJavaStringToUTF8(env, j_sync_group_id); - auto group_id = base::Uuid::ParseLowercase(sync_group_id_str); - auto messages = service_->GetMessagesForGroup(group_id, type); + auto messages = service_->GetMessagesForGroup(*sync_group_id, type); return PersistentMessagesToJava(env, messages); } @@ -149,29 +141,24 @@ base::android::ScopedJavaLocalRef<jobject> MessagingBackendServiceBridge::GetActivityLog( JNIEnv* env, - const base::android::JavaRef<jstring>& j_collaboration_id) { + const std::string& collaboration_id) { ActivityLogQueryParams query_params; - query_params.collaboration_id = data_sharing::GroupId( - base::android::ConvertJavaStringToUTF8(env, j_collaboration_id)); + query_params.collaboration_id = data_sharing::GroupId(collaboration_id); auto activity_log_items = service_->GetActivityLog(query_params); return ActivityLogItemsToJava(env, activity_log_items); } void MessagingBackendServiceBridge::ClearDirtyTabMessagesForGroup( JNIEnv* env, - const base::android::JavaRef<jstring>& j_collaboration_id) { - auto collaboration_id = data_sharing::GroupId( - base::android::ConvertJavaStringToUTF8(env, j_collaboration_id)); - service_->ClearDirtyTabMessagesForGroup(collaboration_id); + const std::string& collaboration_id) { + service_->ClearDirtyTabMessagesForGroup( + data_sharing::GroupId(collaboration_id)); } void MessagingBackendServiceBridge::ClearPersistentMessage( JNIEnv* env, - const base::android::JavaRef<jstring>& j_message_id, + const base::Uuid& message_id, int32_t j_type) { - CHECK(j_message_id); - auto message_id = base::Uuid::ParseLowercase( - base::android::ConvertJavaStringToUTF8(env, j_message_id)); auto type = static_cast<PersistentNotificationType>(j_type); service_->ClearPersistentMessage(message_id, type); }
diff --git a/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.h b/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.h index fd1e5724..e70b5498 100644 --- a/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.h +++ b/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.h
@@ -5,11 +5,14 @@ #ifndef COMPONENTS_COLLABORATION_INTERNAL_ANDROID_MESSAGING_MESSAGING_BACKEND_SERVICE_BRIDGE_H_ #define COMPONENTS_COLLABORATION_INTERNAL_ANDROID_MESSAGING_MESSAGING_BACKEND_SERVICE_BRIDGE_H_ +#include <optional> #include <set> +#include <string> #include "base/android/scoped_java_ref.h" #include "base/supports_user_data.h" #include "base/uuid.h" +#include "components/collaboration/internal/android/messaging/conversion_utils.h" #include "components/collaboration/public/messaging/messaging_backend_service.h" namespace collaboration::messaging::android { @@ -45,25 +48,23 @@ base::android::ScopedJavaLocalRef<jobject> GetMessagesForTab( JNIEnv* env, int32_t j_local_tab_id, - const base::android::JavaRef<jstring>& j_sync_tab_id, + const std::optional<base::Uuid>& sync_tab_id, int32_t j_type); base::android::ScopedJavaLocalRef<jobject> GetMessagesForGroup( JNIEnv* env, const base::android::JavaRef<jobject>& j_local_group_id, - const base::android::JavaRef<jstring>& j_sync_group_id, + const std::optional<base::Uuid>& sync_group_id, int32_t j_type); base::android::ScopedJavaLocalRef<jobject> GetMessages(JNIEnv* env, int32_t j_type); base::android::ScopedJavaLocalRef<jobject> GetActivityLog( JNIEnv* env, - const base::android::JavaRef<jstring>& j_collaboration_id); - void ClearDirtyTabMessagesForGroup( - JNIEnv* env, - const base::android::JavaRef<jstring>& j_collaboration_id); - void ClearPersistentMessage( - JNIEnv* env, - const base::android::JavaRef<jstring>& j_message_id, - int32_t j_type); + const std::string& collaboration_id); + void ClearDirtyTabMessagesForGroup(JNIEnv* env, + const std::string& collaboration_id); + void ClearPersistentMessage(JNIEnv* env, + const base::Uuid& message_id, + int32_t j_type); void RunInstantaneousMessageSuccessCallback(JNIEnv* env, int64_t j_callback,
diff --git a/components/commerce/core/android/java/src/org/chromium/components/commerce/core/ShoppingService.java b/components/commerce/core/android/java/src/org/chromium/components/commerce/core/ShoppingService.java index 13cacf92..7eecf2e2 100644 --- a/components/commerce/core/android/java/src/org/chromium/components/commerce/core/ShoppingService.java +++ b/components/commerce/core/android/java/src/org/chromium/components/commerce/core/ShoppingService.java
@@ -8,6 +8,7 @@ import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.base.Callback; @@ -602,11 +603,11 @@ int type, int idType, int managementType, - String id, - String seenOfferId, + @JniType("std::string") String id, + @JniType("std::string") String seenOfferId, long seenPrice, - String seenCountry, - String seenLocale, + @JniType("std::string") String seenCountry, + @JniType("std::string") String seenLocale, Callback<Boolean> callback); void unsubscribe( @@ -614,7 +615,7 @@ int type, int idType, int managementType, - String id, + @JniType("std::string") String id, Callback<Boolean> callback); void isSubscribed( @@ -622,7 +623,7 @@ int type, int idType, int managementType, - String id, + @JniType("std::string") String id, Callback<Boolean> callback); boolean isSubscribedFromCache( @@ -630,7 +631,7 @@ int type, int idType, int managementType, - String id); + @JniType("std::string") String id); void getAllPriceTrackedBookmarks( long nativeShoppingServiceAndroid, Callback<List<BookmarkId>> callback);
diff --git a/components/commerce/core/android/shopping_service_android.cc b/components/commerce/core/android/shopping_service_android.cc index 8d06cbe..ecdda65 100644 --- a/components/commerce/core/android/shopping_service_android.cc +++ b/components/commerce/core/android/shopping_service_android.cc
@@ -21,7 +21,6 @@ #include "components/commerce/core/android/core_jni/DiscountInfo_jni.h" #include "components/commerce/core/android/core_jni/ShoppingService_jni.h" -using base::android::ConvertJavaStringToUTF8; using base::android::ConvertUTF8ToJavaString; using base::android::RunBooleanCallbackAndroid; using base::android::ScopedJavaLocalRef; @@ -284,36 +283,31 @@ ScopedJavaGlobalRef<jobject>(j_callback))); } -void ShoppingServiceAndroid::FetchPriceEmailPref(JNIEnv* env) { +void ShoppingServiceAndroid::FetchPriceEmailPref() { CHECK(shopping_service_); shopping_service_->FetchPriceEmailPref(); } -void ShoppingServiceAndroid::ScheduleSavedProductUpdate(JNIEnv* env) { +void ShoppingServiceAndroid::ScheduleSavedProductUpdate() { CHECK(shopping_service_); shopping_service_->ScheduleSavedProductUpdate(); } -void ShoppingServiceAndroid::Subscribe(JNIEnv* env, - int32_t j_type, +void ShoppingServiceAndroid::Subscribe(int32_t j_type, int32_t j_id_type, int32_t j_management_type, - const JavaRef<jstring>& j_id, - const JavaRef<jstring>& j_seen_offer_id, - int64_t j_seen_price, - const JavaRef<jstring>& j_seen_country, - const JavaRef<jstring>& j_seen_locale, + const std::string& id, + const std::string& seen_offer_id, + int64_t seen_price, + const std::string& seen_country, + const std::string& seen_locale, const JavaRef<jobject>& j_callback) { - std::string id = ConvertJavaStringToUTF8(j_id); - std::string seen_offer_id = ConvertJavaStringToUTF8(j_seen_offer_id); - std::string seen_country = ConvertJavaStringToUTF8(j_seen_country); - std::string seen_locale = ConvertJavaStringToUTF8(j_seen_locale); CHECK(!id.empty()); auto user_seen_offer = std::make_optional<UserSeenOffer>( - seen_offer_id, j_seen_price, seen_country, seen_locale); + seen_offer_id, seen_price, seen_country, seen_locale); CommerceSubscription sub(SubscriptionType(j_type), IdentifierType(j_id_type), id, ManagementType(j_management_type), kUnknownSubscriptionTimestamp, @@ -328,13 +322,11 @@ shopping_service_->Subscribe(std::move(subs), std::move(callback)); } -void ShoppingServiceAndroid::Unsubscribe(JNIEnv* env, - int32_t j_type, +void ShoppingServiceAndroid::Unsubscribe(int32_t j_type, int32_t j_id_type, int32_t j_management_type, - const JavaRef<jstring>& j_id, + const std::string& id, const JavaRef<jobject>& j_callback) { - std::string id = ConvertJavaStringToUTF8(j_id); CHECK(!id.empty()); CommerceSubscription sub(SubscriptionType(j_type), IdentifierType(j_id_type), @@ -350,13 +342,11 @@ shopping_service_->Unsubscribe(std::move(subs), std::move(callback)); } -void ShoppingServiceAndroid::IsSubscribed(JNIEnv* env, - int32_t j_type, +void ShoppingServiceAndroid::IsSubscribed(int32_t j_type, int32_t j_id_type, int32_t j_management_type, - const JavaRef<jstring>& j_id, + const std::string& id, const JavaRef<jobject>& j_callback) { - std::string id = ConvertJavaStringToUTF8(j_id); CHECK(!id.empty()); CommerceSubscription sub(SubscriptionType(j_type), IdentifierType(j_id_type), @@ -372,13 +362,10 @@ ScopedJavaGlobalRef<jobject>(j_callback))); } -bool ShoppingServiceAndroid::IsSubscribedFromCache( - JNIEnv* env, - int32_t j_type, - int32_t j_id_type, - int32_t j_management_type, - const JavaRef<jstring>& j_id) { - std::string id = ConvertJavaStringToUTF8(j_id); +bool ShoppingServiceAndroid::IsSubscribedFromCache(int32_t j_type, + int32_t j_id_type, + int32_t j_management_type, + const std::string& id) { CHECK(!id.empty()); CommerceSubscription sub(SubscriptionType(j_type), IdentifierType(j_id_type), @@ -418,27 +405,27 @@ succeeded); } -bool ShoppingServiceAndroid::IsShoppingListEligible(JNIEnv* env) { +bool ShoppingServiceAndroid::IsShoppingListEligible() { CHECK(shopping_service_); return shopping_service_->IsShoppingListEligible(); } -bool ShoppingServiceAndroid::IsMerchantViewerEnabled(JNIEnv* env) { +bool ShoppingServiceAndroid::IsMerchantViewerEnabled() { CHECK(shopping_service_); return commerce::IsMerchantViewerEnabled( shopping_service_->GetAccountChecker()); } -bool ShoppingServiceAndroid::IsPriceInsightsEligible(JNIEnv* env) { +bool ShoppingServiceAndroid::IsPriceInsightsEligible() { CHECK(shopping_service_); return commerce::IsPriceInsightsEligible( shopping_service_->GetAccountChecker()); } -bool ShoppingServiceAndroid::IsDiscountEligibleToShowOnNavigation(JNIEnv* env) { +bool ShoppingServiceAndroid::IsDiscountEligibleToShowOnNavigation() { CHECK(shopping_service_); return commerce::IsDiscountEligibleToShowOnNavigation(
diff --git a/components/commerce/core/android/shopping_service_android.h b/components/commerce/core/android/shopping_service_android.h index 2adfa02..469c0a12 100644 --- a/components/commerce/core/android/shopping_service_android.h +++ b/components/commerce/core/android/shopping_service_android.h
@@ -60,51 +60,47 @@ const JavaRef<jobject>& j_gurl, const JavaRef<jobject>& j_callback); - void FetchPriceEmailPref(JNIEnv* env); + void FetchPriceEmailPref(); - void ScheduleSavedProductUpdate(JNIEnv* env); + void ScheduleSavedProductUpdate(); - void Subscribe(JNIEnv* env, - int32_t j_type, + void Subscribe(int32_t j_type, int32_t j_id_type, int32_t j_management_type, - const JavaRef<jstring>& j_id, - const JavaRef<jstring>& j_seen_offer_id, - int64_t j_seen_price, - const JavaRef<jstring>& j_seen_country, - const JavaRef<jstring>& j_seen_locale, + const std::string& id, + const std::string& seen_offer_id, + int64_t seen_price, + const std::string& seen_country, + const std::string& seen_locale, const JavaRef<jobject>& j_callback); - void Unsubscribe(JNIEnv* env, - int32_t j_type, + void Unsubscribe(int32_t j_type, int32_t j_id_type, int32_t j_management_type, - const JavaRef<jstring>& j_id, + const std::string& id, const JavaRef<jobject>& j_callback); - void IsSubscribed(JNIEnv* env, - int32_t j_type, + void IsSubscribed(int32_t j_type, int32_t j_id_type, int32_t j_management_type, - const JavaRef<jstring>& j_id, + const std::string& id, const JavaRef<jobject>& j_callback); - bool IsSubscribedFromCache(JNIEnv* env, - int32_t j_type, + bool IsSubscribedFromCache(int32_t j_type, int32_t j_id_type, int32_t j_management_type, - const JavaRef<jstring>& j_id); + const std::string& j_id); void GetAllPriceTrackedBookmarks(JNIEnv* env, const JavaRef<jobject>& j_callback); - bool IsShoppingListEligible(JNIEnv* env); + bool IsShoppingListEligible(); - bool IsMerchantViewerEnabled(JNIEnv* env); + bool IsMerchantViewerEnabled(); - bool IsPriceInsightsEligible(JNIEnv* env); + bool IsPriceInsightsEligible(); - bool IsDiscountEligibleToShowOnNavigation(JNIEnv* env); + bool IsDiscountEligibleToShowOnNavigation(); ScopedJavaGlobalRef<jobject> java_ref() { return java_ref_; }
diff --git a/components/contextual_search/contextual_search_service.cc b/components/contextual_search/contextual_search_service.cc index 1565c71..9c306ed 100644 --- a/components/contextual_search/contextual_search_service.cc +++ b/components/contextual_search/contextual_search_service.cc
@@ -54,6 +54,9 @@ // The IdentityManager may be destroyed before this service, so clear the // pointer to avoid dangling pointer issues. identity_manager_ = nullptr; + // Destroy sessions now to invalidate the WeakPtrs held by pending + // callbacks (e.g. the cluster-info reset timer in ComposeboxQueryController). + sessions_.clear(); } // static
diff --git a/components/contextual_search/internal/composebox_query_controller.cc b/components/contextual_search/internal/composebox_query_controller.cc index 2472d8f..7eec23db 100644 --- a/components/contextual_search/internal/composebox_query_controller.cc +++ b/components/contextual_search/internal/composebox_query_controller.cc
@@ -21,6 +21,7 @@ #include "base/task/thread_pool.h" #include "base/time/time.h" #include "components/contextual_tasks/public/features.h" +#include "components/contextual_tasks/public/utils.h" #include "components/lens/contextual_input.h" #include "components/lens/lens_features.h" #include "components/lens/lens_overlay_mime_type.h" @@ -140,6 +141,36 @@ namespace { +// The maximum number of times to retry fetching cluster info. +constexpr int kMaxClusterInfoRetries = 3; + +// The backoff policy for Contextual Tasks network requests. +constexpr net::BackoffEntry::Policy kClusterInfoBackoffPolicy = { + // Number of initial errors (in sequence) to ignore before applying + // exponential back-off rules. + 1, + + // Initial delay for exponential back-off in ms. + 500, + + // Factor by which the waiting time will be multiplied. + 2, + + // Fuzzing percentage. ex: 10% will spread requests randomly + // between 90%-100% of the calculated time. + 0.2, // 20% + + // Maximum amount of time we are willing to delay our request in ms. + 10000, // 10 seconds. + + // Time to keep an entry from being discarded even when it + // has no significant state, -1 to never discard. + -1, + + // Don't use initial delay unless the last request was an error. + false, +}; + // Creates a payload for a contextual data upload request, for webpage contents // or for uploaded pdf files. lens::Payload CreateContentextualDataUploadPayload( @@ -295,7 +326,8 @@ channel_(channel), locale_(locale), template_url_service_(template_url_service), - variations_client_(variations_client) { + variations_client_(variations_client), + cluster_info_backoff_(&kClusterInfoBackoffPolicy) { send_lns_surface_ = feature_params->send_lns_surface; suppress_lns_surface_param_if_no_image_ = feature_params->suppress_lns_surface_param_if_no_image; @@ -900,11 +932,31 @@ } } - // Update the file upload status to processing. UpdateFileUploadStatus(file_token, contextual_search::ContextUploadStatus::kProcessing, std::nullopt); + if (query_controller_state_ == QueryControllerState::kClusterInfoInvalid) { + // If we've exhausted retries or are still in the backoff period, fail the + // context upload immediately. + if (cluster_info_retries_ >= kMaxClusterInfoRetries) { + UpdateFileUploadStatus( + file_token, contextual_search::ContextUploadStatus::kUploadFailed, + contextual_search::ContextUploadErrorType::kServerError); + return; + } + + base::TimeDelta delay = cluster_info_backoff_.GetTimeUntilRelease(); + if (delay.is_positive()) { + UpdateFileUploadStatus( + file_token, contextual_search::ContextUploadStatus::kUploadFailed, + contextual_search::ContextUploadErrorType::kServerError); + return; + } + + FetchClusterInfo(); + } + // If the cluster info is available, update the file upload status to ready // for suggest. // If the file upload later fails due to @@ -1140,7 +1192,10 @@ cluster_info_access_token_fetcher_.reset(); cluster_info_endpoint_fetcher_.reset(); cluster_info_.reset(); + cluster_info_retries_ = 0; + cluster_info_backoff_.Reset(); request_id_generator_.ResetRequestId(); + SetQueryControllerState(QueryControllerState::kOff); } void ComposeboxQueryController::ResetRequestClusterInfoState() { @@ -1312,14 +1367,38 @@ void ComposeboxQueryController::HandleClusterInfoResponse( std::unique_ptr<endpoint_fetcher::EndpointResponse> response) { cluster_info_endpoint_fetcher_.reset(); + if (response->http_status_code != google_apis::ApiErrorCode::HTTP_SUCCESS) { + ++cluster_info_retries_; + + if (cluster_info_retries_ <= kMaxClusterInfoRetries) { + cluster_info_backoff_.InformOfRequest(false); + } + SetQueryControllerState(QueryControllerState::kClusterInfoInvalid); + + // Fail uploads that are waiting on cluster info. + std::vector<base::UnguessableToken> file_tokens_to_fail; + for (const auto& [file_token, file_info] : active_files_) { + if (file_info->upload_status == + contextual_search::ContextUploadStatus::kProcessing) { + file_tokens_to_fail.push_back(file_token); + } + } + for (const auto& file_token : file_tokens_to_fail) { + UpdateFileUploadStatus( + file_token, contextual_search::ContextUploadStatus::kUploadFailed, + contextual_search::ContextUploadErrorType::kServerError); + } + if (pending_search_url_request_) { std::move(pending_search_url_request_).Run(/*failure=*/false); } return; } + cluster_info_backoff_.Reset(); + lens::LensOverlayServerClusterInfoResponse server_response; if (!server_response.ParseFromString(response->response)) { SetQueryControllerState(QueryControllerState::kClusterInfoInvalid);
diff --git a/components/contextual_search/internal/composebox_query_controller.h b/components/contextual_search/internal/composebox_query_controller.h index 4eb9575..a3976031 100644 --- a/components/contextual_search/internal/composebox_query_controller.h +++ b/components/contextual_search/internal/composebox_query_controller.h
@@ -19,6 +19,7 @@ #include "components/endpoint_fetcher/endpoint_fetcher.h" #include "components/lens/lens_overlay_request_id_generator.h" #include "components/lens/proto/server/lens_overlay_response.pb.h" +#include "net/base/backoff_entry.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "third_party/lens_server_proto/added_inputs.pb.h" #include "third_party/lens_server_proto/aim_communication.pb.h" @@ -510,6 +511,9 @@ // The last received cluster info. std::optional<lens::LensOverlayClusterInfo> cluster_info_ = std::nullopt; + // The number of times fetching cluster info has failed. + int cluster_info_retries_ = 0; + // The endpoint fetcher used for the cluster info request. std::unique_ptr<endpoint_fetcher::EndpointFetcher> cluster_info_endpoint_fetcher_; @@ -544,6 +548,9 @@ // Owned by the Profile, and thus guaranteed to outlive this instance. const raw_ptr<variations::VariationsClient> variations_client_; + // Backoff entry used to control the retry logic for the cluster info request. + net::BackoffEntry cluster_info_backoff_; + // Whether or not to send the lns_surface parameter. // TODO(crbug.com/430070871): Remove this once the server supports the // `lns_surface` parameter.
diff --git a/components/contextual_search/internal/composebox_query_controller_unittest.cc b/components/contextual_search/internal/composebox_query_controller_unittest.cc index ddb113f..007d3e9 100644 --- a/components/contextual_search/internal/composebox_query_controller_unittest.cc +++ b/components/contextual_search/internal/composebox_query_controller_unittest.cc
@@ -287,7 +287,8 @@ ContextUploadStatus expected_status = ContextUploadStatus::kUploadSuccessful, std::optional<ContextUploadErrorType> expected_error_type = std::nullopt, - bool expect_suggest_signals_ready = true) { + bool expect_suggest_signals_ready = true, + bool expect_upload_started = true) { ContextUploadStatusTuple processing_file_upload_status = file_upload_status_future_.Take(); EXPECT_EQ(file_token, std::get<0>(processing_file_upload_status)); @@ -307,7 +308,8 @@ std::get<3>(processing_suggest_file_upload_status)); } - if (expected_status != ContextUploadStatus::kValidationFailed) { + if (expected_status != ContextUploadStatus::kValidationFailed && + expect_upload_started) { // For client-side validation failures, the state will never change to // kUploadStarted. ContextUploadStatusTuple upload_started_file_upload_status = @@ -326,8 +328,11 @@ EXPECT_EQ(expected_status, std::get<2>(final_file_upload_status)); EXPECT_EQ(expected_error_type, std::get<3>(final_file_upload_status)); - if (expected_status == ContextUploadStatus::kValidationFailed) { - // For client-side validation failures, the file upload request will not + if (expected_status == ContextUploadStatus::kValidationFailed || + (!expect_upload_started && + expected_status == ContextUploadStatus::kUploadFailed)) { + // For client-side validation failures or early upload failures + // (like failing to fetch cluster info), the file upload request will not // be sent. EXPECT_EQ(controller().num_file_upload_requests_sent(), 0); } else { @@ -622,6 +627,141 @@ /*expected_state=*/QueryControllerState::kClusterInfoInvalid); } +TEST_F(ComposeboxQueryControllerTest, ClusterInfoFailureFailsActiveUploads) { + // Arrange: Simulate an error in the cluster info request. + controller().set_next_cluster_info_request_should_return_error(true); + + // Act: Start the session. + controller().InitializeIfNeeded(); + + // Act: Start the file upload flow (this also triggers the cluster info + // fetch retry under the hood, but doesn't resolve immediately). + const base::UnguessableToken file_token = base::UnguessableToken::Create(); + StartPdfFileUploadFlow(file_token, + /*file_data=*/std::vector<uint8_t>()); + + // Assert: Validate cluster info request and state changes. + // The first fetch fails. + WaitForClusterInfo( + /*expected_state=*/QueryControllerState::kClusterInfoInvalid); + + // The active file upload should fail with kServerError since cluster info + // couldn't be fetched. + WaitForFileUpload( + file_token, lens::MimeType::kPdf, + /*expected_status=*/ContextUploadStatus::kUploadFailed, + /*expected_error_type=*/ContextUploadErrorType::kServerError, + /*expect_suggest_signals_ready=*/false, + /*expect_upload_started=*/false); +} + +TEST_F(ComposeboxQueryControllerTest, ClusterInfoFailureRetries) { + // Arrange: Simulate errors in the cluster info request. + controller().set_next_cluster_info_request_should_return_error(true); + + // Act: Start the session. + controller().InitializeIfNeeded(); + // Assert: Validate cluster info request and state changes. + // InitializeIfNeeded sets state to kAwaitingClusterInfoResponse then + // kClusterInfoInvalid. + WaitForClusterInfo( + /*expected_state=*/QueryControllerState::kClusterInfoInvalid, + /*fetch_request_count=*/1); + + // Act: Start file uploads to trigger retries. + for (int i = 2; i <= 3; ++i) { + controller().set_next_cluster_info_request_should_return_error(true); + task_environment().FastForwardBy(base::Seconds(10)); + const base::UnguessableToken file_token = base::UnguessableToken::Create(); + // StartPdfFileUploadFlow will check if state is kClusterInfoInvalid. + // If fewer than 5 retries, it calls FetchClusterInfo, which sets + // state to kAwaitingClusterInfoResponse. + StartPdfFileUploadFlow(file_token, /*file_data=*/std::vector<uint8_t>()); + + // Validate cluster info request retried. + WaitForClusterInfo( + /*expected_state=*/QueryControllerState::kClusterInfoInvalid, + /*fetch_request_count=*/i); + + // Validate the active file upload failed. + WaitForFileUpload( + file_token, lens::MimeType::kPdf, + /*expected_status=*/ContextUploadStatus::kUploadFailed, + /*expected_error_type=*/ContextUploadErrorType::kServerError, + /*expect_suggest_signals_ready=*/false, + /*expect_upload_started=*/false); + } + + // Act: Start another file upload. This time, no fetch should be attempted. + // Set the next fetch request to succeed, if it happens. + controller().set_next_cluster_info_request_should_return_error(false); + const base::UnguessableToken last_file_token = + base::UnguessableToken::Create(); + StartPdfFileUploadFlow(last_file_token, /*file_data=*/std::vector<uint8_t>()); + + // Validate the active file upload failed immediately. + WaitForFileUpload( + last_file_token, lens::MimeType::kPdf, + /*expected_status=*/ContextUploadStatus::kUploadFailed, + /*expected_error_type=*/ContextUploadErrorType::kServerError, + /*expect_suggest_signals_ready=*/false, + /*expect_upload_started=*/false); + + // Assert: The number of fetch requests should still be 3 + // (kMaxClusterInfoRetries). + EXPECT_EQ(controller().num_cluster_info_fetch_requests_sent(), 3); +} + +TEST_F(ComposeboxQueryControllerTest, ClearClusterInfoResetsRetries) { + // Arrange: Simulate an error in the cluster info request. + controller().set_next_cluster_info_request_should_return_error(true); + + // Act: Start the session. + controller().InitializeIfNeeded(); + + // Assert: Validate cluster info request and state changes. + WaitForClusterInfo( + /*expected_state=*/QueryControllerState::kClusterInfoInvalid, + /*fetch_request_count=*/1); + + // Act: Exhaust retries. + for (int i = 2; i <= 3; ++i) { + controller().set_next_cluster_info_request_should_return_error(true); + task_environment().FastForwardBy(base::Seconds(10)); + const base::UnguessableToken file_token = base::UnguessableToken::Create(); + StartPdfFileUploadFlow(file_token, /*file_data=*/std::vector<uint8_t>()); + + // Validate cluster info request retried. + WaitForClusterInfo( + /*expected_state=*/QueryControllerState::kClusterInfoInvalid, + /*fetch_request_count=*/i); + + // Validate the active file upload failed. + WaitForFileUpload( + file_token, lens::MimeType::kPdf, + /*expected_status=*/ContextUploadStatus::kUploadFailed, + /*expected_error_type=*/ContextUploadErrorType::kServerError, + /*expect_suggest_signals_ready=*/false, + /*expect_upload_started=*/false); + } + + // Clear query controller info. + controller().ClearClusterInfo(); + EXPECT_EQ(QueryControllerState::kOff, controller_state_future_.Take()); + + // Arrange: Simulate another error. + controller().set_next_cluster_info_request_should_return_error(true); + + // Start the session again. + // Since retries were reset, a new fetch should be issued and fail. + controller().InitializeIfNeeded(); + + // Assert: Validate cluster info request and state changes. + WaitForClusterInfo( + /*expected_state=*/QueryControllerState::kClusterInfoInvalid, + /*fetch_request_count=*/4); +} + TEST_F(ComposeboxQueryControllerTest, UploadFileRequestFailure) { // Act: Start the session. controller().InitializeIfNeeded(); @@ -1186,6 +1326,17 @@ std::get<2>(processing_file_upload_status)); EXPECT_EQ(std::nullopt, std::get<3>(processing_file_upload_status)); + // The active file upload should fail with kServerError since cluster info + // couldn't be fetched. + ContextUploadStatusTuple final_file_upload_status = + file_upload_status_future_.Take(); + EXPECT_EQ(file_token, std::get<0>(final_file_upload_status)); + EXPECT_EQ(lens::MimeType::kPdf, std::get<1>(final_file_upload_status)); + EXPECT_EQ(ContextUploadStatus::kUploadFailed, + std::get<2>(final_file_upload_status)); + EXPECT_EQ(ContextUploadErrorType::kServerError, + std::get<3>(final_file_upload_status)); + // Assert: file_upload_status_future_ is empty. EXPECT_TRUE(file_upload_status_future_.IsEmpty()); } @@ -1350,12 +1501,14 @@ WaitForFileUpload(file_token, lens::MimeType::kPdf); // Act: Fast forward time to expire the cluster info. - // The default cluster info lifetime is 1 hour. - task_environment().FastForwardBy(base::Hours(1) + base::Seconds(1)); + // The default cluster info lifetime is 30 minutes. + task_environment().FastForwardBy(base::Minutes(30) + base::Seconds(1)); // Assert: Validate cluster info request and state changes. // The cluster info should be re-fetched. - // First, the state becomes kClusterInfoInvalid. + // First, the cluster info is cleared (state becomes kOff). + EXPECT_EQ(QueryControllerState::kOff, controller_state_future_.Take()); + // Then, the state becomes kClusterInfoInvalid. EXPECT_EQ(QueryControllerState::kClusterInfoInvalid, controller_state_future_.Take()); // Then, it starts fetching and becomes kAwaitingClusterInfoResponse. @@ -1365,7 +1518,7 @@ EXPECT_EQ(QueryControllerState::kClusterInfoReceived, controller_state_future_.Take()); - EXPECT_GE(controller().num_cluster_info_fetch_requests_sent(), 2); + EXPECT_EQ(controller().num_cluster_info_fetch_requests_sent(), 2); // Assert: The file should still be in the active files map. ASSERT_TRUE(controller().GetFileInfoForTesting(file_token));
diff --git a/components/contextual_search/internal/test_composebox_query_controller.h b/components/contextual_search/internal/test_composebox_query_controller.h index c536069b..814ee33 100644 --- a/components/contextual_search/internal/test_composebox_query_controller.h +++ b/components/contextual_search/internal/test_composebox_query_controller.h
@@ -76,6 +76,8 @@ bool enable_cluster_info_ttl); ~TestComposeboxQueryController() override; + using ComposeboxQueryController::ClearClusterInfo; + // Mutators. void set_fake_cluster_info_response( lens::LensOverlayServerClusterInfoResponse response) {
diff --git a/components/contextual_tasks/internal/ai_thread_sync_bridge.cc b/components/contextual_tasks/internal/ai_thread_sync_bridge.cc index b3feb80..7092d5d 100644 --- a/components/contextual_tasks/internal/ai_thread_sync_bridge.cc +++ b/components/contextual_tasks/internal/ai_thread_sync_bridge.cc
@@ -5,6 +5,7 @@ #include "components/contextual_tasks/internal/ai_thread_sync_bridge.h" #include "base/functional/callback_helpers.h" +#include "components/sync/base/deletion_origin.h" #include "components/sync/model/data_batch.h" #include "components/sync/model/in_memory_metadata_change_list.h" #include "components/sync/model/mutable_data_batch.h" @@ -208,6 +209,26 @@ return threads; } +void AiThreadSyncBridge::DeleteThread(const Thread& thread) { + if (!change_processor()->IsTrackingMetadata()) { + return; + } + std::unique_ptr<syncer::DataTypeStore::WriteBatch> batch = + data_type_store_->CreateWriteBatch(); + + change_processor()->Delete(thread.server_id, + syncer::DeletionOrigin::Unspecified(), + batch->GetMetadataChangeList()); + + ai_thread_entities_.erase(thread.server_id); + batch->DeleteData(thread.server_id); + + data_type_store_->CommitWriteBatch( + std::move(batch), + base::BindOnce(&AiThreadSyncBridge::OnDataTypeStoreCommit, + weak_ptr_factory_.GetWeakPtr())); +} + void AiThreadSyncBridge::AddObserver(Observer* observer) { observers_.AddObserver(observer); }
diff --git a/components/contextual_tasks/internal/ai_thread_sync_bridge.h b/components/contextual_tasks/internal/ai_thread_sync_bridge.h index 8ebf3cc..e1a4cd8 100644 --- a/components/contextual_tasks/internal/ai_thread_sync_bridge.h +++ b/components/contextual_tasks/internal/ai_thread_sync_bridge.h
@@ -81,6 +81,9 @@ // Returns all threads. virtual std::vector<Thread> GetThreads() const; + // Delete the thread. + void DeleteThread(const Thread& thread); + void AddObserver(Observer* observer); void RemoveObserver(Observer* observer);
diff --git a/components/contextual_tasks/internal/ai_thread_sync_bridge_unittest.cc b/components/contextual_tasks/internal/ai_thread_sync_bridge_unittest.cc index 0e7ff55..dd73a772 100644 --- a/components/contextual_tasks/internal/ai_thread_sync_bridge_unittest.cc +++ b/components/contextual_tasks/internal/ai_thread_sync_bridge_unittest.cc
@@ -10,6 +10,7 @@ #include "base/test/task_environment.h" #include "components/sync/model/data_batch.h" #include "components/sync/model/data_type_store.h" +#include "components/sync/protocol/ai_thread_specifics.pb.h" #include "components/sync/test/data_type_store_test_util.h" #include "components/sync/test/mock_data_type_local_change_processor.h" #include "testing/gtest/include/gtest/gtest.h" @@ -18,6 +19,11 @@ namespace { +const char kInitServerId[] = "00000000-0000-0000-0000-00000000001"; +const char kInitConversationId[] = "init_conversation_id"; +const char kInitTitle[] = "init_title"; +const int64_t kInitLastTurnTimeUnixEpochMillis = 52; + using testing::_; using testing::ReturnRef; @@ -26,15 +32,62 @@ void SetUp() override { ON_CALL(mock_processor_, GetPossiblyTrimmedRemoteSpecifics(_)) .WillByDefault(ReturnRef(sync_pb::EntitySpecifics::default_instance())); + ON_CALL(mock_processor_, IsTrackingMetadata()) + .WillByDefault(testing::Return(true)); bridge_ = std::make_unique<AiThreadSyncBridge>( mock_processor_.CreateForwardingProcessor(), - syncer::DataTypeStoreTestUtil::FactoryForInMemoryStoreForTest()); + syncer::DataTypeStoreTestUtil::FactoryForForwardingStore(store_.get())); } protected: base::test::TaskEnvironment task_environment_; testing::NiceMock<syncer::MockDataTypeLocalChangeProcessor> mock_processor_; std::unique_ptr<AiThreadSyncBridge> bridge_; + std::unique_ptr<syncer::DataTypeStore> store_ = + syncer::DataTypeStoreTestUtil::CreateInMemoryStoreForTest(); +}; + +class AiThreadSyncBridgeTestWithInitSpecificsTest + : public AiThreadSyncBridgeTest { + public: + void SetUp() override { + AiThreadSyncBridgeTest::SetUp(); + AddSpecifics(kInitServerId, kInitTitle, kInitConversationId, + kInitLastTurnTimeUnixEpochMillis); + } + + void AddSpecifics(const std::string& server_id, + const std::string& title, + const std::string& conversation_turn_id, + int64_t last_turn_time) { + syncer::EntityChangeList add_changes; + syncer::EntityData entity_data; + entity_data.specifics.mutable_ai_thread()->set_server_id(server_id); + entity_data.specifics.mutable_ai_thread()->set_title(title); + entity_data.specifics.mutable_ai_thread()->set_conversation_turn_id( + conversation_turn_id); + entity_data.specifics.mutable_ai_thread() + ->set_last_turn_time_unix_epoch_millis(last_turn_time); + + add_changes.push_back( + syncer::EntityChange::CreateAdd(server_id, std::move(entity_data))); + + std::optional<syncer::ModelError> error = + bridge_->ApplyIncrementalSyncChanges( + bridge_->CreateMetadataChangeList(), std::move(add_changes)); + EXPECT_FALSE(error); + } + + void VerifyInitSpecifics() { + EXPECT_EQ(1u, bridge_->GetThreads().size()); + EXPECT_EQ(kInitServerId, bridge_->GetThreads()[0].server_id); + EXPECT_EQ(kInitConversationId, + bridge_->GetThreads()[0].conversation_turn_id); + EXPECT_EQ(kInitTitle, bridge_->GetThreads()[0].title); + EXPECT_EQ( + kInitLastTurnTimeUnixEpochMillis, + bridge_->GetThreads()[0].last_turn_time.InMillisecondsSinceUnixEpoch()); + } }; TEST_F(AiThreadSyncBridgeTest, GetDataForCommit) { @@ -229,6 +282,24 @@ 43); } +TEST_F(AiThreadSyncBridgeTestWithInitSpecificsTest, RemoveThread) { + VerifyInitSpecifics(); + AddSpecifics("00000000-0000-0000-0000-00000000002", "new_title", + "new_conversation_id", 999); + EXPECT_EQ(2u, bridge_->GetThreads().size()); + int new_idx = bridge_->GetThreads()[0].server_id == + "00000000-0000-0000-0000-00000000002" + ? 0 + : 1; + EXPECT_EQ("new_title", bridge_->GetThreads()[new_idx].title); + EXPECT_EQ("new_conversation_id", + bridge_->GetThreads()[new_idx].conversation_turn_id); + EXPECT_EQ(999, bridge_->GetThreads()[new_idx] + .last_turn_time.InMillisecondsSinceUnixEpoch()); + bridge_->DeleteThread(bridge_->GetThreads()[new_idx]); + EXPECT_EQ(1u, bridge_->GetThreads().size()); +} + } // namespace } // namespace contextual_tasks
diff --git a/components/contextual_tasks/internal/contextual_tasks_service_impl.cc b/components/contextual_tasks/internal/contextual_tasks_service_impl.cc index 7aa5bff6..b32295d6 100644 --- a/components/contextual_tasks/internal/contextual_tasks_service_impl.cc +++ b/components/contextual_tasks/internal/contextual_tasks_service_impl.cc
@@ -11,6 +11,7 @@ #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/metrics/histogram_functions.h" +#include "base/notreached.h" #include "base/task/single_thread_task_runner.h" #include "base/uuid.h" #include "components/contextual_search/contextual_search_service.h" @@ -333,6 +334,19 @@ const std::string& server_id) { auto it = tasks_.find(task_id); if (it != tasks_.end()) { + DCHECK(it->second.GetThread().has_value()); + switch (type) { + case ThreadType::kAiMode: + ai_thread_sync_bridge_->DeleteThread(it->second.GetThread().value()); + break; + case ThreadType::kGemini: + gemini_thread_sync_bridge_->DeleteThread( + it->second.GetThread().value()); + break; + case ThreadType::kUnknown: + default: + NOTREACHED(); + } it->second.RemoveThread(type, server_id); // If the task no longer has any thread, remove it. if (!it->second.GetThread()) {
diff --git a/components/contextual_tasks/internal/gemini_thread_sync_bridge.cc b/components/contextual_tasks/internal/gemini_thread_sync_bridge.cc index d78aeb96..26385ec 100644 --- a/components/contextual_tasks/internal/gemini_thread_sync_bridge.cc +++ b/components/contextual_tasks/internal/gemini_thread_sync_bridge.cc
@@ -7,6 +7,7 @@ #include "base/functional/callback_helpers.h" #include "base/notimplemented.h" #include "base/uuid.h" +#include "components/sync/base/deletion_origin.h" #include "components/sync/model/data_batch.h" #include "components/sync/model/in_memory_metadata_change_list.h" #include "components/sync/model/mutable_data_batch.h" @@ -234,6 +235,25 @@ return threads; } +void GeminiThreadSyncBridge::DeleteThread(const Thread& thread) { + if (!change_processor()->IsTrackingMetadata()) { + return; + } + std::unique_ptr<syncer::DataTypeStore::WriteBatch> batch = + data_type_store_->CreateWriteBatch(); + change_processor()->Delete(thread.server_id, + syncer::DeletionOrigin::Unspecified(), + batch->GetMetadataChangeList()); + + gemini_thread_specifics_.erase(thread.server_id); + batch->DeleteData(thread.server_id); + + data_type_store_->CommitWriteBatch( + std::move(batch), + base::BindOnce(&GeminiThreadSyncBridge::OnDataTypeStoreCommit, + weak_ptr_factory_.GetWeakPtr())); +} + void GeminiThreadSyncBridge::AddObserver(Observer* observer) { observers_.AddObserver(observer); }
diff --git a/components/contextual_tasks/internal/gemini_thread_sync_bridge.h b/components/contextual_tasks/internal/gemini_thread_sync_bridge.h index 3c9c6ec..bb9761c 100644 --- a/components/contextual_tasks/internal/gemini_thread_sync_bridge.h +++ b/components/contextual_tasks/internal/gemini_thread_sync_bridge.h
@@ -76,6 +76,9 @@ // Returns all threads. virtual std::vector<Thread> GetThreads() const; + // Delete the thread. + void DeleteThread(const Thread& thread); + void AddObserver(Observer* observer); void RemoveObserver(Observer* observer);
diff --git a/components/contextual_tasks/internal/gemini_thread_sync_bridge_unittest.cc b/components/contextual_tasks/internal/gemini_thread_sync_bridge_unittest.cc index c8e62fa..10a122b 100644 --- a/components/contextual_tasks/internal/gemini_thread_sync_bridge_unittest.cc +++ b/components/contextual_tasks/internal/gemini_thread_sync_bridge_unittest.cc
@@ -137,8 +137,10 @@ void SetUp() override { store_ = syncer::DataTypeStoreTestUtil::CreateInMemoryStoreForTest(); AddInitialSpecifics(); + ON_CALL(processor_, IsTrackingMetadata()) + .WillByDefault(testing::Return(true)); bridge_ = std::make_unique<GeminiThreadSyncBridge>( - mock_processor_.CreateForwardingProcessor(), + processor_.CreateForwardingProcessor(), syncer::DataTypeStoreTestUtil::FactoryForForwardingStore(store_.get())); base::RunLoop run_loop; bridge_->AddObserver(&observer_); @@ -175,6 +177,15 @@ return data; } + bool ThreadExists(const std::string& server_id) { + for (const auto& thread : bridge_->GetThreads()) { + if (server_id == thread.server_id) { + return true; + } + } + return false; + } + private: void AddInitialSpecifics() { std::unique_ptr<syncer::DataTypeStore::WriteBatch> batch = @@ -204,6 +215,7 @@ loop.Run(); } + testing::NiceMock<syncer::MockDataTypeLocalChangeProcessor> processor_; std::unique_ptr<syncer::DataTypeStore> store_; testing::NiceMock<MockObserver> observer_; }; @@ -303,6 +315,29 @@ threads[0].last_turn_time.InMillisecondsSinceUnixEpoch()); } +TEST_F(GeminiThreadSyncBridgeWithInitSpecificsTest, RemoveThread) { + EXPECT_FALSE(ThreadExists("00000000-0000-0000-0000-000000000003")); + syncer::EntityChangeList entity_change_list; + syncer::EntityData data = + CreateEntityData("00000000-0000-0000-0000-000000000003", "new_title", 52); + std::string storage_key = bridge()->GetStorageKey(data); + entity_change_list.push_back( + syncer::EntityChange::CreateAdd(storage_key, std::move(data))); + bridge()->ApplyIncrementalSyncChanges(bridge()->CreateMetadataChangeList(), + std::move(entity_change_list)); + EXPECT_TRUE(ThreadExists("00000000-0000-0000-0000-000000000003")); + int new_idx = bridge()->GetThreads()[0].server_id == + "00000000-0000-0000-0000-000000000003" + ? 0 + : 1; + EXPECT_EQ(52, bridge() + ->GetThreads()[new_idx] + .last_turn_time.InMillisecondsSinceUnixEpoch()); + EXPECT_EQ("new_title", bridge()->GetThreads()[new_idx].title); + bridge()->DeleteThread(bridge()->GetThreads()[new_idx]); + EXPECT_FALSE(ThreadExists("00000000-0000-0000-0000-000000000003")); +} + } // namespace } // namespace contextual_tasks
diff --git a/components/contextual_tasks/public/features.cc b/components/contextual_tasks/public/features.cc index 05fcc20..88c9a2ad 100644 --- a/components/contextual_tasks/public/features.cc +++ b/components/contextual_tasks/public/features.cc
@@ -55,8 +55,6 @@ BASE_FEATURE(kEnableNotifyZeroStateRenderedCapability, base::FEATURE_DISABLED_BY_DEFAULT); -BASE_FEATURE(kContextualTasksExpandButton, base::FEATURE_ENABLED_BY_DEFAULT); - BASE_FEATURE(kContextualTasksSendFullVersionListEnabled, base::FEATURE_ENABLED_BY_DEFAULT); @@ -492,12 +490,6 @@ const char kContextualTasksContextLibraryDescription[] = "Enables integration with the server side context library."; -const char kContextualTasksExpandButtonName[] = - "Contextual Tasks Expand Button"; -const char kContextualTasksExpandButtonDescription[] = - "Replace the overflow menu in the side panel with a button to move the " - "thread to a new tab."; - const char kContextualTasksSuggestionsEnabledName[] = "Contextual Tasks Suggestions Enabled"; const char kContextualTasksSuggestionsEnabledDescription[] =
diff --git a/components/contextual_tasks/public/features.h b/components/contextual_tasks/public/features.h index 243322e..af8592f 100644 --- a/components/contextual_tasks/public/features.h +++ b/components/contextual_tasks/public/features.h
@@ -43,10 +43,6 @@ // param. BASE_DECLARE_FEATURE(kEnableNotifyZeroStateRenderedCapability); -// Replace the overflow menu in the side panel with an explicit button to move -// the thread to a new tab. -BASE_DECLARE_FEATURE(kContextualTasksExpandButton); - // If enabled, adds the Sec-CH-UA-Full-Version-List header to all network // requests initiated from within an embedded Co-Browse <webview>. BASE_DECLARE_FEATURE(kContextualTasksSendFullVersionListEnabled); @@ -274,8 +270,6 @@ extern const char kContextualTasksContextLibraryDescription[]; extern const char kContextualTasksContextName[]; extern const char kContextualTasksContextDescription[]; -extern const char kContextualTasksExpandButtonName[]; -extern const char kContextualTasksExpandButtonDescription[]; extern const char kContextualTasksSuggestionsEnabledName[]; extern const char kContextualTasksSuggestionsEnabledDescription[];
diff --git a/components/crash/android/crash_keys_android.cc b/components/crash/android/crash_keys_android.cc index b3de9a0..770f433 100644 --- a/components/crash/android/crash_keys_android.cc +++ b/components/crash/android/crash_keys_android.cc
@@ -5,10 +5,12 @@ #include "components/crash/android/crash_keys_android.h" #include <array> +#include <optional> #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "components/crash/core/common/crash_key.h" +#include "third_party/jni_zero/default_conversions.h" // Must come after all headers that specialize FromJniType() / ToJniType(). #include "components/crash/android/jni_headers/CrashKeys_jni.h" @@ -56,21 +58,19 @@ Java_CrashKeys_flushToNative(env, jinstance); } -static void JNI_CrashKeys_Set(JNIEnv* env, - int32_t key, - const base::android::JavaRef<jstring>& value) { - if (value.is_null()) { +static void JNI_CrashKeys_Set(int32_t key, + const std::optional<std::string>& value) { + if (!value) { if (key == static_cast<int32_t>(CrashKeyIndex::INSTALLED_MODULES)) { g_installed_modules_key.Clear(); } else { GetCrashKey(key).Clear(); } } else { - std::string val = base::android::ConvertJavaStringToUTF8(env, value); if (key == static_cast<int32_t>(CrashKeyIndex::INSTALLED_MODULES)) { - g_installed_modules_key.Set(val); + g_installed_modules_key.Set(*value); } else { - GetCrashKey(key).Set(val); + GetCrashKey(key).Set(*value); } } }
diff --git a/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java b/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java index 8b9d810..e38ecf7e 100644 --- a/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java +++ b/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java
@@ -5,6 +5,7 @@ package org.chromium.components.crash; import org.jni_zero.CalledByNative; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.base.ThreadUtils; @@ -106,6 +107,6 @@ @NativeMethods interface Natives { - void set(int key, @Nullable String value); + void set(int key, @JniType("std::optional<std::string>") @Nullable String value); } }
diff --git a/components/data_sharing/internal/android/data_sharing_service_android.cc b/components/data_sharing/internal/android/data_sharing_service_android.cc index 1c86b12f..ca2fcb21 100644 --- a/components/data_sharing/internal/android/data_sharing_service_android.cc +++ b/components/data_sharing/internal/android/data_sharing_service_android.cc
@@ -31,8 +31,6 @@ #include "components/data_sharing/internal/jni_headers/ObserverBridge_jni.h" using base::android::AttachCurrentThread; -using base::android::ConvertJavaStringToUTF8; -using base::android::ConvertUTF8ToJavaString; using base::android::JavaRef; using base::android::RunObjectCallbackAndroid; using base::android::ScopedJavaGlobalRef; @@ -130,8 +128,7 @@ void DataSharingServiceAndroid::GroupDataObserverBridge::OnGroupRemoved( const GroupId& group_id, const base::Time& event_time) { JNIEnv* env = AttachCurrentThread(); - Java_ObserverBridge_onGroupRemoved( - env, java_obj_, ConvertUTF8ToJavaString(env, group_id.value())); + Java_ObserverBridge_onGroupRemoved(env, java_obj_, group_id.value()); } // This function is declared in data_sharing_service.h and @@ -169,79 +166,67 @@ Java_DataSharingServiceImpl_clearNativePtr(env, java_obj_); } -void DataSharingServiceAndroid::ReadGroup(JNIEnv* env, - const JavaRef<jstring>& group_id, +void DataSharingServiceAndroid::ReadGroup(const std::string& group_id, const JavaRef<jobject>& j_callback) { // TODO(crbug.com/382033539): migrate android implementation to use // synchronous ReadGroup(). data_sharing_service_->ReadGroupDeprecated( - GroupId(ConvertJavaStringToUTF8(env, group_id)), + GroupId(group_id), base::BindOnce(&RunGroupDataOrFailureOutcomeCallback, ScopedJavaGlobalRef<jobject>(j_callback))); } void DataSharingServiceAndroid::CreateGroup( - JNIEnv* env, - const JavaRef<jstring>& group_name, + const std::string& group_name, const JavaRef<jobject>& j_callback) { data_sharing_service_->CreateGroup( - ConvertJavaStringToUTF8(env, group_name), - base::BindOnce(&RunGroupDataOrFailureOutcomeCallback, - ScopedJavaGlobalRef<jobject>(j_callback))); + group_name, base::BindOnce(&RunGroupDataOrFailureOutcomeCallback, + ScopedJavaGlobalRef<jobject>(j_callback))); } void DataSharingServiceAndroid::InviteMember( - JNIEnv* env, - const JavaRef<jstring>& group_id, - const JavaRef<jstring>& invitee_email, + const std::string& group_id, + const std::string& invitee_email, const JavaRef<jobject>& j_callback) { data_sharing_service_->InviteMember( - GroupId(ConvertJavaStringToUTF8(env, group_id)), - ConvertJavaStringToUTF8(env, invitee_email), + GroupId(group_id), invitee_email, base::BindOnce(&RunPeopleGroupActionOutcomeCallback, ScopedJavaGlobalRef<jobject>(j_callback))); } -void DataSharingServiceAndroid::AddMember(JNIEnv* env, - const JavaRef<jstring>& group_id, - const JavaRef<jstring>& access_token, +void DataSharingServiceAndroid::AddMember(const std::string& group_id, + const std::string& access_token, const JavaRef<jobject>& j_callback) { data_sharing_service_->AddMember( - GroupId(ConvertJavaStringToUTF8(env, group_id)), - ConvertJavaStringToUTF8(env, access_token), + GroupId(group_id), access_token, base::BindOnce(&RunPeopleGroupActionOutcomeCallback, ScopedJavaGlobalRef<jobject>(j_callback))); } void DataSharingServiceAndroid::RemoveMember( - JNIEnv* env, - const JavaRef<jstring>& group_id, - const JavaRef<jstring>& member_email, + const std::string& group_id, + const std::string& member_email, const JavaRef<jobject>& j_callback) { data_sharing_service_->RemoveMember( - GroupId(ConvertJavaStringToUTF8(env, group_id)), - ConvertJavaStringToUTF8(env, member_email), + GroupId(group_id), member_email, base::BindOnce(&RunPeopleGroupActionOutcomeCallback, ScopedJavaGlobalRef<jobject>(j_callback))); } -bool DataSharingServiceAndroid::IsEmptyService(JNIEnv* env) { +bool DataSharingServiceAndroid::IsEmptyService() { return data_sharing_service_->IsEmptyService(); } -ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetNetworkLoader( - JNIEnv* env) { +ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetNetworkLoader() { return network_loader_->GetJavaObject(); } ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetDataSharingUrl( JNIEnv* env, - const JavaRef<jstring>& j_group_id, - const JavaRef<jstring>& j_access_token) { + const std::string& group_id, + const std::string& access_token) { // Note that this function is only passing the required fields to the native // service. - std::string group_id = ConvertJavaStringToUTF8(env, j_group_id); - std::string access_token = ConvertJavaStringToUTF8(env, j_access_token); std::unique_ptr<GURL> url = data_sharing_service_->GetDataSharingUrl( GroupData(GroupId(group_id), /*display_name*/ "", /*members*/ {}, /*former_members*/ {}, access_token)); @@ -262,39 +247,33 @@ } void DataSharingServiceAndroid::EnsureGroupVisibility( - JNIEnv* env, - const JavaRef<jstring>& group_id, + const std::string& group_id, const JavaRef<jobject>& j_callback) { data_sharing_service_->EnsureGroupVisibility( - GroupId(ConvertJavaStringToUTF8(env, group_id)), + GroupId(group_id), base::BindOnce(&RunGroupDataOrFailureOutcomeCallback, ScopedJavaGlobalRef<jobject>(j_callback))); } void DataSharingServiceAndroid::GetSharedEntitiesPreview( - JNIEnv* env, - const JavaRef<jstring>& group_id, - const JavaRef<jstring>& access_token, + const std::string& group_id, + const std::string& access_token, const JavaRef<jobject>& j_callback) { data_sharing_service_->GetSharedEntitiesPreview( - GroupToken(GroupId(ConvertJavaStringToUTF8(env, group_id)), - ConvertJavaStringToUTF8(env, access_token)), + GroupToken(GroupId(group_id), access_token), base::BindOnce(&RunSharedDataPreviewOrFailureOutcomeCallback, ScopedJavaGlobalRef<jobject>(j_callback))); } -ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetUiDelegate( - JNIEnv* env) { +ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetUiDelegate() { return data_sharing_service_->GetUiDelegate()->GetJavaObject(); } void DataSharingServiceAndroid::Log( - JNIEnv* env, /*logger_common::mojom::LogSource*/ int32_t source, - const JavaRef<jstring>& message) { + const std::string& message) { DATA_SHARING_LOG(static_cast<logger_common::mojom::LogSource>(source), - data_sharing_service_->GetLogger(), - ConvertJavaStringToUTF8(env, message)); + data_sharing_service_->GetLogger(), message); } ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetJavaObject() { @@ -309,17 +288,15 @@ static ScopedJavaLocalRef<jobject> JNI_DataSharingServiceImpl_GetDataSharingUrlForTesting( JNIEnv* env, - const JavaRef<jstring>& j_group_id, - const JavaRef<jstring>& j_access_token) { + const std::string& group_id, + const std::string& access_token) { GURL url = *DataSharingServiceImpl::GetDataSharingUrl( - GroupToken(GroupId(ConvertJavaStringToUTF8(env, j_group_id)), - ConvertJavaStringToUTF8(env, j_access_token))); + GroupToken(GroupId(group_id), access_token)); return url::GURLAndroid::FromNativeGURL(env, url); } void DataSharingServiceAndroid::SetSharedEntitiesPreviewForTesting( - JNIEnv* env, - const JavaRef<jstring>& j_group_id) { + const std::string& group_id) { auto fake_proxy = std::make_unique<FakePreviewServerProxy>(); data_sharing::SharedTabGroupPreview preview; preview.title = "my group title"; @@ -333,8 +310,8 @@ data_sharing::DataSharingService::SharedDataPreviewOrFailureOutcome outcome = std::move(sharedDataPreview); - fake_proxy->SetSharedEntitiesPreviewForTesting( - GroupId(ConvertJavaStringToUTF8(j_group_id)), std::move(outcome)); + fake_proxy->SetSharedEntitiesPreviewForTesting(GroupId(group_id), + std::move(outcome)); data_sharing_service_->SetPreviewServerProxyForTesting(std::move(fake_proxy)); }
diff --git a/components/data_sharing/internal/android/data_sharing_service_android.h b/components/data_sharing/internal/android/data_sharing_service_android.h index 1508b95..21e22c5 100644 --- a/components/data_sharing/internal/android/data_sharing_service_android.h +++ b/components/data_sharing/internal/android/data_sharing_service_android.h
@@ -27,47 +27,38 @@ ~DataSharingServiceAndroid() override; // DataSharingService Java API methods, implemented by native service: - void ReadGroup(JNIEnv* env, - const JavaRef<jstring>& group_id, + void ReadGroup(const std::string& group_id, const JavaRef<jobject>& j_callback); - void CreateGroup(JNIEnv* env, - const JavaRef<jstring>& group_name, + void CreateGroup(const std::string& group_name, const JavaRef<jobject>& j_callback); - void InviteMember(JNIEnv* env, - const JavaRef<jstring>& group_id, - const JavaRef<jstring>& invitee_email, + void InviteMember(const std::string& group_id, + const std::string& invitee_email, const JavaRef<jobject>& j_callback); - void AddMember(JNIEnv* env, - const JavaRef<jstring>& group_id, - const JavaRef<jstring>& access_token, + void AddMember(const std::string& group_id, + const std::string& access_token, const JavaRef<jobject>& j_callback); - void RemoveMember(JNIEnv* env, - const JavaRef<jstring>& group_id, - const JavaRef<jstring>& member_email, + void RemoveMember(const std::string& group_id, + const std::string& member_email, const JavaRef<jobject>& j_callback); - bool IsEmptyService(JNIEnv* env); - ScopedJavaLocalRef<jobject> GetNetworkLoader(JNIEnv* env); + bool IsEmptyService(); + ScopedJavaLocalRef<jobject> GetNetworkLoader(); ScopedJavaLocalRef<jobject> GetDataSharingUrl( JNIEnv* env, - const JavaRef<jstring>& group_id, - const JavaRef<jstring>& access_token); + const std::string& group_id, + const std::string& access_token); ScopedJavaLocalRef<jobject> ParseDataSharingUrl( JNIEnv* env, const JavaRef<jobject>& j_url); - void EnsureGroupVisibility(JNIEnv* env, - const JavaRef<jstring>& group_id, + void EnsureGroupVisibility(const std::string& group_id, const JavaRef<jobject>& j_callback); - void GetSharedEntitiesPreview(JNIEnv* env, - const JavaRef<jstring>& group_id, - const JavaRef<jstring>& access_token, + void GetSharedEntitiesPreview(const std::string& group_id, + const std::string& access_token, const JavaRef<jobject>& j_callback); - ScopedJavaLocalRef<jobject> GetUiDelegate(JNIEnv* env); - void Log(JNIEnv* env, - /*logger_common::mojom::LogSource*/ int32_t source, - const JavaRef<jstring>& message); + ScopedJavaLocalRef<jobject> GetUiDelegate(); + void Log(/*logger_common::mojom::LogSource*/ int32_t source, + const std::string& message); - void SetSharedEntitiesPreviewForTesting(JNIEnv* env, - const JavaRef<jstring>& j_group_id); + void SetSharedEntitiesPreviewForTesting(const std::string& group_id); // Returns the DataSharingServiceImpl java object. ScopedJavaLocalRef<jobject> GetJavaObject();
diff --git a/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/DataSharingServiceImpl.java b/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/DataSharingServiceImpl.java index d80e4659..8c0c685 100644 --- a/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/DataSharingServiceImpl.java +++ b/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/DataSharingServiceImpl.java
@@ -8,6 +8,7 @@ import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.base.Callback; @@ -182,30 +183,30 @@ interface Natives { void readGroup( long nativeDataSharingServiceAndroid, - String groupId, + @JniType("std::string") String groupId, Callback<GroupDataOrFailureOutcome> callback); void createGroup( long nativeDataSharingServiceAndroid, - String groupName, + @JniType("std::string") String groupName, Callback<GroupDataOrFailureOutcome> callback); void inviteMember( long nativeDataSharingServiceAndroid, - String groupId, - String inviteeEmail, + @JniType("std::string") String groupId, + @JniType("std::string") String inviteeEmail, Callback<Integer> callback); void addMember( long nativeDataSharingServiceAndroid, - String groupId, - String accessToken, + @JniType("std::string") String groupId, + @JniType("std::string") String accessToken, Callback<Integer> callback); void removeMember( long nativeDataSharingServiceAndroid, - String groupId, - String memberEmail, + @JniType("std::string") String groupId, + @JniType("std::string") String memberEmail, Callback<Integer> callback); boolean isEmptyService(long nativeDataSharingServiceAndroid); @@ -213,29 +214,35 @@ DataSharingNetworkLoader getNetworkLoader(long nativeDataSharingServiceAndroid); GURL getDataSharingUrl( - long nativeDataSharingServiceAndroid, String groupId, String accessToken); + long nativeDataSharingServiceAndroid, + @JniType("std::string") String groupId, + @JniType("std::string") String accessToken); DataSharingService.ParseUrlResult parseDataSharingUrl( long nativeDataSharingServiceAndroid, GURL url); void ensureGroupVisibility( long nativeDataSharingServiceAndroid, - String groupId, + @JniType("std::string") String groupId, Callback<GroupDataOrFailureOutcome> callback); void getSharedEntitiesPreview( long nativeDataSharingServiceAndroid, - String groupId, - String accessToken, + @JniType("std::string") String groupId, + @JniType("std::string") String accessToken, Callback<SharedDataPreviewOrFailureOutcome> callback); DataSharingUIDelegate getUiDelegate(long nativeDataSharingServiceAndroid); - void log(long nativeDataSharingServiceAndroid, int source, String message); + void log( + long nativeDataSharingServiceAndroid, + int source, + @JniType("std::string") String message); - GURL getDataSharingUrlForTesting(String groupId, String accessToken); + GURL getDataSharingUrlForTesting( + @JniType("std::string") String groupId, @JniType("std::string") String accessToken); void setSharedEntitiesPreviewForTesting( - long nativeDataSharingServiceAndroid, String groupId); + long nativeDataSharingServiceAndroid, @JniType("std::string") String groupId); } }
diff --git a/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/ObserverBridge.java b/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/ObserverBridge.java index 0cb291a..7be4a18 100644 --- a/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/ObserverBridge.java +++ b/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/ObserverBridge.java
@@ -5,6 +5,7 @@ package org.chromium.components.data_sharing; import org.jni_zero.CalledByNative; +import org.jni_zero.JniType; import org.chromium.base.ObserverList; import org.chromium.build.annotations.NullMarked; @@ -51,7 +52,7 @@ @CalledByNative @Override - public void onGroupRemoved(String groupId) { + public void onGroupRemoved(@JniType("std::string") String groupId) { for (DataSharingService.Observer javaObserver : mJavaObservers) { javaObserver.onGroupRemoved(groupId); }
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc index 2ae6d72..b73f6b2 100644 --- a/components/feature_engagement/public/feature_constants.cc +++ b/components/feature_engagement/public/feature_constants.cc
@@ -1011,10 +1011,10 @@ base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kIPHiOSTabGroupsDesktopFeature, "IPH_iOSTabGroupsDesktop", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kIPHiOSPriceTrackingDesktopFeature, "IPH_iOSPriceTrackingDesktop", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); #endif // !BUILDFLAG(IS_ANDROID) #if !BUILDFLAG(IS_IOS)
diff --git a/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java b/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java index b919eb2..370ae28 100644 --- a/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java +++ b/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java
@@ -6,6 +6,7 @@ import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.base.task.AsyncTask; @@ -45,7 +46,8 @@ * share an underlying InstanceIDWithSubtype. */ @CalledByNative - public static InstanceIDBridge create(long nativeInstanceIDAndroid, String subtype) { + public static InstanceIDBridge create( + long nativeInstanceIDAndroid, @JniType("std::string") String subtype) { return new InstanceIDBridge(nativeInstanceIDAndroid, subtype); } @@ -104,14 +106,16 @@ } /** - * Async wrapper for {@link InstanceID#getToken(String, String)}. - * |isLazy| isn't part of the InstanceID.getToken() call and not sent to the - * FCM server. It's used to mark the subscription as lazy such that incoming - * messages are deferred until there are visible activities. + * Async wrapper for {@link InstanceID#getToken(String, String)}. |isLazy| isn't part of the + * InstanceID.getToken() call and not sent to the FCM server. It's used to mark the subscription + * as lazy such that incoming messages are deferred until there are visible activities. */ @CalledByNative private void getToken( - final int requestId, final String authorizedEntity, final String scope, int flags) { + final int requestId, + final @JniType("std::string") String authorizedEntity, + final @JniType("std::string") String scope, + int flags) { new BridgeAsyncTask<String>() { @Override protected String doBackgroundWork() { @@ -142,7 +146,9 @@ /** Async wrapper for {@link InstanceID#deleteToken(String, String)}. */ @CalledByNative private void deleteToken( - final int requestId, final String authorizedEntity, final String scope) { + final int requestId, + final @JniType("std::string") String authorizedEntity, + final @JniType("std::string") String scope) { new BridgeAsyncTask<Boolean>() { @Override protected Boolean doBackgroundWork() { @@ -248,11 +254,13 @@ @NativeMethods interface Natives { - void didGetID(long nativeInstanceIDAndroid, int requestId, String id); + void didGetID( + long nativeInstanceIDAndroid, int requestId, @JniType("std::string") String id); void didGetCreationTime(long nativeInstanceIDAndroid, int requestId, long creationTime); - void didGetToken(long nativeInstanceIDAndroid, int requestId, String token); + void didGetToken( + long nativeInstanceIDAndroid, int requestId, @JniType("std::string") String token); void didDeleteToken(long nativeInstanceIDAndroid, int requestId, boolean success);
diff --git a/components/gcm_driver/instance_id/instance_id_android.cc b/components/gcm_driver/instance_id/instance_id_android.cc index 1fc51cb..a2bf1dd 100644 --- a/components/gcm_driver/instance_id/instance_id_android.cc +++ b/components/gcm_driver/instance_id/instance_id_android.cc
@@ -20,8 +20,6 @@ #include "components/gcm_driver/instance_id/android/jni_headers/InstanceIDBridge_jni.h" using base::android::AttachCurrentThread; -using base::android::ConvertJavaStringToUTF8; -using base::android::ConvertUTF8ToJavaString; namespace instance_id { @@ -57,9 +55,8 @@ std::string subtype = app_id; JNIEnv* env = AttachCurrentThread(); - java_ref_.Reset( - Java_InstanceIDBridge_create(env, reinterpret_cast<intptr_t>(this), - ConvertUTF8ToJavaString(env, subtype))); + java_ref_.Reset(Java_InstanceIDBridge_create( + env, reinterpret_cast<intptr_t>(this), subtype)); } InstanceIDAndroid::~InstanceIDAndroid() { @@ -110,10 +107,8 @@ [](int sum, Flags flag) { return sum + static_cast<int>(flag); }); JNIEnv* env = AttachCurrentThread(); - Java_InstanceIDBridge_getToken( - env, java_ref_, request_id, - ConvertUTF8ToJavaString(env, authorized_entity), - ConvertUTF8ToJavaString(env, scope), java_flags); + Java_InstanceIDBridge_getToken(env, java_ref_, request_id, authorized_entity, + scope, java_flags); } void InstanceIDAndroid::ValidateToken(const std::string& authorized_entity, @@ -134,10 +129,8 @@ std::make_unique<DeleteTokenCallback>(std::move(callback))); JNIEnv* env = AttachCurrentThread(); - Java_InstanceIDBridge_deleteToken( - env, java_ref_, request_id, - ConvertUTF8ToJavaString(env, authorized_entity), - ConvertUTF8ToJavaString(env, scope)); + Java_InstanceIDBridge_deleteToken(env, java_ref_, request_id, + authorized_entity, scope); } void InstanceIDAndroid::DeleteIDImpl(DeleteIDCallback callback) { @@ -152,12 +145,12 @@ void InstanceIDAndroid::DidGetID(JNIEnv* env, int32_t request_id, - const base::android::JavaRef<jstring>& jid) { + const std::string& id) { DCHECK(thread_checker_.CalledOnValidThread()); GetIDCallback* callback = get_id_callbacks_.Lookup(request_id); DCHECK(callback); - std::move(*callback).Run(ConvertJavaStringToUTF8(jid)); + std::move(*callback).Run(id); get_id_callbacks_.Remove(request_id); } @@ -181,15 +174,13 @@ get_creation_time_callbacks_.Remove(request_id); } -void InstanceIDAndroid::DidGetToken( - JNIEnv* env, - int32_t request_id, - const base::android::JavaRef<jstring>& jtoken) { +void InstanceIDAndroid::DidGetToken(JNIEnv* env, + int32_t request_id, + const std::string& token) { DCHECK(thread_checker_.CalledOnValidThread()); GetTokenCallback* callback = get_token_callbacks_.Lookup(request_id); DCHECK(callback); - std::string token = ConvertJavaStringToUTF8(jtoken); std::move(*callback).Run( token, token.empty() ? InstanceID::UNKNOWN_ERROR : InstanceID::SUCCESS); get_token_callbacks_.Remove(request_id);
diff --git a/components/gcm_driver/instance_id/instance_id_android.h b/components/gcm_driver/instance_id/instance_id_android.h index 280c7e1..60d0940 100644 --- a/components/gcm_driver/instance_id/instance_id_android.h +++ b/components/gcm_driver/instance_id/instance_id_android.h
@@ -65,15 +65,11 @@ void DeleteIDImpl(DeleteIDCallback callback) override; // Methods called from Java via JNI: - void DidGetID(JNIEnv* env, - int32_t request_id, - const base::android::JavaRef<jstring>& jid); + void DidGetID(JNIEnv* env, int32_t request_id, const std::string& id); void DidGetCreationTime(JNIEnv* env, int32_t request_id, int64_t creation_time_unix_ms); - void DidGetToken(JNIEnv* env, - int32_t request_id, - const base::android::JavaRef<jstring>& jtoken); + void DidGetToken(JNIEnv* env, int32_t request_id, const std::string& token); void DidDeleteToken(JNIEnv* env, int32_t request_id, bool success); void DidDeleteID(JNIEnv* env, int32_t request_id, bool success);
diff --git a/components/javascript_dialogs/android/app_modal_dialog_view_android.cc b/components/javascript_dialogs/android/app_modal_dialog_view_android.cc index 992453df..bc230fb4 100644 --- a/components/javascript_dialogs/android/app_modal_dialog_view_android.cc +++ b/components/javascript_dialogs/android/app_modal_dialog_view_android.cc
@@ -49,10 +49,8 @@ } ScopedJavaLocalRef<jobject> dialog_object; - ScopedJavaLocalRef<jstring> title = - ConvertUTF16ToJavaString(env, controller_->title()); - ScopedJavaLocalRef<jstring> message = - ConvertUTF16ToJavaString(env, controller_->message_text()); + const std::u16string& title = controller_->title(); + const std::u16string& message = controller_->message_text(); switch (controller_->javascript_dialog_type()) { case content::JAVASCRIPT_DIALOG_TYPE_ALERT: { @@ -72,11 +70,9 @@ break; } case content::JAVASCRIPT_DIALOG_TYPE_PROMPT: { - ScopedJavaLocalRef<jstring> default_prompt_text = - ConvertUTF16ToJavaString(env, controller_->default_prompt_text()); dialog_object = Java_JavascriptAppModalDialog_createPromptDialog( env, title, message, controller_->display_suppress_checkbox(), - default_prompt_text); + controller_->default_prompt_text()); break; } default: @@ -109,12 +105,9 @@ } void AppModalDialogViewAndroid::DidAcceptAppModalDialog( - JNIEnv* env, - const JavaRef<jstring>& prompt, + const std::u16string& prompt, bool should_suppress_js_dialogs) { - std::u16string prompt_text = - base::android::ConvertJavaStringToUTF16(env, prompt); - controller_->OnAccept(prompt_text, should_suppress_js_dialogs); + controller_->OnAccept(prompt, should_suppress_js_dialogs); delete this; } @@ -128,7 +121,6 @@ } void AppModalDialogViewAndroid::DidCancelAppModalDialog( - JNIEnv* env, bool should_suppress_js_dialogs) { controller_->OnCancel(should_suppress_js_dialogs); delete this;
diff --git a/components/javascript_dialogs/android/app_modal_dialog_view_android.h b/components/javascript_dialogs/android/app_modal_dialog_view_android.h index 0737a9e2..4e98731 100644 --- a/components/javascript_dialogs/android/app_modal_dialog_view_android.h +++ b/components/javascript_dialogs/android/app_modal_dialog_view_android.h
@@ -6,6 +6,7 @@ #define COMPONENTS_JAVASCRIPT_DIALOGS_ANDROID_APP_MODAL_DIALOG_VIEW_ANDROID_H_ #include <memory> +#include <string> #include "base/android/jni_weak_ref.h" #include "base/android/scoped_java_ref.h" @@ -37,12 +38,9 @@ bool IsShowing() const override; // Called when java confirms or cancels the dialog. - void DidAcceptAppModalDialog( - JNIEnv* env, - const base::android::JavaRef<jstring>& prompt_text, - bool suppress_js_dialogs); - void DidCancelAppModalDialog(JNIEnv* env, + void DidAcceptAppModalDialog(const std::u16string& prompt_text, bool suppress_js_dialogs); + void DidCancelAppModalDialog(bool suppress_js_dialogs); const base::android::ScopedJavaGlobalRef<jobject>& GetDialogObject() const;
diff --git a/components/javascript_dialogs/android/java/src/org/chromium/components/javascript_dialogs/JavascriptAppModalDialog.java b/components/javascript_dialogs/android/java/src/org/chromium/components/javascript_dialogs/JavascriptAppModalDialog.java index f806b94..b792b4fe 100644 --- a/components/javascript_dialogs/android/java/src/org/chromium/components/javascript_dialogs/JavascriptAppModalDialog.java +++ b/components/javascript_dialogs/android/java/src/org/chromium/components/javascript_dialogs/JavascriptAppModalDialog.java
@@ -8,6 +8,7 @@ import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.build.annotations.NullMarked; @@ -44,21 +45,28 @@ @CalledByNative public static JavascriptAppModalDialog createAlertDialog( - String title, String message, boolean shouldShowSuppressCheckBox) { + @JniType("std::u16string") String title, + @JniType("std::u16string") String message, + boolean shouldShowSuppressCheckBox) { return new JavascriptAppModalDialog( title, message, null, shouldShowSuppressCheckBox, R.string.ok, 0); } @CalledByNative public static JavascriptAppModalDialog createConfirmDialog( - String title, String message, boolean shouldShowSuppressCheckBox) { + @JniType("std::u16string") String title, + @JniType("std::u16string") String message, + boolean shouldShowSuppressCheckBox) { return new JavascriptAppModalDialog( title, message, null, shouldShowSuppressCheckBox, R.string.ok, R.string.cancel); } @CalledByNative public static JavascriptAppModalDialog createBeforeUnloadDialog( - String title, String message, boolean isReload, boolean shouldShowSuppressCheckBox) { + @JniType("std::u16string") String title, + @JniType("std::u16string") String message, + boolean isReload, + boolean shouldShowSuppressCheckBox) { return new JavascriptAppModalDialog( title, message, @@ -70,10 +78,10 @@ @CalledByNative public static JavascriptAppModalDialog createPromptDialog( - String title, - String message, + @JniType("std::u16string") String title, + @JniType("std::u16string") String message, boolean shouldShowSuppressCheckBox, - String defaultPromptText) { + @JniType("std::u16string") String defaultPromptText) { return new JavascriptAppModalDialog( title, message, @@ -128,7 +136,9 @@ @NativeMethods interface Natives { void didAcceptAppModalDialog( - long nativeAppModalDialogViewAndroid, String prompt, boolean suppress); + long nativeAppModalDialogViewAndroid, + @JniType("std::u16string") String prompt, + boolean suppress); void didCancelAppModalDialog(long nativeAppModalDialogViewAndroid, boolean suppress);
diff --git a/components/omnibox/browser/aim_eligibility_service.cc b/components/omnibox/browser/aim_eligibility_service.cc index 5c8dca5..cad7531 100644 --- a/components/omnibox/browser/aim_eligibility_service.cc +++ b/components/omnibox/browser/aim_eligibility_service.cc
@@ -529,7 +529,7 @@ bool AimEligibilityService::IsFuseboxEligible() const { if (!base::FeatureList::IsEnabled( omnibox::kAimFuseboxEligibilityCheckEnabled)) { - return true; + return IsAimEligible(); } return IsAimEligible() && GetMostRecentResponse().is_fusebox_eligible(); }
diff --git a/components/omnibox/browser/aim_eligibility_service_unittest.cc b/components/omnibox/browser/aim_eligibility_service_unittest.cc index da8725d..647582b 100644 --- a/components/omnibox/browser/aim_eligibility_service_unittest.cc +++ b/components/omnibox/browser/aim_eligibility_service_unittest.cc
@@ -475,13 +475,15 @@ TEST_F(AimEligibilityServiceTest, IsFuseboxEligible_FeatureDisabled) { base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature( - omnibox::kAimFuseboxEligibilityCheckEnabled); + feature_list.InitWithFeatures({}, + {omnibox::kAimFuseboxEligibilityCheckEnabled, + omnibox::kAimServerEligibilityEnabled}); omnibox::AimEligibilityResponse response; + response.set_is_eligible(true); response.set_is_fusebox_eligible(false); aim_eligibility_service_->SetAimEligibilityResponse(std::move(response)); // Should be true regardless of response if feature is disabled. - EXPECT_TRUE(aim_eligibility_service_->IsFuseboxEligible()); + EXPECT_EQ(true, aim_eligibility_service_->IsFuseboxEligible()); }
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java index d1c517ee..a956d88 100644 --- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java +++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
@@ -197,34 +197,34 @@ private static AutocompleteMatch build( long nativeObject, int nativeType, - int[] nativeSubtypes, + @JniType("std::vector<int32_t>") int[] nativeSubtypes, boolean isSearchType, int iconType, int transition, - String contents, - int[] contentClassificationOffsets, - int[] contentClassificationStyles, - String description, - int[] descriptionClassificationOffsets, - int[] descriptionClassificationStyles, + @JniType("std::u16string") String contents, + @JniType("std::vector<int32_t>") int[] contentClassificationOffsets, + @JniType("std::vector<int32_t>") int[] contentClassificationStyles, + @JniType("std::u16string") String description, + @JniType("std::vector<int32_t>") int[] descriptionClassificationOffsets, + @JniType("std::vector<int32_t>") int[] descriptionClassificationStyles, byte[] serializedAnswerTemplate, int answerType, - String fillIntoEdit, - GURL url, - GURL imageUrl, - String imageDominantColor, + @JniType("std::u16string") String fillIntoEdit, + @JniType("GURL") GURL url, + @JniType("GURL") GURL imageUrl, + @JniType("std::string") String imageDominantColor, boolean isDeletable, - String postContentType, + @JniType("std::string") String postContentType, byte[] postData, int groupId, byte[] clipboardImageData, boolean hasTabMatch, @JniType("std::vector") List<OmniboxAction> actions, boolean allowedToBeDefaultMatch, - String inlineAutocompletion, - String additionalText, - String localTabGroupId, - String associatedKeyword, + @JniType("std::u16string") String inlineAutocompletion, + @JniType("std::u16string") String additionalText, + @JniType("base::Uuid") String localTabGroupId, + @JniType("std::u16string") String associatedKeyword, byte[] serializedSuggestTemplate) { assert contentClassificationOffsets.length == contentClassificationStyles.length; List<MatchClassification> contentClassifications = new ArrayList<>(); @@ -298,9 +298,9 @@ */ @CalledByNative private void updateClipboardContent( - String contents, - GURL url, - @Nullable String postContentType, + @JniType("std::u16string") String contents, + @JniType("GURL") GURL url, + @JniType("std::string") @Nullable String postContentType, byte @Nullable [] postData, byte @Nullable [] clipboardImageData) { mDisplayText = contents; @@ -317,7 +317,10 @@ @CalledByNative @VisibleForTesting - void updateNavigationDetails(GURL url, String[] headerKeys, String[] headerVals) { + void updateNavigationDetails( + @JniType("GURL") GURL url, + @JniType("std::vector<std::string>") String[] headerKeys, + @JniType("std::vector<std::string>") String[] headerVals) { mUrl = url; assert headerKeys.length == headerVals.length; @@ -344,9 +347,9 @@ @CalledByNative private void setDescription( - String description, - int[] descriptionClassificationOffsets, - int[] descriptionClassificationStyles) { + @JniType("std::u16string") String description, + @JniType("std::vector<int32_t>") int[] descriptionClassificationOffsets, + @JniType("std::vector<int32_t>") int[] descriptionClassificationStyles) { assert descriptionClassificationOffsets.length == descriptionClassificationStyles.length; mDescription = description; mDescriptionClassifications.clear();
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 5830fa911..a7ebd03d 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
@@ -13,6 +13,7 @@ import androidx.annotation.ColorRes; import androidx.annotation.VisibleForTesting; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.build.annotations.NullMarked; @@ -360,7 +361,9 @@ @NativeMethods public interface Natives { + @JniType("std::vector<int32_t>") int[] parseForEmphasizeComponents( - String text, AutocompleteSchemeClassifier autocompleteSchemeClassifier); + @JniType("std::u16string") String text, + AutocompleteSchemeClassifier autocompleteSchemeClassifier); } }
diff --git a/components/omnibox/browser/autocomplete_match_android.cc b/components/omnibox/browser/autocomplete_match_android.cc index 5f2b531..926b28c 100644 --- a/components/omnibox/browser/autocomplete_match_android.cc +++ b/components/omnibox/browser/autocomplete_match_android.cc
@@ -19,6 +19,7 @@ #include "components/omnibox/common/omnibox_feature_configs.h" #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_bridge.h" #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h" +#include "third_party/jni_zero/default_conversions.h" #include "third_party/omnibox_proto/suggest_template_info.pb.h" #include "url/android/gurl_android.h" @@ -44,15 +45,15 @@ if (java_match_) return ScopedJavaLocalRef<jobject>(*java_match_); - std::vector<int> contents_class_offsets; - std::vector<int> contents_class_styles; + std::vector<int32_t> contents_class_offsets; + std::vector<int32_t> contents_class_styles; for (auto contents_class_item : contents_class) { contents_class_offsets.push_back(contents_class_item.offset); contents_class_styles.push_back(contents_class_item.style); } - std::vector<int> description_class_offsets; - std::vector<int> description_class_styles; + std::vector<int32_t> description_class_offsets; + std::vector<int32_t> description_class_styles; for (auto description_class_item : description_class) { description_class_offsets.push_back(description_class_item.offset); description_class_styles.push_back(description_class_item.style); @@ -67,26 +68,20 @@ } } - ScopedJavaLocalRef<jstring> j_image_dominant_color; - ScopedJavaLocalRef<jstring> j_post_content_type; - ScopedJavaLocalRef<jbyteArray> j_post_content; - std::string clipboard_image_data; - - if (!image_dominant_color.empty()) { - j_image_dominant_color = ConvertUTF8ToJavaString(env, image_dominant_color); + std::string post_content_type; + ScopedJavaLocalRef<jbyteArray> j_post_data; + if (post_content) { + post_content_type = post_content->first; + j_post_data = base::android::ToJavaByteArray(env, post_content->second); } - if (post_content && !post_content->first.empty() && - !post_content->second.empty()) { - j_post_content_type = ConvertUTF8ToJavaString(env, post_content->first); - j_post_content = ToJavaByteArray(env, post_content->second); - } - + ScopedJavaLocalRef<jbyteArray> j_clipboard_image_data; if (search_terms_args.get()) { - clipboard_image_data = search_terms_args->image_thumbnail_content; + j_clipboard_image_data = base::android::ToJavaByteArray( + env, search_terms_args->image_thumbnail_content); } - std::vector<int> temp_subtypes(subtypes.begin(), subtypes.end()); + std::vector<int32_t> temp_subtypes(subtypes.begin(), subtypes.end()); std::vector<jni_zero::ScopedJavaLocalRef<jobject>> actions_list; if (actions.empty() && takeover_action) { @@ -112,28 +107,18 @@ java_match_ = std::make_unique<ScopedJavaGlobalRef<jobject>>( Java_AutocompleteMatch_build( - env, reinterpret_cast<intptr_t>(this), type, - ToJavaIntArray(env, temp_subtypes), IsSearchType(type), icon_type, - transition, ConvertUTF16ToJavaString(env, contents), - ToJavaIntArray(env, contents_class_offsets), - ToJavaIntArray(env, contents_class_styles), - ConvertUTF16ToJavaString(env, description), - ToJavaIntArray(env, description_class_offsets), - ToJavaIntArray(env, description_class_styles), j_answer_template, - answer_type, ConvertUTF16ToJavaString(env, fill_into_edit), - url::GURLAndroid::FromNativeGURL(env, destination_url), - url::GURLAndroid::FromNativeGURL(env, image_url), - j_image_dominant_color, SupportsDeletion(), j_post_content_type, - j_post_content, suggestion_group_id.value_or(omnibox::GROUP_INVALID), - ToJavaByteArray(env, clipboard_image_data), - has_tab_match.value_or(false), actions_list, - allowed_to_be_default_match, - ConvertUTF16ToJavaString(env, inline_autocompletion), - ConvertUTF16ToJavaString(env, additional_text), - tab_groups::UuidToJavaString( - env, matching_tab_group_uuid.value_or(base::Uuid())), - ConvertUTF16ToJavaString(env, associated_keyword), - j_suggest_template)); + env, reinterpret_cast<intptr_t>(this), type, temp_subtypes, + IsSearchType(type), icon_type, transition, contents, + contents_class_offsets, contents_class_styles, description, + description_class_offsets, description_class_styles, + j_answer_template, answer_type, fill_into_edit, destination_url, + image_url, image_dominant_color, SupportsDeletion(), + post_content_type, j_post_data, + suggestion_group_id.value_or(omnibox::GROUP_INVALID), + j_clipboard_image_data, has_tab_match.value_or(false), actions_list, + allowed_to_be_default_match, inline_autocompletion, additional_text, + matching_tab_group_uuid.value_or(base::Uuid()), + associated_keyword, j_suggest_template)); return ScopedJavaLocalRef<jobject>(*java_match_); } @@ -183,24 +168,21 @@ if (!java_match_) return; - std::string clipboard_image_data; + ScopedJavaLocalRef<jbyteArray> j_clipboard_image_data; if (search_terms_args.get()) { - clipboard_image_data = search_terms_args->image_thumbnail_content; + j_clipboard_image_data = base::android::ToJavaByteArray( + env, search_terms_args->image_thumbnail_content); } - ScopedJavaLocalRef<jstring> j_post_content_type; - ScopedJavaLocalRef<jbyteArray> j_post_content; - if (post_content && !post_content->first.empty() && - !post_content->second.empty()) { - j_post_content_type = ConvertUTF8ToJavaString(env, post_content->first); - j_post_content = ToJavaByteArray(env, post_content->second); + ScopedJavaLocalRef<jbyteArray> j_post_data; + if (post_content) { + j_post_data = base::android::ToJavaByteArray(env, post_content->second); } Java_AutocompleteMatch_updateClipboardContent( - env, *java_match_, ConvertUTF16ToJavaString(env, contents), - url::GURLAndroid::FromNativeGURL(env, destination_url), - j_post_content_type, j_post_content, - ToJavaByteArray(env, clipboard_image_data)); + env, *java_match_, contents, destination_url, + post_content ? post_content->first : "", j_post_data, + j_clipboard_image_data); } void AutocompleteMatch::UpdateJavaNavigationDetails() { @@ -215,10 +197,7 @@ } Java_AutocompleteMatch_updateNavigationDetails( - env, *java_match_, - url::GURLAndroid::FromNativeGURL(env, destination_url), - ToJavaArrayOfStrings(env, header_keys), - ToJavaArrayOfStrings(env, header_vals)); + env, *java_match_, destination_url, header_keys, header_vals); } } @@ -241,17 +220,16 @@ void AutocompleteMatch::UpdateJavaDescription() { if (java_match_) { - std::vector<int> description_class_offsets; - std::vector<int> description_class_styles; + std::vector<int32_t> description_class_offsets; + std::vector<int32_t> description_class_styles; for (auto description_class_item : description_class) { description_class_offsets.push_back(description_class_item.offset); description_class_styles.push_back(description_class_item.style); } JNIEnv* env = base::android::AttachCurrentThread(); - Java_AutocompleteMatch_setDescription( - env, *java_match_, ConvertUTF16ToJavaString(env, description), - ToJavaIntArray(env, description_class_offsets), - ToJavaIntArray(env, description_class_styles)); + Java_AutocompleteMatch_setDescription(env, *java_match_, description, + description_class_offsets, + description_class_styles); } }
diff --git a/components/omnibox/browser/mock_aim_eligibility_service.cc b/components/omnibox/browser/mock_aim_eligibility_service.cc index cbb8b03..7681313 100644 --- a/components/omnibox/browser/mock_aim_eligibility_service.cc +++ b/components/omnibox/browser/mock_aim_eligibility_service.cc
@@ -18,6 +18,8 @@ identity_manager, "en-US", std::move(configuration)) { + ON_CALL(*this, IsAimEligible()).WillByDefault(testing::Return(true)); + ON_CALL(*this, IsFuseboxEligible()).WillByDefault(testing::Return(true)); ON_CALL(*this, GetSearchboxConfig()) .WillByDefault(testing::Return(&mock_config)); }
diff --git a/components/omnibox/browser/omnibox_url_emphasizer.cc b/components/omnibox/browser/omnibox_url_emphasizer.cc index c4a639a..70169c1 100644 --- a/components/omnibox/browser/omnibox_url_emphasizer.cc +++ b/components/omnibox/browser/omnibox_url_emphasizer.cc
@@ -8,6 +8,7 @@ #include "components/omnibox/browser/autocomplete_input.h" #include "components/omnibox/browser/autocomplete_scheme_classifier.h" #include "components/omnibox/browser/autocomplete_scheme_classifier_android.h" +#include "third_party/jni_zero/default_conversions.h" // Must come after all headers that specialize FromJniType() / ToJniType(). #include "components/omnibox/browser/scheme_classifier_jni/OmniboxUrlEmphasizer_jni.h" @@ -16,24 +17,20 @@ using base::android::ScopedJavaLocalRef; // static -static ScopedJavaLocalRef<jintArray> +static std::vector<int32_t> JNI_OmniboxUrlEmphasizer_ParseForEmphasizeComponents( - JNIEnv* env, - const JavaRef<jstring>& jtext, + const std::u16string& text, const base::android::JavaRef<jobject>& jautocomplete_scheme_classifier) { AutocompleteSchemeClassifier* autocomplete_scheme_classifier = AutocompleteSchemeClassifierAndroid::FromJavaObj( jautocomplete_scheme_classifier); DCHECK(autocomplete_scheme_classifier); - std::u16string text(base::android::ConvertJavaStringToUTF16(env, jtext)); - url::Component scheme, host; AutocompleteInput::ParseForEmphasizeComponents( text, *autocomplete_scheme_classifier, &scheme, &host); - int emphasize_values[] = {scheme.begin, scheme.len, host.begin, host.len}; - return base::android::ToJavaIntArray(env, emphasize_values); + return {scheme.begin, scheme.len, host.begin, host.len}; } DEFINE_JNI(OmniboxUrlEmphasizer)
diff --git a/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc b/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc index 00f913be..062a6f5 100644 --- a/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc +++ b/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc
@@ -133,4 +133,21 @@ ASSERT_FALSE(session); } +// Verify that CreateSession works for the classifier feature. +TEST(ModelBrokerClientTest, ClassifierSession) { + base::test::TaskEnvironment task_environment_; + OptimizationGuideLogger logger; + FakeModelBroker fake_broker({}); + fake_broker.InstallClassifierModel(FakeBaseModelAsset::Content{}); + + ModelBrokerClient client(fake_broker.BindAndPassRemote(), + logger.GetWeakPtr()); + base::test::TestFuture<ModelBrokerClient::CreateSessionResult> future; + + // Requesting the classifier feature should succeed. + client.CreateSession(mojom::OnDeviceFeature::kClassifier, + SessionConfigParams{}, future.GetCallback()); + ASSERT_TRUE(future.Take()); +} + } // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/model_broker_state.cc b/components/optimization_guide/core/model_execution/model_broker_state.cc index 9d2e076..3df08490 100644 --- a/components/optimization_guide/core/model_execution/model_broker_state.cc +++ b/components/optimization_guide/core/model_execution/model_broker_state.cc
@@ -13,6 +13,7 @@ #include "components/optimization_guide/core/model_execution/on_device_asset_manager.h" #include "components/optimization_guide/core/model_execution/on_device_features.h" #include "components/optimization_guide/core/model_execution/on_device_model_access_controller.h" +#include "components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h" #include "components/optimization_guide/core/model_execution/on_device_model_service_controller.h" #include "components/optimization_guide/core/optimization_guide_features.h" #include "components/optimization_guide/public/mojom/model_broker.mojom.h" @@ -41,8 +42,17 @@ on_device_model::ServiceClient::LaunchFn launch_fn, component_updater::ComponentUpdateService* component_update_service) : service_client_(std::move(launch_fn)), - download_progress_manager_(component_update_service, - {base_delegate->GetComponentId()}), + // Note: tracking multiple components makes the download progress relative + // to the total size of all components. Since we only download the + // perspective model when it is used, this can result in the progress + // jumping if only one model is being downloaded. + // TODO(crbug.com/491858797): Clean up observer for classifier model. + download_progress_manager_( + component_update_service, + classifier_delegate + ? std::vector<std::string>{base_delegate->GetComponentId(), + classifier_delegate->GetComponentId()} + : std::vector<std::string>{base_delegate->GetComponentId()}), usage_tracker_(&local_state), model_broker_impl_( usage_tracker_, @@ -53,8 +63,9 @@ component_state_manager_(&local_state, performance_classifier_.GetSafeRef(), usage_tracker_, - std::move(base_delegate)), - service_controller_( + std::move(base_delegate), + OnDeviceModelServiceController::kModelType), + base_model_controller_( service_client_, usage_tracker_, model_broker_impl_, @@ -63,8 +74,15 @@ asset_manager_(local_state, usage_tracker_, component_state_manager_, - service_controller_, - model_provider) {} + base_model_controller_, + model_provider) { + if (classifier_delegate) { + classifier_controller_.emplace( + local_state, performance_classifier_.GetSafeRef(), usage_tracker_, + service_client_.GetSafeRef(), model_broker_impl_, + std::move(classifier_delegate)); + } +} ModelBrokerState::~ModelBrokerState() = default; void ModelBrokerState::BindModelBroker( @@ -113,7 +131,11 @@ base::ToString(feature)); // Ensure a solution is constructed for this feature, to avoid returning // kUnknown when this is called too early. - service_controller_.UpdateSolutionProvider(feature); + base_model_controller_.UpdateSolutionProvider(feature); + if (classifier_controller_) { + classifier_controller_->UpdateSolution(); + } + return model_broker_impl_.GetSolutionProvider(feature).solution().error_or( OnDeviceModelEligibilityReason::kSuccess); } @@ -208,7 +230,7 @@ if (!features::IsOnDeviceExecutionEnabled()) { return {}; } - auto capabilities = service_controller_.GetCapabilities(); + auto capabilities = base_model_controller_.GetCapabilities(); capabilities.RetainAll( performance_classifier_.GetPossibleOnDeviceCapabilities()); return capabilities;
diff --git a/components/optimization_guide/core/model_execution/model_broker_state.h b/components/optimization_guide/core/model_execution/model_broker_state.h index 11e4ae600c..cf6b7da 100644 --- a/components/optimization_guide/core/model_execution/model_broker_state.h +++ b/components/optimization_guide/core/model_execution/model_broker_state.h
@@ -11,6 +11,7 @@ #include "components/optimization_guide/core/delivery/optimization_guide_model_provider.h" #include "components/optimization_guide/core/model_execution/on_device_asset_manager.h" #include "components/optimization_guide/core/model_execution/on_device_capability.h" +#include "components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h" #include "components/optimization_guide/core/model_execution/on_device_model_download_progress_manager.h" #include "components/optimization_guide/core/model_execution/on_device_model_service_controller.h" #include "components/optimization_guide/core/model_execution/performance_class.h" @@ -48,8 +49,8 @@ return component_state_manager_; } - OnDeviceModelServiceController& service_controller() { - return service_controller_; + OnDeviceModelServiceController& base_model_controller() { + return base_model_controller_; } // OnDeviceCapability @@ -94,10 +95,9 @@ ModelBrokerImpl model_broker_impl_; PerformanceClassifier performance_classifier_; OnDeviceModelComponentStateManager component_state_manager_; - OnDeviceModelServiceController service_controller_; + OnDeviceModelServiceController base_model_controller_; + std::optional<OnDeviceModelClassifierController> classifier_controller_; OnDeviceAssetManager asset_manager_; - std::unique_ptr<OnDeviceModelComponentStateManager::Delegate> - classifier_delegate_; base::WeakPtrFactory<ModelBrokerState> weak_ptr_factory_{this}; };
diff --git a/components/optimization_guide/core/model_execution/on_device_asset_manager_unittest.cc b/components/optimization_guide/core/model_execution/on_device_asset_manager_unittest.cc index e5008c2..ca0221d 100644 --- a/components/optimization_guide/core/model_execution/on_device_asset_manager_unittest.cc +++ b/components/optimization_guide/core/model_execution/on_device_asset_manager_unittest.cc
@@ -112,7 +112,7 @@ fake_safety.model_info()); EXPECT_FALSE(broker_.GetOrCreateBrokerState() - .service_controller() + .base_model_controller() .GetSafetyClientForTesting() .safety_model_info()); } @@ -124,7 +124,7 @@ UpdateTarget(proto::OPTIMIZATION_TARGET_TEXT_SAFETY, fake_safety.model_info()); ASSERT_TRUE(broker_.GetOrCreateBrokerState() - .service_controller() + .base_model_controller() .GetSafetyClientForTesting() .safety_model_info()); } @@ -136,7 +136,7 @@ UpdateTarget(proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION, fake_language.model_info()); EXPECT_EQ(fake_language.model_path(), broker_.GetOrCreateBrokerState() - .service_controller() + .base_model_controller() .GetSafetyClientForTesting() .language_detection_model_path()); }
diff --git a/components/optimization_guide/core/model_execution/on_device_features.cc b/components/optimization_guide/core/model_execution/on_device_features.cc index c508b42..930bfc0 100644 --- a/components/optimization_guide/core/model_execution/on_device_features.cc +++ b/components/optimization_guide/core/model_execution/on_device_features.cc
@@ -38,6 +38,25 @@ } } +OnDeviceModelType GetOnDeviceModelType(mojom::OnDeviceFeature feature) { + switch (feature) { + case mojom::OnDeviceFeature::kClassifier: + return OnDeviceModelType::kClassifierModel; + case mojom::OnDeviceFeature::kCompose: + case mojom::OnDeviceFeature::kTest: + case mojom::OnDeviceFeature::kPromptApi: + case mojom::OnDeviceFeature::kHistorySearch: + case mojom::OnDeviceFeature::kSummarize: + case mojom::OnDeviceFeature::kHistoryQueryIntent: + case mojom::OnDeviceFeature::kScamDetection: + case mojom::OnDeviceFeature::kPermissionsAi: + case mojom::OnDeviceFeature::kProofreaderApi: + case mojom::OnDeviceFeature::kWritingAssistanceApi: + case mojom::OnDeviceFeature::kOnDeviceSpeechRecognition: + return OnDeviceModelType::kBaseModel; + } +} + // To enable on-device execution for a feature, update this to return a // non-null target. proto::OptimizationTarget GetOptimizationTargetForFeature(
diff --git a/components/optimization_guide/core/model_execution/on_device_features.h b/components/optimization_guide/core/model_execution/on_device_features.h index eb905ed..61d08ff8 100644 --- a/components/optimization_guide/core/model_execution/on_device_features.h +++ b/components/optimization_guide/core/model_execution/on_device_features.h
@@ -20,10 +20,19 @@ mojom::OnDeviceFeature::kMinValue, mojom::OnDeviceFeature::kMaxValue>; +enum class OnDeviceModelType { + kBaseModel, + kClassifierModel, +}; + // Return the name to use in histogram variants for this feature key. COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES) std::string_view GetVariantName(mojom::OnDeviceFeature feature); +// Returns the model type required for the feature. +COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES) +OnDeviceModelType GetOnDeviceModelType(mojom::OnDeviceFeature feature); + // Returns which ModelExecutionFeature is used for this feature key. COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES) proto::ModelExecutionFeature ToModelExecutionFeatureProto(
diff --git a/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc b/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc index 46aa0ad..4ef7410 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc
@@ -15,6 +15,7 @@ #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "base/trace_event/trace_event.h" +#include "components/optimization_guide/core/model_execution/on_device_features.h" #include "components/optimization_guide/core/model_execution/on_device_model_component.h" #include "components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h" #include "components/optimization_guide/core/model_execution/on_device_model_metadata.h" @@ -155,7 +156,8 @@ component_state_manager_(&local_state, performance_classifier, usage_tracker, - std::move(delegate)) { + std::move(delegate), + kModelType) { component_state_manager_.AddObserver(this); }
diff --git a/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h b/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h index d36849c0d..f400405 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h +++ b/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h
@@ -29,6 +29,10 @@ class OnDeviceModelClassifierController : public OnDeviceModelComponentStateManager::Observer { public: + // The model type managed by this controller. + static constexpr OnDeviceModelType kModelType = + OnDeviceModelType::kClassifierModel; + OnDeviceModelClassifierController( PrefService& local_state, base::SafeRef<PerformanceClassifier> performance_classifier,
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component.cc b/components/optimization_guide/core/model_execution/on_device_model_component.cc index 7a45e3f..a751b8d 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_component.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_component.cc
@@ -184,6 +184,15 @@ } } +bool WasOnDeviceModelRecentlyUsed(UsageTracker* usage_tracker, + OnDeviceModelType model_type) { + return std::ranges::any_of( + OnDeviceFeatureSet::All(), [&](mojom::OnDeviceFeature feature) { + return GetOnDeviceModelType(feature) == model_type && + usage_tracker->WasOnDeviceEligibleFeatureRecentlyUsed(feature); + }); +} + } // namespace std::ostream& operator<<(std::ostream& out, OnDeviceModelStatus status) { @@ -271,11 +280,13 @@ PrefService* local_state, base::SafeRef<PerformanceClassifier> performance_classifier, UsageTracker& usage_tracker, - std::unique_ptr<Delegate> delegate) + std::unique_ptr<Delegate> delegate, + OnDeviceModelType model_type) : local_state_(local_state), performance_classifier_(std::move(performance_classifier)), delegate_(std::move(delegate)), - usage_tracker_(usage_tracker) { + usage_tracker_(usage_tracker), + model_type_(model_type) { CHECK(local_state); // Useful to catch poor test setup. usage_tracker_observation_.Observe(&usage_tracker); pref_change_registrar_.Init(local_state); @@ -419,6 +430,10 @@ mojom::OnDeviceFeature feature) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (GetOnDeviceModelType(feature) != model_type_) { + return; + } + base::UmaHistogramEnumeration( "OptimizationGuide.ModelExecution.OnDeviceModelStatusAtUseTime", GetOnDeviceModelStatus()); @@ -480,7 +495,7 @@ result.disk_space_free = disk_space_free_bytes; result.device_capable = performance_classifier_->IsDeviceCapable(); result.on_device_feature_recently_used = - usage_tracker_->WasAnyOnDeviceEligibleFeatureRecentlyUsed(); + WasOnDeviceModelRecentlyUsed(&usage_tracker_.get(), model_type_); result.enabled_by_feature = features::IsOnDeviceExecutionEnabled(); result.enabled_by_enterprise_policy = GetGenAILocalFoundationalModelEnterprisePolicySettings(local_state_) ==
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component.h b/components/optimization_guide/core/model_execution/on_device_model_component.h index 3cf5dc4..7b46b8f8 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_component.h +++ b/components/optimization_guide/core/model_execution/on_device_model_component.h
@@ -26,6 +26,7 @@ #include "base/types/pass_key.h" #include "base/values.h" #include "base/version.h" +#include "components/optimization_guide/core/model_execution/on_device_features.h" #include "components/optimization_guide/core/model_execution/performance_class.h" #include "components/optimization_guide/core/model_execution/usage_tracker.h" #include "components/optimization_guide/core/optimization_guide_enums.h" @@ -307,7 +308,8 @@ PrefService* local_state, base::SafeRef<PerformanceClassifier> performance_classifier, UsageTracker& usage_tracker, - std::unique_ptr<Delegate> delegate); + std::unique_ptr<Delegate> delegate, + OnDeviceModelType model_type); ~OnDeviceModelComponentStateManager() override; // Returns whether the component installation is valid. @@ -424,6 +426,9 @@ SEQUENCE_CHECKER(sequence_checker_); + // The model type managed by this state manager. + const OnDeviceModelType model_type_; + base::WeakPtrFactory<OnDeviceModelComponentStateManager> weak_ptr_factory_{ this}; };
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc b/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc index 496b634a..b29edf63 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc
@@ -144,6 +144,7 @@ FakeModelBroker broker_{{ .performance_class = OnDeviceModelPerformanceClass::kUnknown, .preinstall_base_model = false, + .include_classifier = false, }}; base::HistogramTester histograms_; };
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc index bc8ca02..c97feee 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
@@ -306,6 +306,9 @@ void OnDeviceModelServiceController::UpdateSolutionProvider( mojom::OnDeviceFeature feature) { + if (GetOnDeviceModelType(feature) != kModelType) { + return; + } // Note: This always constructs the Solution, even if the provider was not // constructed yet, to update supported_adaptation_ranks_ on the base model. model_broker_impl_->GetSolutionProvider(feature).Update(GetSolution(feature));
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h index 964df88..7e8fb2f 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h +++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h
@@ -70,6 +70,9 @@ // a single instance of this object. class OnDeviceModelServiceController final { public: + // The model type managed by this controller. + static constexpr OnDeviceModelType kModelType = OnDeviceModelType::kBaseModel; + OnDeviceModelServiceController( on_device_model::ServiceClient& service_client, UsageTracker& usage_tracker,
diff --git a/components/optimization_guide/core/model_execution/test/fake_model_broker.cc b/components/optimization_guide/core/model_execution/test/fake_model_broker.cc index 8e02924..3b06cf8 100644 --- a/components/optimization_guide/core/model_execution/test/fake_model_broker.cc +++ b/components/optimization_guide/core/model_execution/test/fake_model_broker.cc
@@ -39,7 +39,7 @@ } ModelBrokerPrefService::~ModelBrokerPrefService() = default; -FakeModelBroker::FakeModelBroker(const Options& options) { +FakeModelBroker::FakeModelBroker(const Options& options) : options_(options) { if (options.performance_class != OnDeviceModelPerformanceClass::kUnknown) { UpdatePerformanceClassPref(&local_state_.local_state(), options.performance_class); @@ -66,6 +66,17 @@ component_state_.Install(std::move(asset)); } +void FakeModelBroker::InstallClassifierModel( + FakeBaseModelAsset::Content content) { + InstallClassifierModel( + std::make_unique<FakeBaseModelAsset>(std::move(content))); +} + +void FakeModelBroker::InstallClassifierModel( + std::unique_ptr<FakeBaseModelAsset> asset) { + classifier_component_state_.Install(std::move(asset)); +} + void FakeModelBroker::UpdateTarget(proto::OptimizationTarget target, const ModelInfo& model_info) { model_provider_.UpdateModelImmediatelyForTesting( @@ -89,11 +100,14 @@ ModelBrokerState& FakeModelBroker::GetOrCreateBrokerState() { if (!model_broker_state_) { - model_broker_state_.emplace(local_state_.local_state(), model_provider_, - component_state_.CreateDelegate(), - component_state_.CreateDelegate(), - fake_launcher_.LaunchFn(), - &component_state_.component_update_service()); + model_broker_state_.emplace( + local_state_.local_state(), model_provider_, + component_state_.CreateDelegate(), + options_.include_classifier + ? classifier_component_state_.CreateDelegate() + : nullptr, + fake_launcher_.LaunchFn(), + &component_state_.component_update_service()); } return *model_broker_state_; }
diff --git a/components/optimization_guide/core/model_execution/test/fake_model_broker.h b/components/optimization_guide/core/model_execution/test/fake_model_broker.h index f072772..28a4cb7 100644 --- a/components/optimization_guide/core/model_execution/test/fake_model_broker.h +++ b/components/optimization_guide/core/model_execution/test/fake_model_broker.h
@@ -57,6 +57,8 @@ OnDeviceModelPerformanceClass::kHigh; // If true, installs a base model to the component_state_. bool preinstall_base_model = true; + // If true, initializes the classifier controller. + bool include_classifier = true; }; explicit FakeModelBroker(const Options& options); ~FakeModelBroker(); @@ -70,11 +72,14 @@ void SimulateShutdown() { model_broker_state_.reset(); component_state_.SimulateShutdown(); + classifier_component_state_.SimulateShutdown(); } void CrashService() { fake_launcher_.CrashService(); } void InstallBaseModel(FakeBaseModelAsset::Content content); void InstallBaseModel(std::unique_ptr<FakeBaseModelAsset> asset); + void InstallClassifierModel(FakeBaseModelAsset::Content content); + void InstallClassifierModel(std::unique_ptr<FakeBaseModelAsset> asset); void UpdateTarget(proto::OptimizationTarget target, const ModelInfo& model_info); void UpdateModelAdaptation(const FakeAdaptationAsset& asset); @@ -83,6 +88,9 @@ PrefService& local_state() { return local_state_.local_state(); } TestComponentState& component_state() { return component_state_; } + TestComponentState& classifier_component_state() { + return classifier_component_state_; + } ModelProviderRegistry& model_provider() { return model_provider_; } // Lazily instantiates model_broker_state_ @@ -94,11 +102,13 @@ on_device_model::FakeServiceLauncher& launcher() { return fake_launcher_; } private: + Options options_; ScopedModelBrokerFeatureList feature_list_; ModelBrokerPrefService local_state_; on_device_model::FakeOnDeviceServiceSettings fake_settings_; on_device_model::FakeServiceLauncher fake_launcher_{&fake_settings_}; TestComponentState component_state_; + TestComponentState classifier_component_state_; OptimizationGuideLogger logger_; ModelProviderRegistry model_provider_{&logger_}; std::optional<ModelBrokerState> model_broker_state_;
diff --git a/components/optimization_guide/core/model_execution/usage_tracker.cc b/components/optimization_guide/core/model_execution/usage_tracker.cc index 4b8f4ff..4b3c7ba 100644 --- a/components/optimization_guide/core/model_execution/usage_tracker.cc +++ b/components/optimization_guide/core/model_execution/usage_tracker.cc
@@ -47,14 +47,6 @@ feature); } -bool UsageTracker::WasAnyOnDeviceEligibleFeatureRecentlyUsed() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return std::ranges::any_of( - OnDeviceFeatureSet::All(), [&](mojom::OnDeviceFeature feature) { - return WasOnDeviceEligibleFeatureRecentlyUsed(feature); - }); -} - void UsageTracker::AddObserver(Observer* observer) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); observers_.AddObserver(observer);
diff --git a/components/optimization_guide/core/model_execution/usage_tracker.h b/components/optimization_guide/core/model_execution/usage_tracker.h index 31096b1..824e6f4 100644 --- a/components/optimization_guide/core/model_execution/usage_tracker.h +++ b/components/optimization_guide/core/model_execution/usage_tracker.h
@@ -9,6 +9,7 @@ #include "base/observer_list.h" #include "base/observer_list_types.h" #include "base/sequence_checker.h" +#include "components/optimization_guide/core/model_execution/on_device_features.h" #include "components/optimization_guide/public/mojom/model_broker.mojom-shared.h" class PrefService; @@ -44,9 +45,6 @@ bool WasOnDeviceEligibleFeatureRecentlyUsed( mojom::OnDeviceFeature feature) const; - // Returns whether any on-device eligible feature was recently used. - bool WasAnyOnDeviceEligibleFeatureRecentlyUsed() const; - void AddObserver(Observer* observer); void RemoveObserver(Observer* observer);
diff --git a/components/page_content_annotations/content/page_category_classifier_bridge_impl.cc b/components/page_content_annotations/content/page_category_classifier_bridge_impl.cc index ba657611..3ba5d280 100644 --- a/components/page_content_annotations/content/page_category_classifier_bridge_impl.cc +++ b/components/page_content_annotations/content/page_category_classifier_bridge_impl.cc
@@ -4,7 +4,10 @@ #include "components/page_content_annotations/content/page_category_classifier_bridge_impl.h" +#include "base/metrics/histogram_functions.h" +#include "base/numerics/safe_conversions.h" #include "components/page_content_annotations/core/on_device_category_classifier.h" +#include "components/page_content_annotations/core/page_content_annotation_type.h" #include "content/public/browser/page.h" namespace page_content_annotations { @@ -32,10 +35,33 @@ for (const auto& embedding : embeddings) { if (embedding.passage.second == EmbeddingPassageType::kTitleAndUrl) { category_classifier_->OnPageEmbeddingAvailable( - page.GetMainDocument().GetLastCommittedURL(), embedding.embedding); + page.GetMainDocument().GetLastCommittedURL(), embedding.embedding, + base::BindOnce( + &PageCategoryClassifierBridgeImpl::OnCategoryClassifiersCompleted, + weak_ptr_factory_.GetWeakPtr())); return; } } } +void PageCategoryClassifierBridgeImpl::OnCategoryClassifiersCompleted( + const std::vector<Category>& outputs) { + for (const Category& category : outputs) { + switch (category.category_type) { + case CategoryType::kEducation: + base::UmaHistogramPercentage( + "OptimizationGuide.PageContentAnnotations.CategoryClassifier." + "EducationScore", + base::ClampRound(category.score * 100)); + break; + case CategoryType::kShopping: + base::UmaHistogramPercentage( + "OptimizationGuide.PageContentAnnotations.CategoryClassifier." + "ShoppingScore", + base::ClampRound(category.score * 100)); + break; + } + } +} + } // namespace page_content_annotations
diff --git a/components/page_content_annotations/content/page_category_classifier_bridge_impl.h b/components/page_content_annotations/content/page_category_classifier_bridge_impl.h index b904374..d313c47b 100644 --- a/components/page_content_annotations/content/page_category_classifier_bridge_impl.h +++ b/components/page_content_annotations/content/page_category_classifier_bridge_impl.h
@@ -5,10 +5,14 @@ #ifndef COMPONENTS_PAGE_CONTENT_ANNOTATIONS_CONTENT_PAGE_CATEGORY_CLASSIFIER_BRIDGE_IMPL_H_ #define COMPONENTS_PAGE_CONTENT_ANNOTATIONS_CONTENT_PAGE_CATEGORY_CLASSIFIER_BRIDGE_IMPL_H_ +#include <vector> + #include "base/memory/raw_ref.h" +#include "base/memory/weak_ptr.h" #include "base/scoped_observation.h" #include "components/page_content_annotations/content/page_embeddings_service.h" #include "components/page_content_annotations/core/page_category_classifier_bridge.h" +#include "components/page_content_annotations/core/page_content_annotation_type.h" namespace page_content_annotations { @@ -34,12 +38,17 @@ void OnPageEmbeddingsAvailable(content::Page& page) override; private: + void OnCategoryClassifiersCompleted(const std::vector<Category>& outputs); + const raw_ref<PageEmbeddingsService> page_embeddings_service_; const raw_ref<OnDeviceCategoryClassifier> category_classifier_; base::ScopedObservation<PageEmbeddingsService, PageEmbeddingsService::Observer> scoped_observation_{this}; + + base::WeakPtrFactory<PageCategoryClassifierBridgeImpl> weak_ptr_factory_{ + this}; }; } // namespace page_content_annotations
diff --git a/components/page_content_annotations/core/on_device_category_classifier.cc b/components/page_content_annotations/core/on_device_category_classifier.cc index 6e356c9..573d15d9 100644 --- a/components/page_content_annotations/core/on_device_category_classifier.cc +++ b/components/page_content_annotations/core/on_device_category_classifier.cc
@@ -42,14 +42,15 @@ void OnDeviceCategoryClassifier::OnPageEmbeddingAvailable( const GURL& url, - const passage_embeddings::Embedding& embedding) { + const passage_embeddings::Embedding& embedding, + base::OnceCallback<void(const std::vector<Category>&)> callback) { // Run each of the category classifiers on the returned embedding. auto barrier_callback = base::BarrierCallback<std::pair<CategoryType, std::optional<float>>>( category_classifier_model_handlers_.size(), base::BindOnce( &OnDeviceCategoryClassifier::OnCategoryClassifiersCompleted, - weak_ptr_factory_.GetWeakPtr(), url)); + weak_ptr_factory_.GetWeakPtr(), std::move(callback), url)); for (const auto& [category_type, model_handler] : category_classifier_model_handlers_) { model_handler->ExecuteModelWithInput( @@ -60,6 +61,7 @@ } void OnDeviceCategoryClassifier::OnCategoryClassifiersCompleted( + base::OnceCallback<void(const std::vector<Category>&)> callback, const GURL& url, const std::vector<std::pair<CategoryType, std::optional<float>>>& classifier_outputs) { @@ -70,6 +72,9 @@ {.category_type = category_type, .score = *maybe_score}); } } + if (!callback.is_null()) { + std::move(callback).Run(outputs); + } } } // namespace page_content_annotations
diff --git a/components/page_content_annotations/core/on_device_category_classifier.h b/components/page_content_annotations/core/on_device_category_classifier.h index d331234..62fe522 100644 --- a/components/page_content_annotations/core/on_device_category_classifier.h +++ b/components/page_content_annotations/core/on_device_category_classifier.h
@@ -14,6 +14,7 @@ #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "components/optimization_guide/core/inference/model_handler.h" +#include "components/page_content_annotations/core/page_content_annotation_type.h" #include "components/passage_embeddings/core/passage_embeddings_types.h" namespace optimization_guide { @@ -39,12 +40,15 @@ delete; // Invoked when an embedding has been successfully computed for the page. - void OnPageEmbeddingAvailable(const GURL& url, - const passage_embeddings::Embedding& embedding); + void OnPageEmbeddingAvailable( + const GURL& url, + const passage_embeddings::Embedding& embedding, + base::OnceCallback<void(const std::vector<Category>&)> callback); private: // Callback invoked when all category classifiers have completed execution. void OnCategoryClassifiersCompleted( + base::OnceCallback<void(const std::vector<Category>&)> callback, const GURL& url, const std::vector<std::pair<CategoryType, std::optional<float>>>& classifier_outputs);
diff --git a/components/page_content_annotations/core/page_content_annotation_type.h b/components/page_content_annotations/core/page_content_annotation_type.h index 8bc50775..55a2cf73 100644 --- a/components/page_content_annotations/core/page_content_annotation_type.h +++ b/components/page_content_annotations/core/page_content_annotation_type.h
@@ -37,9 +37,10 @@ enum class CategoryType { kEducation = 0, + kShopping = 1, // Add new types above this line. - kMaxValue = kEducation, + kMaxValue = kShopping, }; struct Category {
diff --git a/components/permissions/android/android_permission_util.cc b/components/permissions/android/android_permission_util.cc index 4c2c799..e0200e816 100644 --- a/components/permissions/android/android_permission_util.cc +++ b/components/permissions/android/android_permission_util.cc
@@ -7,6 +7,7 @@ #include <variant> #include "base/android/jni_array.h" +#include "base/android/jni_string.h" #include "base/auto_reset.h" #include "base/metrics/histogram_functions.h" #include "base/notreached.h" @@ -18,6 +19,7 @@ #include "components/permissions/permission_uma_util.h" #include "components/permissions/permission_util.h" #include "content/public/browser/web_contents.h" +#include "third_party/jni_zero/default_conversions.h" #include "ui/android/window_android.h" // Must come after all headers that specialize FromJniType() / ToJniType(). @@ -45,22 +47,22 @@ ContentSettingsType content_settings_type, std::vector<std::string>* out) { JNIEnv* env = base::android::AttachCurrentThread(); - base::android::AppendJavaStringArrayToStringVector( - env, + std::vector<std::string> result = Java_PermissionUtil_getRequiredAndroidPermissionsForContentSetting( - env, static_cast<int>(content_settings_type)), - out); + env, static_cast<int>(content_settings_type)); + out->insert(out->end(), std::make_move_iterator(result.begin()), + std::make_move_iterator(result.end())); } void AppendOptionalAndroidPermissionsForContentSetting( ContentSettingsType content_settings_type, std::vector<std::string>* out) { JNIEnv* env = base::android::AttachCurrentThread(); - base::android::AppendJavaStringArrayToStringVector( - env, + std::vector<std::string> result = Java_PermissionUtil_getOptionalAndroidPermissionsForContentSetting( - env, static_cast<int>(content_settings_type)), - out); + env, static_cast<int>(content_settings_type)); + out->insert(out->end(), std::make_move_iterator(result.begin()), + std::make_move_iterator(result.end())); } bool HasRequiredAndroidPermissionsForContentSetting( @@ -68,8 +70,7 @@ ContentSettingsType content_settings_type) { JNIEnv* env = base::android::AttachCurrentThread(); return Java_AndroidPermissionRequester_hasRequiredAndroidPermissionsForContentSetting( - env, window_android->GetJavaObject(), - static_cast<int>(content_settings_type)); + env, window_android, static_cast<int>(content_settings_type)); } PermissionRepromptState ShouldRepromptUserForPermissions( @@ -139,7 +140,7 @@ auto* window_android = web_contents->GetNativeView()->GetWindowAndroid(); DCHECK(window_android); return Java_PermissionUtil_needsLocationPermissionForBluetooth( - env, window_android->GetJavaObject()); + env, window_android); } bool NeedsNearbyDevicesPermissionForBluetooth( @@ -148,7 +149,7 @@ auto* window_android = web_contents->GetNativeView()->GetWindowAndroid(); DCHECK(window_android); return Java_PermissionUtil_needsNearbyDevicesPermissionForBluetooth( - env, window_android->GetJavaObject()); + env, window_android); } bool NeedsLocationServicesForBluetooth() { @@ -162,7 +163,7 @@ auto* window_android = web_contents->GetNativeView()->GetWindowAndroid(); DCHECK(window_android); return Java_PermissionUtil_canRequestSystemPermissionsForBluetooth( - env, window_android->GetJavaObject()); + env, window_android); } bool HasSystemPermission(ContentSettingsType type, @@ -193,7 +194,7 @@ auto* window_android = web_contents->GetNativeView()->GetWindowAndroid(); DCHECK(window_android); return Java_PermissionUtil_canRequestSystemPermission( - env, static_cast<int>(type), window_android->GetJavaObject()); + env, static_cast<int>(type), window_android); } void RequestSystemPermissionsForBluetooth(content::WebContents* web_contents) { @@ -201,16 +202,15 @@ auto* window_android = web_contents->GetNativeView()->GetWindowAndroid(); DCHECK(window_android); // TODO(crbug.com/40255210): Pass the callback from native layer. - return Java_PermissionUtil_requestSystemPermissionsForBluetooth( - env, window_android->GetJavaObject(), nullptr); + Java_PermissionUtil_requestSystemPermissionsForBluetooth(env, window_android, + nullptr); } void RequestLocationServices(content::WebContents* web_contents) { JNIEnv* env = base::android::AttachCurrentThread(); auto* window_android = web_contents->GetNativeView()->GetWindowAndroid(); DCHECK(window_android); - return Java_PermissionUtil_requestLocationServices( - env, window_android->GetJavaObject()); + Java_PermissionUtil_requestLocationServices(env, window_android); } base::AutoReset<bool> EnableSystemLocationSettingForTesting() { @@ -226,7 +226,7 @@ JNIEnv* env = base::android::AttachCurrentThread(); Java_PermissionUtil_handlePermissionPromptAllow( - env, window_android->GetJavaObject(), web_contents->GetJavaWebContents(), + env, window_android, web_contents, static_cast<int>(content_settings_type)); } @@ -310,18 +310,14 @@ // valid. static void JNI_PermissionUtil_DismissNotificationsPermissionRequest( JNIEnv* env, - const base::android::JavaRef<jobject>& jweb_contents) { - content::WebContents* web_contents = - content::WebContents::FromJavaWebContents(jweb_contents); + content::WebContents* web_contents) { permissions::internal::DismissNotificationsPermissionRequest(web_contents); } static void JNI_PermissionUtil_ResolveNotificationsPermissionRequest( JNIEnv* env, - const base::android::JavaRef<jobject>& jweb_contents, + content::WebContents* web_contents, int32_t content_setting) { - content::WebContents* web_contents = - content::WebContents::FromJavaWebContents(jweb_contents); ContentSetting setting = static_cast<ContentSetting>(content_setting); permissions::internal::ResolveNotificationsPermissionRequest(web_contents, setting); @@ -333,9 +329,7 @@ // omnibox. static void JNI_PermissionUtil_NotifyQuietIconDismissed( JNIEnv* env, - const base::android::JavaRef<jobject>& jweb_contents) { - content::WebContents* web_contents = - content::WebContents::FromJavaWebContents(jweb_contents); + content::WebContents* web_contents) { if (!web_contents) { return; }
diff --git a/components/permissions/android/bluetooth_chooser_android.cc b/components/permissions/android/bluetooth_chooser_android.cc index 50b5ee6..ac5a916a 100644 --- a/components/permissions/android/bluetooth_chooser_android.cc +++ b/components/permissions/android/bluetooth_chooser_android.cc
@@ -140,17 +140,15 @@ } void BluetoothChooserAndroid::OnDialogFinished( - JNIEnv* env, content::BluetoothChooserEvent event, - const JavaRef<jstring>& device_id) { + const std::string& device_id) { switch (event) { case content::BluetoothChooserEvent::DENIED_PERMISSION: case content::BluetoothChooserEvent::CANCELLED: event_handler_.Run(event, ""); return; case content::BluetoothChooserEvent::SELECTED: - event_handler_.Run( - event, base::android::ConvertJavaStringToUTF8(env, device_id)); + event_handler_.Run(event, device_id); return; default: NOTREACHED(); @@ -165,17 +163,17 @@ RestartSearch(); } -void BluetoothChooserAndroid::ShowBluetoothOverviewLink(JNIEnv* env) { +void BluetoothChooserAndroid::ShowBluetoothOverviewLink() { OpenURL(kChooserBluetoothOverviewURL); event_handler_.Run(content::BluetoothChooserEvent::SHOW_OVERVIEW_HELP, ""); } -void BluetoothChooserAndroid::ShowBluetoothAdapterOffLink(JNIEnv* env) { +void BluetoothChooserAndroid::ShowBluetoothAdapterOffLink() { OpenURL(kChooserBluetoothOverviewURL); event_handler_.Run(content::BluetoothChooserEvent::SHOW_ADAPTER_OFF_HELP, ""); } -void BluetoothChooserAndroid::ShowNeedLocationPermissionLink(JNIEnv* env) { +void BluetoothChooserAndroid::ShowNeedLocationPermissionLink() { OpenURL(kChooserBluetoothOverviewURL); event_handler_.Run(content::BluetoothChooserEvent::SHOW_NEED_LOCATION_HELP, "");
diff --git a/components/permissions/android/bluetooth_chooser_android.h b/components/permissions/android/bluetooth_chooser_android.h index aa43fed..061f108 100644 --- a/components/permissions/android/bluetooth_chooser_android.h +++ b/components/permissions/android/bluetooth_chooser_android.h
@@ -54,18 +54,17 @@ int signal_strength_level) override; // Report the dialog's result. - void OnDialogFinished(JNIEnv* env, - content::BluetoothChooserEvent event, - const base::android::JavaRef<jstring>& device_id); + void OnDialogFinished(content::BluetoothChooserEvent event, + const std::string& device_id); // Notify bluetooth stack that the search needs to be re-issued. void RestartSearch(); // Calls RestartSearch(). Unused JNI parameters enable calls from Java. void RestartSearch(JNIEnv*); - void ShowBluetoothOverviewLink(JNIEnv* env); - void ShowBluetoothAdapterOffLink(JNIEnv* env); - void ShowNeedLocationPermissionLink(JNIEnv* env); + void ShowBluetoothOverviewLink(); + void ShowBluetoothAdapterOffLink(); + void ShowNeedLocationPermissionLink(); static std::unique_ptr<BluetoothChooserAndroid> CreateForTesting( content::RenderFrameHost* frame,
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/AndroidPermissionRequester.java b/components/permissions/android/java/src/org/chromium/components/permissions/AndroidPermissionRequester.java index f5d7a710..324b69c 100644 --- a/components/permissions/android/java/src/org/chromium/components/permissions/AndroidPermissionRequester.java +++ b/components/permissions/android/java/src/org/chromium/components/permissions/AndroidPermissionRequester.java
@@ -15,6 +15,7 @@ import android.widget.TextView; import org.jni_zero.CalledByNative; +import org.jni_zero.JniType; import org.chromium.base.ApkInfo; import org.chromium.base.metrics.RecordHistogram; @@ -85,7 +86,7 @@ */ @CalledByNative public static boolean hasRequiredAndroidPermissionsForContentSetting( - AndroidPermissionDelegate permissionDelegate, + @JniType("ui::WindowAndroid*") AndroidPermissionDelegate permissionDelegate, @ContentSettingsType.EnumType int contentSettingsType) { Set<String> missingPermissions = filterPermissionsKeepMissing( @@ -163,10 +164,10 @@ for (int i = 0; i < grantResults.length; i++) { String histogramName = null; switch (permissions[i]) { - // Even in the case of Fine location, Coarse - // location permission is also granted. We don't - // need to record histogram for both of them - // separately. + // Even in the case of Fine location, Coarse + // location permission is also granted. We don't + // need to record histogram for both of them + // separately. case "android.permission.ACCESS_COARSE_LOCATION": histogramName = "Permissions.AndroidSystemLevel.Location.Prompt.Shown";
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/BluetoothChooserDialog.java b/components/permissions/android/java/src/org/chromium/components/permissions/BluetoothChooserDialog.java index b312abf4..ff86ea7e 100644 --- a/components/permissions/android/java/src/org/chromium/components/permissions/BluetoothChooserDialog.java +++ b/components/permissions/android/java/src/org/chromium/components/permissions/BluetoothChooserDialog.java
@@ -541,7 +541,7 @@ void onDialogFinished( long nativeBluetoothChooserAndroid, @JniType("content::BluetoothChooserEvent") int eventType, - String deviceId); + @JniType("std::string") String deviceId); void restartSearch(long nativeBluetoothChooserAndroid);
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionUtil.java b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionUtil.java index 6e66b2c..172106b4 100644 --- a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionUtil.java +++ b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionUtil.java
@@ -10,6 +10,7 @@ import android.os.Build; import org.jni_zero.CalledByNative; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.base.metrics.RecordHistogram; @@ -111,6 +112,7 @@ * returned for different content setting types are disjunct. */ @CalledByNative + @JniType("std::vector<std::string>") public static String[] getRequiredAndroidPermissionsForContentSetting(int contentSettingType) { switch (contentSettingType) { case ContentSettingsType.GEOLOCATION, ContentSettingsType.GEOLOCATION_WITH_OPTIONS: @@ -162,6 +164,7 @@ * returned for different content setting types are disjunct. */ @CalledByNative + @JniType("std::vector<std::string>") public static String[] getOptionalAndroidPermissionsForContentSetting(int contentSettingType) { switch (contentSettingType) { case ContentSettingsType.GEOLOCATION, ContentSettingsType.GEOLOCATION_WITH_OPTIONS: @@ -192,7 +195,7 @@ @CalledByNative public static boolean canRequestSystemPermission( - int contentSettingType, WindowAndroid windowAndroid) { + int contentSettingType, @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) { String[] permissions = getRequiredAndroidPermissionsForContentSetting(contentSettingType); for (String permission : permissions) { if (!windowAndroid.canRequestPermission(permission)) { @@ -203,13 +206,15 @@ } @CalledByNative - public static boolean needsLocationPermissionForBluetooth(WindowAndroid windowAndroid) { + public static boolean needsLocationPermissionForBluetooth( + @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) { return Build.VERSION.SDK_INT < Build.VERSION_CODES.S && !windowAndroid.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION); } @CalledByNative - public static boolean needsNearbyDevicesPermissionForBluetooth(WindowAndroid windowAndroid) { + public static boolean needsNearbyDevicesPermissionForBluetooth( + @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && (!windowAndroid.hasPermission(Manifest.permission.BLUETOOTH_SCAN) || !windowAndroid.hasPermission(Manifest.permission.BLUETOOTH_CONNECT)); @@ -225,7 +230,8 @@ } @CalledByNative - public static boolean canRequestSystemPermissionsForBluetooth(WindowAndroid windowAndroid) { + public static boolean canRequestSystemPermissionsForBluetooth( + @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { return windowAndroid.canRequestPermission(Manifest.permission.BLUETOOTH_SCAN) && windowAndroid.canRequestPermission(Manifest.permission.BLUETOOTH_CONNECT); @@ -236,7 +242,8 @@ @CalledByNative public static void requestSystemPermissionsForBluetooth( - WindowAndroid windowAndroid, PermissionCallback callback) { + @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid, + PermissionCallback callback) { String[] requiredPermissions; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { requiredPermissions = @@ -254,7 +261,8 @@ } @CalledByNative - public static void requestLocationServices(WindowAndroid windowAndroid) { + public static void requestLocationServices( + @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) { assumeNonNull(windowAndroid.getActivity().get()) .startActivity(LocationUtils.getInstance().getSystemLocationSettingsIntent()); } @@ -278,8 +286,8 @@ */ @CalledByNative public static void handlePermissionPromptAllow( - WindowAndroid window, - WebContents webContents, + @JniType("ui::WindowAndroid*") WindowAndroid window, + @JniType("content::WebContents*") WebContents webContents, @ContentSettingsType.EnumType int contentSettingsType) { requestAndResolveNotificationsPermissionRequest( window, @@ -369,11 +377,13 @@ @NativeMethods public interface Natives { void resolveNotificationsPermissionRequest( - WebContents webContents, @ContentSetting int contentSetting); + @JniType("content::WebContents*") WebContents webContents, + @ContentSetting int contentSetting); - void dismissNotificationsPermissionRequest(WebContents webContents); + void dismissNotificationsPermissionRequest( + @JniType("content::WebContents*") WebContents webContents); - void notifyQuietIconDismissed(WebContents webContents); + void notifyQuietIconDismissed(@JniType("content::WebContents*") WebContents webContents); } /**
diff --git a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc index 3634a072..f2a173cb 100644 --- a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc +++ b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc
@@ -45,12 +45,10 @@ EitherGroupID JavaSyncOrLocalGroupIdToEitherGroupId( JNIEnv* env, - const JavaRef<jstring>& j_sync_group_id, + const std::string& sync_group_id, const JavaRef<jobject>& j_local_group_id) { if (j_local_group_id.is_null()) { - std::string sync_group_id_str = - ConvertJavaStringToUTF8(env, j_sync_group_id); - return base::Uuid::ParseLowercase(sync_group_id_str); + return base::Uuid::ParseLowercase(sync_group_id); } else { LocalTabGroupID local_group_id = TabGroupSyncConversionsBridge::FromJavaTabGroupId(env,
diff --git a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h index 51a284e..37573e5 100644 --- a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h +++ b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h
@@ -34,7 +34,7 @@ // Converts a Java sync or local group ID to EitherGroupID. EitherGroupID JavaSyncOrLocalGroupIdToEitherGroupId( JNIEnv* env, - const JavaRef<jstring>& j_sync_group_id, + const std::string& sync_group_id, const JavaRef<jobject>& j_local_group_id); } // namespace tab_groups
diff --git a/components/security_interstitials/content/insecure_form_navigation_throttle.cc b/components/security_interstitials/content/insecure_form_navigation_throttle.cc index 632204a7..e74d435 100644 --- a/components/security_interstitials/content/insecure_form_navigation_throttle.cc +++ b/components/security_interstitials/content/insecure_form_navigation_throttle.cc
@@ -20,6 +20,7 @@ #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" +#include "third_party/blink/public/common/features.h" #include "url/origin.h" #include "url/url_constants.h" @@ -89,21 +90,31 @@ return content::NavigationThrottle::PROCEED; } - // If the form is in a prerendered page, cancel it. Even though the form - // submission wouldn't include user data (a prerender cannot provide any - // input), the prerendered form submission could still leak data over the - // network (e.g. the path). - // There's an exception to this: Reloading a GET form will proceed since a - // prerender shouldn't check the InsecureFormTabStorage, which is a per-tab - // object. This is done in the check above. - if (handle->IsInPrerenderedMainFrame()) { - return content::NavigationThrottle::CANCEL; + if (!base::FeatureList::IsEnabled( + blink::features::kPrerenderActivationByFormSubmission)) { + // If the form is in a prerendered page, cancel it. Even though the form + // submission wouldn't include user data (a prerender cannot provide any + // input), the prerendered form submission could still leak data over the + // network (e.g. the path). + // There's an exception to this: Reloading a GET form will proceed since a + // prerender shouldn't check the InsecureFormTabStorage, which is a per-tab + // object. This is done in the check above. + if (handle->IsInPrerenderedMainFrame()) { + return content::NavigationThrottle::CANCEL; + } } - // If user has just chosen to proceed on an interstitial, we don't show - // another one. - if (tab_storage && tab_storage->IsProceeding()) { - return content::NavigationThrottle::PROCEED; + // `InsecureFormTabStorage` is stored on WebContents, and the state is + // configured when non-prerendering, so the prerendering shouldn't refer to + // the state to determine the outcome of the `InsecureFormNavigationThrottle`. + if (!base::FeatureList::IsEnabled( + blink::features::kPrerenderActivationByFormSubmission) || + !handle->IsInPrerenderedMainFrame()) { + // If user has just chosen to proceed on an interstitial, we don't show + // another one. + if (tab_storage && tab_storage->IsProceeding()) { + return content::NavigationThrottle::PROCEED; + } } // Do not set special error page HTML for insecure forms in subframes; those @@ -139,6 +150,14 @@ return content::NavigationThrottle::PROCEED; } + if (base::FeatureList::IsEnabled( + blink::features::kPrerenderActivationByFormSubmission) && + handle->IsInPrerenderedMainFrame()) { + // Cancel prerendering to avoid logging any metrics or showing an + // interstitial for an invisible page. + return content::NavigationThrottle::CANCEL; + } + std::unique_ptr<InsecureFormBlockingPage> blocking_page = blocking_page_factory_->CreateInsecureFormBlockingPage(contents, handle->GetURL());
diff --git a/components/sync/android/java/src/org/chromium/components/sync/SyncServiceImpl.java b/components/sync/android/java/src/org/chromium/components/sync/SyncServiceImpl.java index 19fd49a..b9e2c780 100644 --- a/components/sync/android/java/src/org/chromium/components/sync/SyncServiceImpl.java +++ b/components/sync/android/java/src/org/chromium/components/sync/SyncServiceImpl.java
@@ -461,14 +461,14 @@ @CalledByNative private static void onGetTypesWithUnsyncedDataResult( - Callback<Set<Integer>> callback, int[] types) { + Callback<Set<Integer>> callback, @JniType("std::vector<int32_t>") int[] types) { callback.onResult(dataTypeArrayToSet(types)); } @CalledByNative private static void onGetLocalDataDescriptionsResult( Callback<HashMap<Integer, LocalDataDescription>> callback, - @JniType("std::vector<int>") int[] dataTypes, + @JniType("std::vector<int32_t>") int[] dataTypes, @JniType("std::vector<syncer::LocalDataDescription>") LocalDataDescription[] localDataDescriptions) { HashMap<Integer, LocalDataDescription> localDataDescription = @@ -481,7 +481,8 @@ /** Invokes the onResult method of the callback from native code. */ @CalledByNative - private static void onGetAllNodesResult(Callback<JSONArray> callback, String serializedNodes) { + private static void onGetAllNodesResult( + Callback<JSONArray> callback, @JniType("std::string") String serializedNodes) { try { callback.onResult(new JSONArray(serializedNodes)); } catch (JSONException e) { @@ -548,8 +549,10 @@ void setInitialSyncFeatureSetupComplete( long nativeSyncServiceAndroidBridge, int syncFirstSetupCompleteSource); + @JniType("std::vector<int32_t>") int[] getActiveDataTypes(long nativeSyncServiceAndroidBridge); + @JniType("std::vector<int32_t>") int[] getSelectedTypes(long nativeSyncServiceAndroidBridge); void getTypesWithUnsyncedData( @@ -557,10 +560,11 @@ void getLocalDataDescriptions( long nativeSyncServiceAndroidBridge, - int[] types, + @JniType("std::vector<int32_t>") int[] types, Callback<HashMap<Integer, LocalDataDescription>> callback); - void triggerLocalDataMigration(long nativeSyncServiceAndroidBridge, int[] types); + void triggerLocalDataMigration( + long nativeSyncServiceAndroidBridge, @JniType("std::vector<int32_t>") int[] types); boolean isTypeManagedByPolicy(long nativeSyncServiceAndroidBridge, int type); @@ -569,7 +573,7 @@ void setSelectedTypes( long nativeSyncServiceAndroidBridge, boolean syncEverything, - int[] userSelectableTypeArray); + @JniType("std::vector<int32_t>") int[] userSelectableTypeArray); void setSelectedType( long nativeSyncServiceAndroidBridge, @@ -596,9 +600,11 @@ int getUserActionableError(long nativeSyncServiceAndroidBridge); - void setEncryptionPassphrase(long nativeSyncServiceAndroidBridge, String passphrase); + void setEncryptionPassphrase( + long nativeSyncServiceAndroidBridge, @JniType("std::string") String passphrase); - boolean setDecryptionPassphrase(long nativeSyncServiceAndroidBridge, String passphrase); + boolean setDecryptionPassphrase( + long nativeSyncServiceAndroidBridge, @JniType("std::string") String passphrase); long getExplicitPassphraseTime(long nativeSyncServiceAndroidBridge); @@ -625,6 +631,7 @@ long getLastSyncedTimeForDebugging(long nativeSyncServiceAndroidBridge); void keepAccountSettingsPrefsOnlyForUsers( - long nativeSyncServiceAndroidBridge, String[] gaiaIds); + long nativeSyncServiceAndroidBridge, + @JniType("std::vector<std::string>") String[] gaiaIds); } }
diff --git a/components/sync/android/sync_service_android_bridge.cc b/components/sync/android/sync_service_android_bridge.cc index e02f7a3..787f37c 100644 --- a/components/sync/android/sync_service_android_bridge.cc +++ b/components/sync/android/sync_service_android_bridge.cc
@@ -30,6 +30,7 @@ #include "components/sync/service/sync_user_settings.h" #include "google_apis/gaia/gaia_id.h" #include "google_apis/gaia/google_service_auth_error.h" +#include "third_party/jni_zero/default_conversions.h" // Must come after all headers that specialize FromJniType() / ToJniType(). #include "components/sync/android/jni_headers/SyncServiceImpl_jni.h" @@ -52,32 +53,30 @@ return static_cast<DataType>(type); } -ScopedJavaLocalRef<jintArray> DataTypeSetToJavaIntArray(JNIEnv* env, - DataTypeSet types) { - std::vector<int> type_vector(types.begin(), types.end()); - return base::android::ToJavaIntArray(env, type_vector); +std::vector<int32_t> DataTypeSetToVector(DataTypeSet types) { + std::vector<int32_t> type_vector; + for (DataType type : types) { + type_vector.push_back(static_cast<int32_t>(type)); + } + return type_vector; } -DataTypeSet JavaIntArrayToDataTypeSet(JNIEnv* env, - const JavaRef<jintArray>& types) { - std::vector<int> types_vector; - base::android::JavaIntArrayToIntVector(env, types, &types_vector); +DataTypeSet VectorToDataTypeSet(const std::vector<int32_t>& types_vector) { DataTypeSet data_type_set; - for (int type : types_vector) { + for (int32_t type : types_vector) { data_type_set.Put(IntToDataTypeChecked(type)); } return data_type_set; } -ScopedJavaLocalRef<jintArray> UserSelectableTypeSetToJavaIntArray( - JNIEnv* env, +std::vector<int32_t> UserSelectableTypeSetToVector( UserSelectableTypeSet types) { - std::vector<int> type_vector; + std::vector<int32_t> type_vector; type_vector.reserve(types.size()); for (UserSelectableType type : types) { - type_vector.push_back(static_cast<int>(type)); + type_vector.push_back(static_cast<int32_t>(type)); } - return base::android::ToJavaIntArray(env, type_vector); + return type_vector; } // Native callback for the JNI GetTypesWithUnsyncedData method. When @@ -92,19 +91,19 @@ types.Put(type); } Java_SyncServiceImpl_onGetTypesWithUnsyncedDataResult( - env, callback, DataTypeSetToJavaIntArray(env, types)); + env, callback, DataTypeSetToVector(types)); } void NativeGetLocalDataDescriptionsCallback( JNIEnv* env, const base::android::ScopedJavaGlobalRef<jobject>& callback, std::map<DataType, LocalDataDescription> localDataDescription) { - std::vector<int> data_types; + std::vector<int32_t> data_types; data_types.reserve(localDataDescription.size()); std::vector<LocalDataDescription> local_data_descriptions; local_data_descriptions.reserve(localDataDescription.size()); for (const auto& [data_type, description] : localDataDescription) { - data_types.push_back(data_type); + data_types.push_back(static_cast<int32_t>(data_type)); local_data_descriptions.push_back(description); } @@ -125,8 +124,7 @@ json_string = std::string(); } - Java_SyncServiceImpl_onGetAllNodesResult( - env, callback, ConvertUTF8ToJavaString(env, json_string)); + Java_SyncServiceImpl_onGetAllNodesResult(env, callback, json_string); } UserSelectableType IntToUserSelectableTypeChecked(int type) { @@ -180,32 +178,30 @@ } void SyncServiceAndroidBridge::AcknowledgeBookmarksLimitExceededError( - JNIEnv* env, int32_t source) { native_sync_service_->AcknowledgeBookmarksLimitExceededError( static_cast<SyncService::BookmarksLimitExceededHelpClickedSource>( source)); } -bool SyncServiceAndroidBridge::IsSyncFeatureEnabled(JNIEnv* env) { +bool SyncServiceAndroidBridge::IsSyncFeatureEnabled() { return native_sync_service_->IsSyncFeatureEnabled(); } -bool SyncServiceAndroidBridge::IsSyncFeatureActive(JNIEnv* env) { +bool SyncServiceAndroidBridge::IsSyncFeatureActive() { return native_sync_service_->IsSyncFeatureActive(); } -bool SyncServiceAndroidBridge::IsSyncDisabledByEnterprisePolicy(JNIEnv* env) { +bool SyncServiceAndroidBridge::IsSyncDisabledByEnterprisePolicy() { return native_sync_service_->HasDisableReason( SyncService::DISABLE_REASON_ENTERPRISE_POLICY); } -bool SyncServiceAndroidBridge::IsEngineInitialized(JNIEnv* env) { +bool SyncServiceAndroidBridge::IsEngineInitialized() { return native_sync_service_->IsEngineInitialized(); } -void SyncServiceAndroidBridge::SetSetupInProgress(JNIEnv* env, - bool in_progress) { +void SyncServiceAndroidBridge::SetSetupInProgress(bool in_progress) { if (!in_progress) { sync_blocker_.reset(); return; @@ -216,30 +212,26 @@ } } -bool SyncServiceAndroidBridge::IsInitialSyncFeatureSetupComplete(JNIEnv* env) { +bool SyncServiceAndroidBridge::IsInitialSyncFeatureSetupComplete() { return native_sync_service_->GetUserSettings() ->IsInitialSyncFeatureSetupComplete(); } void SyncServiceAndroidBridge::SetInitialSyncFeatureSetupComplete( - JNIEnv* env, int32_t source) { native_sync_service_->GetUserSettings()->SetInitialSyncFeatureSetupComplete( static_cast<SyncFirstSetupCompleteSource>(source)); } -ScopedJavaLocalRef<jintArray> SyncServiceAndroidBridge::GetActiveDataTypes( - JNIEnv* env) { - return DataTypeSetToJavaIntArray(env, - native_sync_service_->GetActiveDataTypes()); +std::vector<int32_t> SyncServiceAndroidBridge::GetActiveDataTypes() { + return DataTypeSetToVector(native_sync_service_->GetActiveDataTypes()); } -ScopedJavaLocalRef<jintArray> SyncServiceAndroidBridge::GetSelectedTypes( - JNIEnv* env) { +std::vector<int32_t> SyncServiceAndroidBridge::GetSelectedTypes() { UserSelectableTypeSet user_selectable_types; user_selectable_types = native_sync_service_->GetUserSettings()->GetSelectedTypes(); - return UserSelectableTypeSetToJavaIntArray(env, user_selectable_types); + return UserSelectableTypeSetToVector(user_selectable_types); } void SyncServiceAndroidBridge::GetTypesWithUnsyncedData( @@ -255,40 +247,35 @@ void SyncServiceAndroidBridge::GetLocalDataDescriptions( JNIEnv* env, - const base::android::JavaRef<jintArray>& types, + const std::vector<int32_t>& types, const base::android::JavaRef<jobject>& callback) { base::android::ScopedJavaGlobalRef<jobject> java_callback; java_callback.Reset(env, callback); native_sync_service_->GetLocalDataDescriptions( - JavaIntArrayToDataTypeSet(env, types), + VectorToDataTypeSet(types), base::BindOnce(&NativeGetLocalDataDescriptionsCallback, env, java_callback)); } void SyncServiceAndroidBridge::TriggerLocalDataMigration( - JNIEnv* env, - const JavaRef<jintArray>& types) { - native_sync_service_->TriggerLocalDataMigration( - JavaIntArrayToDataTypeSet(env, types)); + const std::vector<int32_t>& types) { + native_sync_service_->TriggerLocalDataMigration(VectorToDataTypeSet(types)); } -bool SyncServiceAndroidBridge::IsTypeManagedByPolicy(JNIEnv* env, - int32_t type) { +bool SyncServiceAndroidBridge::IsTypeManagedByPolicy(int32_t type) { return native_sync_service_->GetUserSettings()->IsTypeManagedByPolicy( IntToUserSelectableTypeChecked(type)); } -bool SyncServiceAndroidBridge::IsTypeManagedByCustodian(JNIEnv* env, - int32_t type) { +bool SyncServiceAndroidBridge::IsTypeManagedByCustodian(int32_t type) { return native_sync_service_->GetUserSettings()->IsTypeManagedByCustodian( IntToUserSelectableTypeChecked(type)); } void SyncServiceAndroidBridge::SetSelectedTypes( - JNIEnv* env, bool sync_everything, - const JavaRef<jintArray>& user_selectable_type_array) { + const std::vector<int32_t>& user_selectable_types_vector) { if (native_sync_service_->GetAccountInfo().account_id.empty()) { // This function shouldn't be called while signed out, but evidence suggests // it sometimes does get called. @@ -299,12 +286,8 @@ return; } - std::vector<int> types_vector; - base::android::JavaIntArrayToIntVector(env, user_selectable_type_array, - &types_vector); - UserSelectableTypeSet user_selectable_types; - for (int type : types_vector) { + for (int32_t type : user_selectable_types_vector) { user_selectable_types.Put(IntToUserSelectableTypeChecked(type)); } @@ -312,9 +295,7 @@ sync_everything, user_selectable_types); } -void SyncServiceAndroidBridge::SetSelectedType(JNIEnv* env, - int32_t type, - bool is_type_on) { +void SyncServiceAndroidBridge::SetSelectedType(int32_t type, bool is_type_on) { if (native_sync_service_->GetAccountInfo().account_id.empty()) { // This function shouldn't be called while signed out, but evidence suggests // it sometimes does get called. @@ -329,41 +310,39 @@ IntToUserSelectableTypeChecked(type), is_type_on); } -bool SyncServiceAndroidBridge::IsCustomPassphraseAllowed(JNIEnv* env) { +bool SyncServiceAndroidBridge::IsCustomPassphraseAllowed() { return native_sync_service_->GetUserSettings()->IsCustomPassphraseAllowed(); } -bool SyncServiceAndroidBridge::IsEncryptEverythingEnabled(JNIEnv* env) { +bool SyncServiceAndroidBridge::IsEncryptEverythingEnabled() { return native_sync_service_->GetUserSettings()->IsEncryptEverythingEnabled(); } -bool SyncServiceAndroidBridge::IsPassphraseRequiredForPreferredDataTypes( - JNIEnv* env) { +bool SyncServiceAndroidBridge::IsPassphraseRequiredForPreferredDataTypes() { return native_sync_service_->GetUserSettings() ->IsPassphraseRequiredForPreferredDataTypes(); } -bool SyncServiceAndroidBridge::IsTrustedVaultKeyRequired(JNIEnv* env) { +bool SyncServiceAndroidBridge::IsTrustedVaultKeyRequired() { return native_sync_service_->GetUserSettings()->IsTrustedVaultKeyRequired(); } -bool SyncServiceAndroidBridge::IsTrustedVaultKeyRequiredForPreferredDataTypes( - JNIEnv* env) { +bool SyncServiceAndroidBridge:: + IsTrustedVaultKeyRequiredForPreferredDataTypes() { return native_sync_service_->GetUserSettings() ->IsTrustedVaultKeyRequiredForPreferredDataTypes(); } -bool SyncServiceAndroidBridge::IsTrustedVaultRecoverabilityDegraded( - JNIEnv* env) { +bool SyncServiceAndroidBridge::IsTrustedVaultRecoverabilityDegraded() { return native_sync_service_->GetUserSettings() ->IsTrustedVaultRecoverabilityDegraded(); } -bool SyncServiceAndroidBridge::IsUsingExplicitPassphrase(JNIEnv* env) { +bool SyncServiceAndroidBridge::IsUsingExplicitPassphrase() { return native_sync_service_->GetUserSettings()->IsUsingExplicitPassphrase(); } -int32_t SyncServiceAndroidBridge::GetPassphraseType(JNIEnv* env) { +int32_t SyncServiceAndroidBridge::GetPassphraseType() { // TODO(crbug.com/40923935): Mapping nullopt -> kImplicitPassphrase preserves // the historic behavior, but ideally we should propagate the nullopt state to // Java. @@ -372,29 +351,26 @@ PassphraseType::kImplicitPassphrase)); } -int32_t SyncServiceAndroidBridge::GetTransportState(JNIEnv* env) { +int32_t SyncServiceAndroidBridge::GetTransportState() { return static_cast<int32_t>(native_sync_service_->GetTransportState()); } -int32_t SyncServiceAndroidBridge::GetUserActionableError(JNIEnv* env) { +int32_t SyncServiceAndroidBridge::GetUserActionableError() { return static_cast<int32_t>(native_sync_service_->GetUserActionableError()); } void SyncServiceAndroidBridge::SetEncryptionPassphrase( - JNIEnv* env, - const JavaRef<jstring>& passphrase) { - native_sync_service_->GetUserSettings()->SetEncryptionPassphrase( - ConvertJavaStringToUTF8(env, passphrase)); + const std::string& passphrase) { + native_sync_service_->GetUserSettings()->SetEncryptionPassphrase(passphrase); } bool SyncServiceAndroidBridge::SetDecryptionPassphrase( - JNIEnv* env, - const JavaRef<jstring>& passphrase) { + const std::string& passphrase) { return native_sync_service_->GetUserSettings()->SetDecryptionPassphrase( - ConvertJavaStringToUTF8(env, passphrase)); + passphrase); } -int64_t SyncServiceAndroidBridge::GetExplicitPassphraseTime(JNIEnv* env) { +int64_t SyncServiceAndroidBridge::GetExplicitPassphraseTime() { return native_sync_service_->GetUserSettings() ->GetExplicitPassphraseTime() .InMillisecondsSinceUnixEpoch(); @@ -408,7 +384,7 @@ base::BindOnce(&NativeGetAllNodesCallback, env, java_callback)); } -GoogleServiceAuthError SyncServiceAndroidBridge::GetAuthError(JNIEnv* env) { +GoogleServiceAuthError SyncServiceAndroidBridge::GetAuthError() { return native_sync_service_->GetAuthError(); } @@ -420,37 +396,37 @@ : ConvertToJavaCoreAccountInfo(env, account_info); } -bool SyncServiceAndroidBridge::HasSyncConsent(JNIEnv* env) { +bool SyncServiceAndroidBridge::HasSyncConsent() { return native_sync_service_->HasSyncConsent(); } -bool SyncServiceAndroidBridge::IsPassphrasePromptMutedForCurrentProductVersion( - JNIEnv* env) { +bool SyncServiceAndroidBridge:: + IsPassphrasePromptMutedForCurrentProductVersion() { return native_sync_service_->GetUserSettings() ->IsPassphrasePromptMutedForCurrentProductVersion(); } void SyncServiceAndroidBridge:: - MarkPassphrasePromptMutedForCurrentProductVersion(JNIEnv* env) { + MarkPassphrasePromptMutedForCurrentProductVersion() { native_sync_service_->GetUserSettings() ->MarkPassphrasePromptMutedForCurrentProductVersion(); } -bool SyncServiceAndroidBridge::HasKeepEverythingSynced(JNIEnv* env) { +bool SyncServiceAndroidBridge::HasKeepEverythingSynced() { return native_sync_service_->GetUserSettings()->IsSyncEverythingEnabled(); } -bool SyncServiceAndroidBridge::ShouldOfferTrustedVaultOptIn(JNIEnv* env) { +bool SyncServiceAndroidBridge::ShouldOfferTrustedVaultOptIn() { return syncer::ShouldOfferTrustedVaultOptIn(native_sync_service_); } -void SyncServiceAndroidBridge::TriggerRefresh(JNIEnv* env) { +void SyncServiceAndroidBridge::TriggerRefresh() { native_sync_service_->TriggerRefresh( SyncService::TriggerRefreshSource::kAndroidSyncServiceBridge, DataTypeSet::All()); } -int64_t SyncServiceAndroidBridge::GetLastSyncedTimeForDebugging(JNIEnv* env) { +int64_t SyncServiceAndroidBridge::GetLastSyncedTimeForDebugging() { base::Time last_sync_time = native_sync_service_->GetLastSyncedTimeForDebugging(); return static_cast<int64_t>( @@ -458,10 +434,7 @@ } void SyncServiceAndroidBridge::KeepAccountSettingsPrefsOnlyForUsers( - JNIEnv* env, - const base::android::JavaRef<jobjectArray>& gaia_ids_array) { - std::vector<std::string> gaia_id_strings; - AppendJavaStringArrayToStringVector(env, gaia_ids_array, &gaia_id_strings); + const std::vector<std::string>& gaia_id_strings) { std::vector<GaiaId> gaia_ids; for (const std::string& gaia_id_string : gaia_id_strings) { gaia_ids.emplace_back(gaia_id_string);
diff --git a/components/sync/android/sync_service_android_bridge.h b/components/sync/android/sync_service_android_bridge.h index 373e98d..a194124 100644 --- a/components/sync/android/sync_service_android_bridge.h +++ b/components/sync/android/sync_service_android_bridge.h
@@ -7,6 +7,8 @@ #include <cstdint> #include <memory> +#include <string> +#include <vector> #include "base/android/scoped_java_ref.h" #include "base/memory/raw_ptr.h" @@ -45,67 +47,59 @@ // Please keep all methods below in the same order as the @NativeMethods in // SyncServiceImpl.java. - void AcknowledgeBookmarksLimitExceededError(JNIEnv* env, int32_t source); - bool IsSyncFeatureEnabled(JNIEnv* env); - bool IsSyncFeatureActive(JNIEnv* env); - bool IsSyncDisabledByEnterprisePolicy(JNIEnv* env); - bool IsEngineInitialized(JNIEnv* env); - void SetSetupInProgress(JNIEnv* env, bool in_progress); - bool IsInitialSyncFeatureSetupComplete(JNIEnv* env); - void SetInitialSyncFeatureSetupComplete(JNIEnv* env, int32_t source); - base::android::ScopedJavaLocalRef<jintArray> GetActiveDataTypes(JNIEnv* env); - base::android::ScopedJavaLocalRef<jintArray> GetSelectedTypes(JNIEnv* env); + void AcknowledgeBookmarksLimitExceededError(int32_t source); + bool IsSyncFeatureEnabled(); + bool IsSyncFeatureActive(); + bool IsSyncDisabledByEnterprisePolicy(); + bool IsEngineInitialized(); + void SetSetupInProgress(bool in_progress); + bool IsInitialSyncFeatureSetupComplete(); + void SetInitialSyncFeatureSetupComplete(int32_t source); + std::vector<int32_t> GetActiveDataTypes(); + std::vector<int32_t> GetSelectedTypes(); void GetTypesWithUnsyncedData( JNIEnv* env, const base::android::JavaRef<jobject>& callback); void GetLocalDataDescriptions( JNIEnv* env, - const base::android::JavaRef<jintArray>& types, + const std::vector<int32_t>& types, const base::android::JavaRef<jobject>& callback); - void TriggerLocalDataMigration( - JNIEnv* env, - const base::android::JavaRef<jintArray>& types); - bool IsTypeManagedByPolicy(JNIEnv* env, int32_t type); - bool IsTypeManagedByCustodian(JNIEnv* env, int32_t type); + void TriggerLocalDataMigration(const std::vector<int32_t>& types); + bool IsTypeManagedByPolicy(int32_t type); + bool IsTypeManagedByCustodian(int32_t type); void SetSelectedTypes( - JNIEnv* env, bool sync_everything, - const base::android::JavaRef<jintArray>& user_selectable_type_selection); - void SetSelectedType(JNIEnv* env, int32_t type, bool is_type_on); - bool IsCustomPassphraseAllowed(JNIEnv* env); - bool IsEncryptEverythingEnabled(JNIEnv* env); - bool IsPassphraseRequiredForPreferredDataTypes(JNIEnv* env); - bool IsTrustedVaultKeyRequired(JNIEnv* env); - bool IsTrustedVaultKeyRequiredForPreferredDataTypes(JNIEnv* env); - bool IsTrustedVaultRecoverabilityDegraded(JNIEnv* env); - bool IsUsingExplicitPassphrase(JNIEnv* env); - int32_t GetPassphraseType(JNIEnv* env); - int32_t GetTransportState(JNIEnv* env); - int32_t GetUserActionableError(JNIEnv* env); - void SetEncryptionPassphrase( - JNIEnv* env, - const base::android::JavaRef<jstring>& passphrase); - bool SetDecryptionPassphrase( - JNIEnv* env, - const base::android::JavaRef<jstring>& passphrase); + const std::vector<int32_t>& user_selectable_types_vector); + void SetSelectedType(int32_t type, bool is_type_on); + bool IsCustomPassphraseAllowed(); + bool IsEncryptEverythingEnabled(); + bool IsPassphraseRequiredForPreferredDataTypes(); + bool IsTrustedVaultKeyRequired(); + bool IsTrustedVaultKeyRequiredForPreferredDataTypes(); + bool IsTrustedVaultRecoverabilityDegraded(); + bool IsUsingExplicitPassphrase(); + int32_t GetPassphraseType(); + int32_t GetTransportState(); + int32_t GetUserActionableError(); + void SetEncryptionPassphrase(const std::string& passphrase); + bool SetDecryptionPassphrase(const std::string& passphrase); // Returns 0 if there's no passphrase time. - int64_t GetExplicitPassphraseTime(JNIEnv* env); + int64_t GetExplicitPassphraseTime(); void GetAllNodes(JNIEnv* env, const base::android::JavaRef<jobject>& callback); - GoogleServiceAuthError GetAuthError(JNIEnv* env); + GoogleServiceAuthError GetAuthError(); base::android::ScopedJavaLocalRef<jobject> GetAccountInfo(JNIEnv* env); - bool HasSyncConsent(JNIEnv* env); - bool IsPassphrasePromptMutedForCurrentProductVersion(JNIEnv* env); - void MarkPassphrasePromptMutedForCurrentProductVersion(JNIEnv* env); - bool HasKeepEverythingSynced(JNIEnv* env); - bool ShouldOfferTrustedVaultOptIn(JNIEnv* env); - void TriggerRefresh(JNIEnv* env); + bool HasSyncConsent(); + bool IsPassphrasePromptMutedForCurrentProductVersion(); + void MarkPassphrasePromptMutedForCurrentProductVersion(); + bool HasKeepEverythingSynced(); + bool ShouldOfferTrustedVaultOptIn(); + void TriggerRefresh(); // Returns a timestamp for when a sync was last executed. The return value is // the internal value of base::Time. - int64_t GetLastSyncedTimeForDebugging(JNIEnv* env); + int64_t GetLastSyncedTimeForDebugging(); void KeepAccountSettingsPrefsOnlyForUsers( - JNIEnv* env, - const base::android::JavaRef<jobjectArray>& gaia_ids); + const std::vector<std::string>& gaia_id_strings); private: // A reference to the sync service for this profile.
diff --git a/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java b/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java index 68a1c1b..db1a052 100644 --- a/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java +++ b/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java
@@ -9,6 +9,7 @@ import androidx.annotation.VisibleForTesting; import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.build.annotations.NullMarked; @@ -240,24 +241,38 @@ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) @NativeMethods public interface Natives { - GURL fixupUrl(String url); + @JniType("GURL") + GURL fixupUrl(@JniType("std::string") String url); - String formatUrlForDisplayOmitScheme(String url); + @JniType("std::u16string") + String formatUrlForDisplayOmitScheme(@JniType("std::string") String url); - String formatUrlForDisplayOmitHTTPScheme(String url); + @JniType("std::u16string") + String formatUrlForDisplayOmitHTTPScheme(@JniType("std::string") String url); - String formatUrlForDisplayOmitSchemeOmitTrivialSubdomains(String url); + @JniType("std::u16string") + String formatUrlForDisplayOmitSchemeOmitTrivialSubdomains( + @JniType("std::string") String url); - String formatUrlForDisplayOmitSchemePathAndTrivialSubdomains(GURL url); + @JniType("std::u16string") + String formatUrlForDisplayOmitSchemePathAndTrivialSubdomains(@JniType("GURL") GURL url); - String formatUrlForDisplayOmitUsernamePassword(String url); + @JniType("std::u16string") + String formatUrlForDisplayOmitUsernamePassword(@JniType("std::string") String url); - String formatUrlForCopy(String url); + @JniType("std::u16string") + String formatUrlForCopy(@JniType("std::string") String url); - String formatUrlForSecurityDisplay(GURL url, @SchemeDisplay int schemeDisplay); + @JniType("std::u16string") + String formatUrlForSecurityDisplay( + @JniType("GURL") GURL url, @SchemeDisplay int schemeDisplay); - String formatOriginForSecurityDisplay(Origin origin, @SchemeDisplay int schemeDisplay); + @JniType("std::u16string") + String formatOriginForSecurityDisplay( + @JniType("url::Origin") Origin origin, @SchemeDisplay int schemeDisplay); - String formatStringUrlForSecurityDisplay(@Nullable String url, @SchemeDisplay int schemeDisplay); + @JniType("std::u16string") + String formatStringUrlForSecurityDisplay( + @JniType("std::string") @Nullable String url, @SchemeDisplay int schemeDisplay); } }
diff --git a/components/url_formatter/url_formatter_android.cc b/components/url_formatter/url_formatter_android.cc index 50c8588..f454f3fa 100644 --- a/components/url_formatter/url_formatter_android.cc +++ b/components/url_formatter/url_formatter_android.cc
@@ -17,134 +17,83 @@ // Must come after all headers that specialize FromJniType() / ToJniType(). #include "components/url_formatter/android/jni_headers/UrlFormatter_jni.h" -using base::android::JavaRef; -using base::android::ScopedJavaLocalRef; - -namespace { - -static GURL JNI_UrlFormatter_ConvertJavaStringToGURL( - JNIEnv* env, - const base::android::JavaRef<jstring>& url) { - return url ? GURL(base::android::ConvertJavaStringToUTF8(env, url)) : GURL(); -} - -} // namespace - namespace url_formatter { namespace android { -static ScopedJavaLocalRef<jobject> JNI_UrlFormatter_FixupUrl( - JNIEnv* env, - const base::android::JavaRef<jstring>& url) { - DCHECK(url); - GURL fixed_url = - url_formatter::FixupURL(base::android::ConvertJavaStringToUTF8(env, url)); - - return url::GURLAndroid::FromNativeGURL(env, fixed_url); +static GURL JNI_UrlFormatter_FixupUrl(const std::string& url) { + return url_formatter::FixupURL(url); } -static ScopedJavaLocalRef<jstring> -JNI_UrlFormatter_FormatUrlForDisplayOmitScheme( - JNIEnv* env, - const base::android::JavaRef<jstring>& url) { - return base::android::ConvertUTF16ToJavaString( - env, url_formatter::FormatUrl( - JNI_UrlFormatter_ConvertJavaStringToGURL(env, url), - url_formatter::kFormatUrlOmitDefaults | - url_formatter::kFormatUrlOmitHTTPS, - base::UnescapeRule::SPACES, nullptr, nullptr, nullptr)); +static std::u16string JNI_UrlFormatter_FormatUrlForDisplayOmitScheme( + const std::string& url) { + return url_formatter::FormatUrl(GURL(url), + url_formatter::kFormatUrlOmitDefaults | + url_formatter::kFormatUrlOmitHTTPS, + base::UnescapeRule::SPACES, nullptr, nullptr, + nullptr); } -static ScopedJavaLocalRef<jstring> -JNI_UrlFormatter_FormatUrlForDisplayOmitHTTPScheme( - JNIEnv* env, - const base::android::JavaRef<jstring>& url) { - return base::android::ConvertUTF16ToJavaString( - env, url_formatter::FormatUrl( - JNI_UrlFormatter_ConvertJavaStringToGURL(env, url), - url_formatter::kFormatUrlOmitDefaults, - base::UnescapeRule::SPACES, nullptr, nullptr, nullptr)); +static std::u16string JNI_UrlFormatter_FormatUrlForDisplayOmitHTTPScheme( + const std::string& url) { + return url_formatter::FormatUrl( + GURL(url), url_formatter::kFormatUrlOmitDefaults, + base::UnescapeRule::SPACES, nullptr, nullptr, nullptr); } -static ScopedJavaLocalRef<jstring> -JNI_UrlFormatter_FormatUrlForDisplayOmitUsernamePassword( - JNIEnv* env, - const base::android::JavaRef<jstring>& url) { - return base::android::ConvertUTF16ToJavaString( - env, url_formatter::FormatUrl( - JNI_UrlFormatter_ConvertJavaStringToGURL(env, url), - url_formatter::kFormatUrlOmitUsernamePassword | - kFormatUrlOmitTrailingSlashOnBareHostname, - base::UnescapeRule::NONE, nullptr, nullptr, nullptr)); +static std::u16string JNI_UrlFormatter_FormatUrlForDisplayOmitUsernamePassword( + const std::string& url) { + return url_formatter::FormatUrl( + GURL(url), + url_formatter::kFormatUrlOmitUsernamePassword | + kFormatUrlOmitTrailingSlashOnBareHostname, + base::UnescapeRule::NONE, nullptr, nullptr, nullptr); } -static ScopedJavaLocalRef<jstring> JNI_UrlFormatter_FormatUrlForCopy( - JNIEnv* env, - const base::android::JavaRef<jstring>& url) { - return base::android::ConvertUTF16ToJavaString( - env, url_formatter::FormatUrl( - JNI_UrlFormatter_ConvertJavaStringToGURL(env, url), - url_formatter::kFormatUrlOmitNothing, base::UnescapeRule::NORMAL, - nullptr, nullptr, nullptr)); +static std::u16string JNI_UrlFormatter_FormatUrlForCopy( + const std::string& url) { + return url_formatter::FormatUrl( + GURL(url), url_formatter::kFormatUrlOmitNothing, + base::UnescapeRule::NORMAL, nullptr, nullptr, nullptr); } -static ScopedJavaLocalRef<jstring> -JNI_UrlFormatter_FormatStringUrlForSecurityDisplay( - JNIEnv* env, - const base::android::JavaRef<jstring>& url, +static std::u16string JNI_UrlFormatter_FormatStringUrlForSecurityDisplay( + const std::string& url, int32_t scheme_display) { - return base::android::ConvertUTF16ToJavaString( - env, url_formatter::FormatUrlForSecurityDisplay( - JNI_UrlFormatter_ConvertJavaStringToGURL(env, url), - static_cast<SchemeDisplay>(scheme_display))); + return url_formatter::FormatUrlForSecurityDisplay( + GURL(url), static_cast<SchemeDisplay>(scheme_display)); } -static ScopedJavaLocalRef<jstring> JNI_UrlFormatter_FormatUrlForSecurityDisplay( - JNIEnv* env, - const base::android::JavaRef<jobject>& j_gurl, +static std::u16string JNI_UrlFormatter_FormatUrlForSecurityDisplay( + const GURL& gurl, int32_t scheme_display) { - DCHECK(j_gurl); - GURL gurl = url::GURLAndroid::ToNativeGURL(env, j_gurl); - return base::android::ConvertUTF16ToJavaString( - env, url_formatter::FormatUrlForSecurityDisplay( - gurl, static_cast<SchemeDisplay>(scheme_display))); + return url_formatter::FormatUrlForSecurityDisplay( + gurl, static_cast<SchemeDisplay>(scheme_display)); } -static ScopedJavaLocalRef<jstring> -JNI_UrlFormatter_FormatOriginForSecurityDisplay( - JNIEnv* env, - const base::android::JavaRef<jobject>& j_origin, +static std::u16string JNI_UrlFormatter_FormatOriginForSecurityDisplay( + const url::Origin& origin, int32_t scheme_display) { - DCHECK(j_origin); - url::Origin origin = url::Origin::FromJavaObject(env, j_origin); - return base::android::ConvertUTF16ToJavaString( - env, url_formatter::FormatOriginForSecurityDisplay( - origin, static_cast<SchemeDisplay>(scheme_display))); + return url_formatter::FormatOriginForSecurityDisplay( + origin, static_cast<SchemeDisplay>(scheme_display)); } -static ScopedJavaLocalRef<jstring> +static std::u16string JNI_UrlFormatter_FormatUrlForDisplayOmitSchemeOmitTrivialSubdomains( - JNIEnv* env, - const base::android::JavaRef<jstring>& url) { - return base::android::ConvertUTF16ToJavaString( - env, url_formatter::FormatUrl( - JNI_UrlFormatter_ConvertJavaStringToGURL(env, url), - url_formatter::kFormatUrlOmitDefaults | - url_formatter::kFormatUrlOmitHTTPS | - url_formatter::kFormatUrlOmitTrivialSubdomains, - base::UnescapeRule::SPACES, nullptr, nullptr, nullptr)); + const std::string& url) { + return url_formatter::FormatUrl( + GURL(url), + url_formatter::kFormatUrlOmitDefaults | + url_formatter::kFormatUrlOmitHTTPS | + url_formatter::kFormatUrlOmitTrivialSubdomains, + base::UnescapeRule::SPACES, nullptr, nullptr, nullptr); } -static ScopedJavaLocalRef<jstring> +static std::u16string JNI_UrlFormatter_FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains( - JNIEnv* env, - const base::android::JavaRef<jobject>& j_gurl) { - DCHECK(j_gurl); - GURL gurl = url::GURLAndroid::ToNativeGURL(env, j_gurl); - return base::android::ConvertUTF16ToJavaString( - env, url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains( - gurl)); + const GURL& gurl) { + return url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains( + gurl); } } // namespace android
diff --git a/components/variations/pref_names.h b/components/variations/pref_names.h index f524a984..33411fd 100644 --- a/components/variations/pref_names.h +++ b/components/variations/pref_names.h
@@ -24,6 +24,10 @@ // studies. inline constexpr char kVariationsCountry[] = "variations_country"; +// The latest administrative area code received by the VariationsService for +// evaluating studies. +inline constexpr char kVariationsGeoLevel1[] = "variations_geo_level1"; + // The number of times that Chrome has crashed before successfully fetching a // new seed. Used to determine whether to fall back to a "safe" seed. inline constexpr char kVariationsCrashStreak[] = "variations_crash_streak";
diff --git a/components/variations/proto/stored_seed_info.proto b/components/variations/proto/stored_seed_info.proto index 472c9bcb..0c5b245 100644 --- a/components/variations/proto/stored_seed_info.proto +++ b/components/variations/proto/stored_seed_info.proto
@@ -10,7 +10,7 @@ // Wraps a stored VariationsSeed and other seed-related info. // -// Next tag: 9 +// Next tag: 10 message StoredSeedInfo { // Raw seed data. // As a memory optimization, the seed data may be cleared from memory after @@ -45,4 +45,9 @@ // Chrome version used for evaluating studies. The version stored at the same // time as `permanent_country_code`. optional string permanent_version = 8; + + // The administrative area code of the client fetched from the server. Mirrors + // the `session_country_code` field, but is not currently used for evaluating + // studies. + optional string session_geo_level1 = 9; }
diff --git a/components/variations/seed_reader_writer.cc b/components/variations/seed_reader_writer.cc index 56f9136..b188606 100644 --- a/components/variations/seed_reader_writer.cc +++ b/components/variations/seed_reader_writer.cc
@@ -267,6 +267,25 @@ channel == version_info::Channel::BETA; } +// Sets the Geo Level1 pref value if its pref name exists. +void SetGeoLevel1Pref(const SeedFieldsPrefs& prefs, + PrefService& local_state, + std::string_view value) { + if (prefs.session_geo_level1) { + local_state.SetString(prefs.session_geo_level1, value); + } +} + +// Gets the Geo Level1 pref value if its pref name exists. Returns an empty +// string otherwise. +std::string GetGeoLevel1Pref(const SeedFieldsPrefs& prefs, + PrefService& local_state) { + if (prefs.session_geo_level1) { + return local_state.GetString(prefs.session_geo_level1); + } + return ""; +} + } // namespace const SeedFieldsPrefs kRegularSeedFieldsPrefs = { @@ -276,6 +295,7 @@ .seed_date = prefs::kVariationsSeedDate, .client_fetch_time = prefs::kVariationsLastFetchTime, .session_country_code = prefs::kVariationsCountry, + .session_geo_level1 = prefs::kVariationsGeoLevel1, .permanent_country_code_version = prefs::kVariationsPermanentConsistencyCountry, }; @@ -287,6 +307,7 @@ .seed_date = prefs::kVariationsSafeSeedDate, .client_fetch_time = prefs::kVariationsSafeSeedFetchTime, .session_country_code = prefs::kVariationsSafeSeedSessionConsistencyCountry, + .session_geo_level1 = nullptr, .permanent_country_code_version = prefs::kVariationsSafeSeedPermanentConsistencyCountry, }; @@ -296,6 +317,7 @@ base::Time seed_date, base::Time client_fetch_time, std::string_view session_country_code, + std::string_view session_geo_level1, std::string_view permanent_country_code, std::string_view permanent_country_version) : signature(signature), @@ -303,6 +325,7 @@ seed_date(seed_date), client_fetch_time(client_fetch_time), session_country_code(session_country_code), + session_geo_level1(session_geo_level1), permanent_country_code(permanent_country_code), permanent_country_version(permanent_country_version) {} @@ -397,8 +420,12 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (ShouldUseSeedFile()) { stored_seed_info_.clear_session_country_code(); + stored_seed_info_.clear_session_geo_level1(); } local_state_->ClearPref(fields_prefs_->session_country_code); + if (fields_prefs_->session_geo_level1) { + local_state_->ClearPref(fields_prefs_->session_geo_level1); + } } SeedInfo SeedReaderWriter::GetSeedInfo() const { @@ -411,6 +438,7 @@ /*client_fetch_time=*/ ProtoTimeToTime(stored_seed_info_.client_fetch_time()), /*session_country_code=*/stored_seed_info_.session_country_code(), + /*session_geo_level1=*/stored_seed_info_.session_geo_level1(), /*permanent_country_code=*/stored_seed_info_.permanent_country_code(), /*permanent_country_version=*/stored_seed_info_.permanent_version()); } else { @@ -425,6 +453,8 @@ local_state_->GetTime(fields_prefs_->client_fetch_time), /*session_country_code=*/ local_state_->GetString(fields_prefs_->session_country_code), + /*session_geo_level1=*/ + GetGeoLevel1Pref(*fields_prefs_, *local_state_), /*permanent_country_code=*/permanent_country_version.country, /*permanent_country_version=*/permanent_country_version.version); } @@ -667,9 +697,11 @@ stored_seed_info_.set_seed_date(TimeToProtoTime(seed_info.seed_date)); stored_seed_info_.set_client_fetch_time( TimeToProtoTime(seed_info.client_fetch_time)); - // Only update the latest country code if it is not empty. + // Only update the latest country code and geo level if country code is not + // empty. if (!seed_info.session_country_code.empty()) { stored_seed_info_.set_session_country_code(seed_info.session_country_code); + stored_seed_info_.set_session_geo_level1(seed_info.session_geo_level1); } if (!seed_info.permanent_country_code.empty()) { stored_seed_info_.set_permanent_country_code( @@ -689,10 +721,13 @@ // being scheduled. seed_writer_->ScheduleWriteWithBackgroundDataSerializer(this); // We still need to update the session country code in local state as it is - // used by hash_realtime_utils::GetHashRealTimeSelectionConfiguringPrefs(). + // used by hash_realtime_utils::GetHashRealTimeSelectionConfiguringPrefs() and + // for some platform experience features. if (!seed_info.session_country_code.empty()) { local_state_->SetString(fields_prefs_->session_country_code, stored_seed_info_.session_country_code()); + SetGeoLevel1Pref(*fields_prefs_, *local_state_, + stored_seed_info_.session_geo_level1()); } return StoreSeedResult::kSuccess; } @@ -794,6 +829,7 @@ local_state_->GetTime(fields_prefs_->client_fetch_time), .session_country_code = local_state_->GetString(fields_prefs_->session_country_code), + .session_geo_level1 = GetGeoLevel1Pref(*fields_prefs_, *local_state_), .permanent_country_code = permanent_country_version.country, .permanent_country_version = permanent_country_version.version, }); @@ -887,6 +923,8 @@ if (!seed_info.session_country_code.empty()) { local_state_->SetString(fields_prefs_->session_country_code, seed_info.session_country_code); + SetGeoLevel1Pref(*fields_prefs_, *local_state_, + seed_info.session_geo_level1); } // Version could be empty in case of the SafeSeed. if (!seed_info.permanent_country_code.empty()) {
diff --git a/components/variations/seed_reader_writer.h b/components/variations/seed_reader_writer.h index ece1900..ed3d90f 100644 --- a/components/variations/seed_reader_writer.h +++ b/components/variations/seed_reader_writer.h
@@ -47,6 +47,7 @@ base::Time seed_date, base::Time client_fetch_time, std::string_view session_country_code, + std::string_view session_geo_level1, std::string_view permanent_country_code, std::string_view permanent_country_version); ~SeedInfo(); @@ -67,6 +68,10 @@ // Latest country code fetched from the server. Used for evaluating session // consistency studies. const std::string session_country_code; + // Latest administrative area code fetched from the server. Mirrors the + // `session_country_code` field, but is not currently used for evaluating + // studies. + const std::string session_geo_level1; // Country code used for evaluating permanent consistency studies. const std::string permanent_country_code; // Chrome version at the time `permanent_country_code` was updated. @@ -83,6 +88,7 @@ const base::Time seed_date; const base::Time client_fetch_time; const std::string_view session_country_code; + const std::string_view session_geo_level1; const std::string_view permanent_country_code; const std::string_view permanent_country_version; }; @@ -94,6 +100,8 @@ const char* seed_date; const char* client_fetch_time; const char* session_country_code; + // `session_geo_level1` may be null. + const char* session_geo_level1; const char* permanent_country_code_version; };
diff --git a/components/variations/seed_reader_writer_unittest.cc b/components/variations/seed_reader_writer_unittest.cc index 5a75f42..8707ee49 100644 --- a/components/variations/seed_reader_writer_unittest.cc +++ b/components/variations/seed_reader_writer_unittest.cc
@@ -7,6 +7,7 @@ #include "base/base64.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/logging.h" #include "base/strings/strcat.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" @@ -79,6 +80,7 @@ stored_seed_info.set_signature("signature"); stored_seed_info.set_milestone(92); stored_seed_info.set_session_country_code("us"); + stored_seed_info.set_session_geo_level1("us-ny"); stored_seed_info.set_seed_date(1234); return stored_seed_info; } @@ -104,6 +106,9 @@ : SeedReaderWriterTestParams(std::get<0>(t), std::get<1>(t), std::get<2>(t)) {} + bool HasGeoLevel1Pref() const { + return seed_fields_prefs.session_geo_level1 != nullptr; + } SeedFieldsPrefs seed_fields_prefs; std::string_view field_trial_group; @@ -121,6 +126,10 @@ explicit ExpectedFieldTrialGroupTestParams(const TupleT& t) : ExpectedFieldTrialGroupTestParams(std::get<0>(t), std::get<1>(t)) {} + bool HasGeoLevel1Pref() const { + return seed_fields_prefs.session_geo_level1 != nullptr; + } + variations::SeedFieldsPrefs seed_fields_prefs; version_info::Channel channel; }; @@ -293,6 +302,7 @@ .seed_date = seed_date, .client_fetch_time = fetch_time, .session_country_code = "us", + .session_geo_level1 = "us-ny", }); // Force write. @@ -349,6 +359,7 @@ ASSERT_NE(seed_info.milestone, 0); ASSERT_FALSE(seed_info.seed_date.is_null()); ASSERT_THAT(seed_info.session_country_code, Not(IsEmpty())); + ASSERT_THAT(seed_info.session_geo_level1, Not(IsEmpty())); // Clear seed and force write. seed_reader_writer.ClearSeedInfo(); @@ -365,6 +376,8 @@ // Session country code is not cleared. EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_country_code, Not(IsEmpty())); + EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_geo_level1, + Not(IsEmpty())); // Verify that the seed info in the seed file is cleared. StoredSeedInfo cleared_seed_info = ReadStoredSeedInfo(); @@ -375,6 +388,7 @@ EXPECT_EQ(cleared_seed_info.client_fetch_time(), 0); // Session country code is not cleared. EXPECT_THAT(cleared_seed_info.session_country_code(), Not(IsEmpty())); + EXPECT_THAT(cleared_seed_info.session_geo_level1(), Not(IsEmpty())); } // Verifies that session country code is cleared from a seed file for clients in @@ -384,6 +398,10 @@ GetParam().field_trial_group); local_state_.SetString(GetParam().seed_fields_prefs.session_country_code, "us"); + if (GetParam().HasGeoLevel1Pref()) { + local_state_.SetString(GetParam().seed_fields_prefs.session_geo_level1, + "us-ny"); + } // Initialize seed_reader_writer with test thread and timer. SeedReaderWriter seed_reader_writer( &local_state_, /*seed_file_dir=*/temp_dir_.GetPath(), kSeedFilename, @@ -393,15 +411,25 @@ ASSERT_THAT(seed_reader_writer.GetSeedInfo().session_country_code, Not(IsEmpty())); + if (GetParam().HasGeoLevel1Pref()) { + ASSERT_THAT(seed_reader_writer.GetSeedInfo().session_geo_level1, + Not(IsEmpty())); + } seed_reader_writer.ClearSessionCountry(); // Session country code is cleared. EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_country_code, IsEmpty()); + EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_geo_level1, IsEmpty()); // Local state pref should be cleared. EXPECT_THAT( local_state_.GetString(GetParam().seed_fields_prefs.session_country_code), IsEmpty()); + if (GetParam().HasGeoLevel1Pref()) { + EXPECT_THAT( + local_state_.GetString(GetParam().seed_fields_prefs.session_geo_level1), + IsEmpty()); + } } // Verifies clients in SeedFiles group read seeds from the seed file. @@ -956,6 +984,7 @@ .seed_date = seed_date, .client_fetch_time = fetch_time, .session_country_code = "us", + .session_geo_level1 = "us-ny", }); // Ensure there's no pending write. @@ -1002,6 +1031,11 @@ local_state_.SetString(GetParam().seed_fields_prefs.session_country_code, "us"); + if (GetParam().HasGeoLevel1Pref()) { + local_state_.SetString(GetParam().seed_fields_prefs.session_geo_level1, + "us-ny"); + } + // Clear seed and force file delete. seed_reader_writer.ClearSeedInfo(); file_writer_thread_.FlushForTesting(); @@ -1017,6 +1051,10 @@ // Session country code is not cleared. EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_country_code, Not(IsEmpty())); + if (GetParam().HasGeoLevel1Pref()) { + EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_geo_level1, + Not(IsEmpty())); + } // Verify seed cleared correctly in Local State prefs and that seed file is // deleted. @@ -1034,6 +1072,12 @@ EXPECT_THAT( local_state_.GetString(GetParam().seed_fields_prefs.session_country_code), Not(IsEmpty())); + if (GetParam().HasGeoLevel1Pref()) { + EXPECT_THAT( + local_state_.GetString(GetParam().seed_fields_prefs.session_geo_level1), + Not(IsEmpty())); + } + EXPECT_FALSE(base::PathExists(temp_seed_file_path_)); } @@ -1052,6 +1096,10 @@ // Create and store seed. local_state_.SetString(GetParam().seed_fields_prefs.session_country_code, "us"); + if (GetParam().HasGeoLevel1Pref()) { + local_state_.SetString(GetParam().seed_fields_prefs.session_geo_level1, + "us-ny"); + } // Clear seed and force file delete. seed_reader_writer.ClearSessionCountry(); @@ -1061,6 +1109,13 @@ EXPECT_THAT( local_state_.GetString(GetParam().seed_fields_prefs.session_country_code), IsEmpty()); + + EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_geo_level1, IsEmpty()); + if (GetParam().HasGeoLevel1Pref()) { + EXPECT_THAT( + local_state_.GetString(GetParam().seed_fields_prefs.session_geo_level1), + IsEmpty()); + } } // Verifies clients using local state to store seeds read seeds from local
diff --git a/components/variations/seed_response.h b/components/variations/seed_response.h index d17d74c4..3b1daa12 100644 --- a/components/variations/seed_response.h +++ b/components/variations/seed_response.h
@@ -21,6 +21,7 @@ std::string data; // "data" is binary, for which protobuf uses strings. std::string signature; std::string country; + std::string geo_level1; base::Time date; bool is_gzip_compressed = false; };
diff --git a/components/variations/service/variations_field_trial_creator.cc b/components/variations/service/variations_field_trial_creator.cc index 5e7adcc..61d97bd 100644 --- a/components/variations/service/variations_field_trial_creator.cc +++ b/components/variations/service/variations_field_trial_creator.cc
@@ -801,6 +801,7 @@ } seed_store_->StoreSeedData(/*done_callback=*/base::DoNothing(), decoded_seed, seed_signature->GetString(), /*country_code=*/"", + /*geo_level1=*/"", /*date_fetched=*/base::Time(), /*is_delta_compressed=*/false, /*is_gzip_compressed=*/true,
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc index 8a82dde4..f14ea5f 100644 --- a/components/variations/service/variations_service.cc +++ b/components/variations/service/variations_service.cc
@@ -707,6 +707,7 @@ void VariationsService::StoreSeed(std::string seed_data, std::string seed_signature, std::string country_code, + std::string geo_level1, base::Time date_fetched, bool is_delta_compressed, bool is_gzip_compressed) { @@ -717,8 +718,8 @@ weak_ptr_factory_.GetWeakPtr(), is_delta_compressed); field_trial_creator_.seed_store()->StoreSeedData( std::move(done_callback), std::move(seed_data), std::move(seed_signature), - std::move(country_code), date_fetched, is_delta_compressed, - is_gzip_compressed, + std::move(country_code), std::move(geo_level1), date_fetched, + is_delta_compressed, is_gzip_compressed, /*require_synchronous=*/false); } @@ -899,9 +900,11 @@ std::string_view signature = GetHeaderValue(headers.get(), "X-Seed-Signature"); std::string_view country_code = GetHeaderValue(headers.get(), "X-Country"); + std::string_view geo_level1 = GetHeaderValue(headers.get(), "X-Geo-Level-1"); StoreSeed(std::move(*response_body), std::string(signature), - std::string(country_code), response_date.value_or(base::Time()), - is_delta_compressed, is_gzip_compressed); + std::string(country_code), std::string(geo_level1), + response_date.value_or(base::Time()), is_delta_compressed, + is_gzip_compressed); } bool VariationsService::MaybeRetryOverHTTP() {
diff --git a/components/variations/service/variations_service.h b/components/variations/service/variations_service.h index 5cf521a..2c236d3 100644 --- a/components/variations/service/variations_service.h +++ b/components/variations/service/variations_service.h
@@ -285,6 +285,7 @@ virtual void StoreSeed(std::string seed_data, std::string seed_signature, std::string country_code, + std::string geo_level1, base::Time date_fetched, bool is_delta_compressed, bool is_gzip_compressed);
diff --git a/components/variations/service/variations_service_unittest.cc b/components/variations/service/variations_service_unittest.cc index 53efeba..ac5e21e 100644 --- a/components/variations/service/variations_service_unittest.cc +++ b/components/variations/service/variations_service_unittest.cc
@@ -135,14 +135,7 @@ : VariationsService(std::make_unique<TestVariationsServiceClient>(), std::move(test_notifier), local_state, - state_manager), - intercepts_fetch_(true), - fetch_attempted_(false), - latest_serial_number_(""), - seed_stores_succeed_(true), - seed_stored_(false), - delta_compressed_seed_(false), - gzip_compressed_seed_(false) { + state_manager) { interception_url_ = GetVariationsServerURL(use_secure_url ? USE_HTTPS : USE_HTTP); set_variations_server_url(interception_url_); @@ -168,6 +161,7 @@ bool fetch_attempted() const { return fetch_attempted_; } bool seed_stored() const { return seed_stored_; } const std::string& stored_country() const { return stored_country_; } + const std::string& stored_geo_level() const { return stored_geo_level_; } bool delta_compressed_seed() const { return delta_compressed_seed_; } bool gzip_compressed_seed() const { return gzip_compressed_seed_; } @@ -199,12 +193,14 @@ void StoreSeed(std::string seed_data, std::string seed_signature, std::string country_code, + std::string geo_level1, base::Time date_fetched, bool is_delta_compressed, bool is_gzip_compressed) override { seed_stored_ = true; stored_seed_data_ = seed_data; stored_country_ = country_code; + stored_geo_level_ = geo_level1; delta_compressed_seed_ = is_delta_compressed; gzip_compressed_seed_ = is_gzip_compressed; OnSeedStoreResult(is_delta_compressed, seed_stores_succeed_, @@ -222,15 +218,16 @@ private: GURL interception_url_; - bool intercepts_fetch_; - bool fetch_attempted_; + bool intercepts_fetch_ = true; + bool fetch_attempted_ = false; std::string latest_serial_number_; - bool seed_stores_succeed_; - bool seed_stored_; + bool seed_stores_succeed_ = true; + bool seed_stored_ = false; std::string stored_seed_data_; std::string stored_country_; - bool delta_compressed_seed_; - bool gzip_compressed_seed_; + std::string stored_geo_level_; + bool delta_compressed_seed_ = false; + bool gzip_compressed_seed_ = false; }; class TestVariationsServiceObserver : public VariationsService::Observer { @@ -653,6 +650,7 @@ head->headers = base::MakeRefCounted<net::HttpResponseHeaders>( net::HttpUtil::AssembleRawHeaders(headers)); head->headers->SetHeader("X-Country", "test"); + head->headers->SetHeader("X-Geo-Level-1", "test-geo-level"); network::URLLoaderCompletionStatus status; status.decoded_body_length = serialized_seed.size(); service.test_url_loader_factory()->AddResponse( @@ -662,6 +660,7 @@ EXPECT_TRUE(service.seed_stored()); EXPECT_EQ("test", service.stored_country()); + EXPECT_EQ("test-geo-level", service.stored_geo_level()); } TEST_F(VariationsServiceTest, Observer) {
diff --git a/components/variations/variations_seed_store.cc b/components/variations/variations_seed_store.cc index 2c40587..6cb624fe 100644 --- a/components/variations/variations_seed_store.cc +++ b/components/variations/variations_seed_store.cc
@@ -313,6 +313,7 @@ std::string data, std::string base64_seed_signature, std::string country_code, + std::string geo_level1, base::Time date_fetched, bool is_delta_compressed, bool is_gzip_compressed, @@ -333,6 +334,7 @@ seed_data.data = std::move(data); seed_data.base64_seed_signature = std::move(base64_seed_signature); seed_data.country_code = std::move(country_code); + seed_data.geo_level1 = std::move(geo_level1); seed_data.date_fetched = date_fetched; seed_data.is_gzip_compressed = is_gzip_compressed; seed_data.is_delta_compressed = is_delta_compressed; @@ -556,6 +558,7 @@ // Regular seed prefs: registry->RegisterStringPref(prefs::kVariationsCompressedSeed, std::string()); registry->RegisterStringPref(prefs::kVariationsCountry, std::string()); + registry->RegisterStringPref(prefs::kVariationsGeoLevel1, std::string()); registry->RegisterTimePref(prefs::kVariationsLastFetchTime, base::Time()); registry->RegisterIntegerPref(prefs::kVariationsSeedMilestone, 0); registry->RegisterTimePref(prefs::kVariationsSeedDate, base::Time()); @@ -646,7 +649,8 @@ }); StoreSeedData(std::move(done_callback), std::move(initial_seed->data), std::move(initial_seed->signature), - std::move(initial_seed->country), initial_seed->date, + std::move(initial_seed->country), + std::move(initial_seed->geo_level1), initial_seed->date, /*is_delta_compressed=*/false, initial_seed->is_gzip_compressed, /*require_synchronous=*/true); } @@ -844,7 +848,8 @@ base::BindOnce(&VariationsSeedStore::StoreValidatedSeed, weak_ptr_factory_.GetWeakPtr(), std::move(done_callback), std::move(result.validated), result.seed_data.country_code, - result.seed_data.date_fetched, require_synchronous); + result.seed_data.geo_level1, result.seed_data.date_fetched, + require_synchronous); ReadSeedData(/*done_callback=*/std::move(store_validated_seed_cb), SeedType::SAFE, require_synchronous); } @@ -880,6 +885,7 @@ base::OnceCallback<void(bool, VariationsSeed)> done_callback, ValidatedSeed seed, std::string country_code, + std::string geo_level1, base::Time date_fetched, bool require_synchronous, SeedReaderWriter::ReadSeedDataResult safe_seed_read_result) { @@ -914,6 +920,7 @@ .seed_date = date_fetched, .client_fetch_time = base::Time::Now(), .session_country_code = country_code, + .session_geo_level1 = geo_level1, }); if (result == StoreSeedResult::kSuccess) { StoreLatestSerialNumber(seed.parsed.serial_number());
diff --git a/components/variations/variations_seed_store.h b/components/variations/variations_seed_store.h index 8f9a66b..7a0f2eb 100644 --- a/components/variations/variations_seed_store.h +++ b/components/variations/variations_seed_store.h
@@ -135,6 +135,7 @@ std::string data, std::string base64_seed_signature, std::string country_code, + std::string geo_level1, base::Time date_fetched, bool is_delta_compressed, bool is_gzip_compressed, @@ -306,6 +307,7 @@ std::string data; std::string base64_seed_signature; std::string country_code; + std::string geo_level1; base::Time date_fetched; bool is_gzip_compressed = false; bool is_delta_compressed = false; @@ -409,6 +411,7 @@ base::OnceCallback<void(bool, VariationsSeed)> done_callback, ValidatedSeed seed, std::string country_code, + std::string geo_level1, base::Time date_fetched, bool require_synchronous, SeedReaderWriter::ReadSeedDataResult read_result);
diff --git a/components/variations/variations_seed_store_unittest.cc b/components/variations/variations_seed_store_unittest.cc index 6bea762..12702bb 100644 --- a/components/variations/variations_seed_store_unittest.cc +++ b/components/variations/variations_seed_store_unittest.cc
@@ -260,6 +260,7 @@ .seed_date = now - delta * 1, .client_fetch_time = now, .session_country_code = "us", + .session_geo_level1 = "us-ny", }); seed_store.SetSerialNumberForTesting("123"); @@ -274,6 +275,7 @@ .seed_date = now - delta * 2, .client_fetch_time = now - delta * 3, .session_country_code = "gt", + .session_geo_level1 = "gt-16", .permanent_country_code = "mx", }); prefs->SetString(prefs::kVariationsSafeSeedLocale, "en-MX"); @@ -1472,6 +1474,7 @@ struct Params { std::string country_code; + std::string geo_level1; bool is_delta_compressed; bool is_gzip_compressed; }; @@ -1488,7 +1491,7 @@ &StoreSeedDataGroupTest::OnSeedStoreResult, base::Unretained(this), run_loop.QuitClosure()), seed_data, /*base64_seed_signature=*/std::string(), params.country_code, - base::Time::Now(), params.is_delta_compressed, + params.geo_level1, base::Time::Now(), params.is_delta_compressed, params.is_gzip_compressed, RequireSynchronousStores()); // If we're testing synchronous stores, we shouldn't issue a Run() call so // that the test verifies that the operation completed synchronously. @@ -1629,6 +1632,33 @@ EXPECT_EQ("test_country", GetSeedInfo(seed_store).session_country_code); } +TEST_P(StoreSeedDataAllGroupsTest, GeoLevelIgnoredWithoutCountry) { + TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath()); + std::string seed = SerializeSeed(CreateTestSeed()); + + // Test with no country code specified - which should skip the geo level. + ASSERT_TRUE( + StoreSeedData(seed_store, seed, {.geo_level1 = "test_geo_level1"})); + EXPECT_EQ("", GetSeedInfo(seed_store).session_country_code); + EXPECT_EQ("", GetSeedInfo(seed_store).session_geo_level1); + + // Test with a valid values. + ASSERT_TRUE(StoreSeedData(seed_store, seed, + { + .country_code = "test_country", + .geo_level1 = "test_geo_level1", + })); + EXPECT_EQ("test_country", GetSeedInfo(seed_store).session_country_code); + EXPECT_EQ("test_geo_level1", GetSeedInfo(seed_store).session_geo_level1); + + // Test with no country code specified - which should preserve the old geo + // level. + ASSERT_TRUE( + StoreSeedData(seed_store, seed, {.geo_level1 = "test_geo_level2"})); + EXPECT_EQ("test_country", GetSeedInfo(seed_store).session_country_code); + EXPECT_EQ("test_geo_level1", GetSeedInfo(seed_store).session_geo_level1); +} + TEST_P(StoreSeedDataAllGroupsTest, GzippedSeed) { TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath()); ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial),
diff --git a/components/viz/service/layers/layer_context_impl.cc b/components/viz/service/layers/layer_context_impl.cc index 3cf1e469..3f7ed13 100644 --- a/components/viz/service/layers/layer_context_impl.cc +++ b/components/viz/service/layers/layer_context_impl.cc
@@ -1979,21 +1979,23 @@ !std::isfinite(update->max_safe_area_inset_bottom)) { return base::unexpected("Invalid max safe area inset bottom"); } - if (update->browser_controls_params.top_controls_height < 0 || - !std::isfinite(update->browser_controls_params.top_controls_height) || - update->browser_controls_params.top_controls_min_height < 0 || + if (!std::isfinite(update->browser_controls_params.top_controls_height) || !std::isfinite(update->browser_controls_params.top_controls_min_height) || - update->browser_controls_params.bottom_controls_height < 0 || !std::isfinite(update->browser_controls_params.bottom_controls_height) || - update->browser_controls_params.bottom_controls_min_height < 0 || !std::isfinite( - update->browser_controls_params.bottom_controls_min_height) || - update->browser_controls_params.top_controls_min_height > - update->browser_controls_params.top_controls_height || - update->browser_controls_params.bottom_controls_min_height > - update->browser_controls_params.bottom_controls_height) { + update->browser_controls_params.bottom_controls_min_height)) { return base::unexpected("Invalid browser controls params"); } + update->browser_controls_params.top_controls_height = + std::max(0.f, update->browser_controls_params.top_controls_height); + update->browser_controls_params.top_controls_min_height = + std::clamp(update->browser_controls_params.top_controls_min_height, 0.f, + update->browser_controls_params.top_controls_height); + update->browser_controls_params.bottom_controls_height = + std::max(0.f, update->browser_controls_params.bottom_controls_height); + update->browser_controls_params.bottom_controls_min_height = + std::clamp(update->browser_controls_params.bottom_controls_min_height, + 0.f, update->browser_controls_params.bottom_controls_height); layers.SetBrowserControlsParams(update->browser_controls_params); host_impl_->browser_controls_manager()->SetOffsetTagModifications( update->browser_controls_offset_tag_modifications);
diff --git a/components/viz/service/layers/layer_context_impl_unittest.cc b/components/viz/service/layers/layer_context_impl_unittest.cc index 9414903..4be873f 100644 --- a/components/viz/service/layers/layer_context_impl_unittest.cc +++ b/components/viz/service/layers/layer_context_impl_unittest.cc
@@ -4387,8 +4387,7 @@ LayerContextImplUpdateDisplayTreeInvalidBrowserControlsTest, ::testing::Values(std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), - std::numeric_limits<float>::quiet_NaN(), - -1.0f), + std::numeric_limits<float>::quiet_NaN()), [](const testing::TestParamInfo< LayerContextImplUpdateDisplayTreeInvalidBrowserControlsTest::ParamType>& info) { @@ -4398,28 +4397,47 @@ if (std::isnan(info.param)) { return "NaN"; } - if (info.param < 0.0f) { - return "Negative"; - } return "Other"; }); -TEST_F(LayerContextImplTest, InvalidMinMaxBrowserControlsHeight) { +TEST_F(LayerContextImplTest, ClampedMinMaxBrowserControlsHeight) { auto update = CreateDefaultUpdate(); update->browser_controls_params.top_controls_height = 10.f; update->browser_controls_params.top_controls_min_height = 20.f; auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update)); - ASSERT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), "Invalid browser controls params"); + ASSERT_TRUE(result.has_value()); + const auto& params = layer_context_impl_->host_impl() + ->active_tree() + ->browser_controls_params(); + EXPECT_EQ(params.top_controls_height, 10.f); + EXPECT_EQ(params.top_controls_min_height, 10.f); update = CreateDefaultUpdate(); update->browser_controls_params.bottom_controls_height = 10.f; update->browser_controls_params.bottom_controls_min_height = 20.f; result = layer_context_impl_->DoUpdateDisplayTree(std::move(update)); - ASSERT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), "Invalid browser controls params"); + ASSERT_TRUE(result.has_value()); + const auto& params2 = layer_context_impl_->host_impl() + ->active_tree() + ->browser_controls_params(); + EXPECT_EQ(params2.bottom_controls_height, 10.f); + EXPECT_EQ(params2.bottom_controls_min_height, 10.f); +} + +TEST_F(LayerContextImplTest, NegativeBrowserControlsHeight) { + auto update = CreateDefaultUpdate(); + update->browser_controls_params.top_controls_height = -10.f; + update->browser_controls_params.top_controls_min_height = -5.f; + + auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update)); + ASSERT_TRUE(result.has_value()); + const auto& params = layer_context_impl_->host_impl() + ->active_tree() + ->browser_controls_params(); + EXPECT_EQ(params.top_controls_height, 0.f); + EXPECT_EQ(params.top_controls_min_height, 0.f); } class LayerContextImplUpdateDisplayTreeInvalidElasticOverscrollTest
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h index 372417d..54e2b1c 100644 --- a/content/browser/bad_message.h +++ b/content/browser/bad_message.h
@@ -361,6 +361,7 @@ RFH_NEW_ISOLATED_WEB_APP_PERMISSION_POLICIES = 333, RFH_CREATE_NEW_WINDOW_INVALID_DISPOSITION = 334, RFH_CREATE_NEW_WINDOW_FROM_SANDBOXED_FRAME = 335, + RFH_MODAL_DIALOG_FROM_SANDBOXED_FRAME = 336, // Please add new elements here. The naming convention is abbreviated class // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/preloading/prefetch/prefetch_match_resolver.cc b/content/browser/preloading/prefetch/prefetch_match_resolver.cc index 43dff576..f2a4faaf 100644 --- a/content/browser/preloading/prefetch/prefetch_match_resolver.cc +++ b/content/browser/preloading/prefetch/prefetch_match_resolver.cc
@@ -53,6 +53,7 @@ PrefetchServiceWorkerState expected_service_worker_state, bool is_nav_prerender, base::WeakPtr<PrerenderHost> prerender_host, + PrerenderHostId prerender_host_id, scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info, Callback callback, perfetto::Flow flow) @@ -64,6 +65,7 @@ flow_(std::move(flow)), is_nav_prerender_(is_nav_prerender), prerender_host_for_metrics_(std::move(prerender_host)), + prerender_host_id_(std::move(prerender_host_id)), preload_pipeline_info_(std::move(preload_pipeline_info)), prefetch_match_metrics_(std::make_unique<PrefetchMatchMetrics>()) { switch (expected_service_worker_state_) { @@ -76,6 +78,10 @@ break; } + // Valid preload pipeline info implies that the navigation is prerender (valid + // prerender host id). + CHECK(!preload_pipeline_info_ || prerender_host_id_); + prefetch_match_metrics_->expected_service_worker_state = expected_service_worker_state; prefetch_match_metrics_->time_match_start = base::TimeTicks::Now(); @@ -126,6 +132,8 @@ return frame_tree_node->navigation_request()->GetWeakPtr(); })(); + PrerenderHostId prerender_host_id = + frame_tree_node->frame_tree().delegate()->GetPrerenderHostId(); auto prerender_host = ([&]() -> base::WeakPtr<PrerenderHost> { PrerenderHostRegistry* prerender_host_registry = frame_tree_node->current_frame_host() @@ -135,8 +143,6 @@ return nullptr; } - PrerenderHostId prerender_host_id = - frame_tree_node->frame_tree().delegate()->GetPrerenderHostId(); PrerenderHost* prerender_host = prerender_host_registry->FindNonReservedHostById(prerender_host_id); if (!prerender_host) { @@ -155,7 +161,8 @@ std::move(navigation_request), prefetch_service, std::move(navigated_key), expected_service_worker_state, frame_tree_node->frame_tree().is_prerendering(), - std::move(prerender_host), std::move(preload_pipeline_info), + std::move(prerender_host), std::move(prerender_host_id), + std::move(preload_pipeline_info), std::move(serving_page_metrics_container), std::move(callback), std::move(flow)); } @@ -172,7 +179,9 @@ PrefetchMatchResolver::FindPrefetchInternal1( /*navigation_request=*/nullptr, prefetch_service, std::move(navigated_key), expected_service_worker_state, is_nav_prerender, - /*prerender_host=*/nullptr, /*preload_pipeline_info=*/nullptr, + /*prerender_host=*/nullptr, + /*prerender_host_id=*/PrerenderHostId(), + /*preload_pipeline_info=*/nullptr, std::move(serving_page_metrics_container), std::move(callback), perfetto::Flow::ProcessScoped(0)); } @@ -185,6 +194,7 @@ PrefetchServiceWorkerState expected_service_worker_state, bool is_nav_prerender, base::WeakPtr<PrerenderHost> prerender_host, + PrerenderHostId prerender_host_id, scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info, base::WeakPtr<PrefetchServingPageMetricsContainer> serving_page_metrics_container, @@ -200,8 +210,8 @@ auto prefetch_match_resolver = base::WrapUnique(new PrefetchMatchResolver( std::move(navigation_request), prefetch_service.GetWeakPtr(), std::move(navigated_key), expected_service_worker_state, is_nav_prerender, - std::move(prerender_host), std::move(preload_pipeline_info), - std::move(callback), std::move(flow))); + std::move(prerender_host), std::move(prerender_host_id), + std::move(preload_pipeline_info), std::move(callback), std::move(flow))); PrefetchMatchResolver& ref = *prefetch_match_resolver.get(); ref.self_ = std::move(prefetch_match_resolver); @@ -723,8 +733,10 @@ // If this is for a prerender initial navigation, mark its preload pipeline is // using a prefetch. Note that the prefetch may not be the prefetch ahead of // prerender. + CHECK(!preload_pipeline_info_ || prerender_host_id_); if (preload_pipeline_info_) { - preload_pipeline_info_->MarkPrerenderMatchedWithPrefetch(); + preload_pipeline_info_->MarkPrerenderMatchedWithPrefetch( + prerender_host_id_); } PrefetchServingHandle serving_handle =
diff --git a/content/browser/preloading/prefetch/prefetch_match_resolver.h b/content/browser/preloading/prefetch/prefetch_match_resolver.h index d46c7a6b..8a5c3fc2 100644 --- a/content/browser/preloading/prefetch/prefetch_match_resolver.h +++ b/content/browser/preloading/prefetch/prefetch_match_resolver.h
@@ -228,6 +228,7 @@ PrefetchServiceWorkerState expected_service_worker_state, bool is_nav_prerender, base::WeakPtr<PrerenderHost> prerender_host, + PrerenderHostId prerender_host_id, scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info, base::WeakPtr<PrefetchServingPageMetricsContainer> serving_page_metrics_container, @@ -241,6 +242,7 @@ PrefetchServiceWorkerState expected_service_worker_state, bool is_nav_prerender, base::WeakPtr<PrerenderHost> prerender_host, + PrerenderHostId prerender_host_id, scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info, Callback callback, perfetto::Flow flow); @@ -332,9 +334,10 @@ // `PreloadServingMetricsCapsule::IsFeatureEnabled()` is false. base::WeakPtr<PrerenderHost> prerender_host_for_metrics_; - // `PreloadPipelineInfo` of prerender if the navigation is prerender. + // Attributes of prerender if the navigation is prerender. // // Non-null iff the navigation is prerender. + const PrerenderHostId prerender_host_id_; const scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info_; std::unique_ptr<PrefetchMatchMetrics> prefetch_match_metrics_;
diff --git a/content/browser/preloading/preload_pipeline_info_impl.cc b/content/browser/preloading/preload_pipeline_info_impl.cc index 8bf1d88..aceff733 100644 --- a/content/browser/preloading/preload_pipeline_info_impl.cc +++ b/content/browser/preloading/preload_pipeline_info_impl.cc
@@ -5,6 +5,7 @@ #include "content/browser/preloading/preload_pipeline_info_impl.h" #include "base/trace_event/trace_event.h" +#include "content/browser/preloading/prerender/prerender_host.h" namespace content { @@ -62,8 +63,16 @@ prefetch_status_ = prefetch_status; } -void PreloadPipelineInfoImpl::MarkPrerenderMatchedWithPrefetch() { - is_prerender_matched_with_prefetch_ = true; +bool PreloadPipelineInfoImpl::IsPrerenderMatchedWithPrefetch( + const PrerenderHostId& prerender_host_id) const { + return prerender_ids_matched_with_prefetch_.contains(prerender_host_id); +} + +void PreloadPipelineInfoImpl::MarkPrerenderMatchedWithPrefetch( + PrerenderHostId prerender_host_id) { + CHECK(!IsPrerenderMatchedWithPrefetch(prerender_host_id)); + + prerender_ids_matched_with_prefetch_.insert(prerender_host_id); } } // namespace content
diff --git a/content/browser/preloading/preload_pipeline_info_impl.h b/content/browser/preloading/preload_pipeline_info_impl.h index cf30879..078a608 100644 --- a/content/browser/preloading/preload_pipeline_info_impl.h +++ b/content/browser/preloading/preload_pipeline_info_impl.h
@@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_PRELOADING_PRELOAD_PIPELINE_INFO_IMPL_H_ #define CONTENT_BROWSER_PRELOADING_PRELOAD_PIPELINE_INFO_IMPL_H_ +#include "base/containers/flat_set.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_refptr.h" #include "base/unguessable_token.h" @@ -12,6 +13,7 @@ #include "content/public/browser/navigation_handle.h" #include "content/public/browser/preload_pipeline_info.h" #include "content/public/browser/preloading.h" +#include "content/public/browser/prerender_host_id.h" #include "third_party/perfetto/include/perfetto/tracing/track_event_args.h" namespace content { @@ -50,10 +52,9 @@ } void SetPrefetchStatus(PrefetchStatus prefetch_status); - bool is_prerender_matched_with_prefetch() const { - return is_prerender_matched_with_prefetch_; - } - void MarkPrerenderMatchedWithPrefetch(); + bool IsPrerenderMatchedWithPrefetch( + const PrerenderHostId& prerender_host_id) const; + void MarkPrerenderMatchedWithPrefetch(PrerenderHostId prerender_host_id); private: friend class base::RefCounted<PreloadPipelineInfo>; @@ -70,9 +71,9 @@ PreloadingEligibility::kUnspecified; std::optional<PrefetchStatus> prefetch_status_ = std::nullopt; - // Set to true when the prerender in this pipeline matches a prefetch. - // Note that the prefetch may not be the prefetch in this pipeline. - bool is_prerender_matched_with_prefetch_ = false; + // Records `PrerenderHostId` that matched to a prefetch. Note that the + // prefetch may not be the prefetch in this pipeline. + base::flat_set<PrerenderHostId> prerender_ids_matched_with_prefetch_; }; } // namespace content
diff --git a/content/browser/preloading/prerender/prerender_host.cc b/content/browser/preloading/prerender/prerender_host.cc index 6b0321f..6dfe382 100644 --- a/content/browser/preloading/prerender/prerender_host.cc +++ b/content/browser/preloading/prerender/prerender_host.cc
@@ -1817,7 +1817,8 @@ // Use a prefetch (in many cases, aheaf of prerender) if it is about to be // used. - if (attributes_.preload_pipeline_info->is_prerender_matched_with_prefetch()) { + if (attributes_.preload_pipeline_info->IsPrerenderMatchedWithPrefetch( + prerender_host_id())) { return false; }
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index c5ee522..12304d4 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -7244,6 +7244,16 @@ JavaScriptDialogType dialog_type, bool disable_third_party_subframe_suppresion, JavaScriptDialogCallback ipc_response_callback) { + // Sandboxed frames should only be allowed to show modal dialogs when they + // have the "allow-modals" attribute. This should have already been checked + // by the renderer process (see LocalDOMWindow::alert/confirm/prompt), and + // this browser-side check defends against compromised renderers. + if (IsSandboxed(network::mojom::WebSandboxFlags::kModals)) { + bad_message::ReceivedBadMessage( + GetProcess(), bad_message::RFH_MODAL_DIALOG_FROM_SANDBOXED_FRAME); + return; + } + // Don't show the dialog if it's triggered on a non-active RenderFrameHost // or is contained in a Fenced Frame. if (!IsActive() || IsNestedWithinFencedFrame()) {
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h index 5664e61..7a1bdc10 100644 --- a/content/browser/renderer_host/render_frame_host_impl.h +++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -3493,6 +3493,8 @@ CreateNewWindowWithInaccessibleFile); FRIEND_TEST_ALL_PREFIXES(SecurityExploitBrowserTest, WindowOpenDisallowedFromSandboxedFrame); + FRIEND_TEST_ALL_PREFIXES(SecurityExploitBrowserTest, + ModalDialogDisallowedFromSandboxedFrame); FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest, RenderViewHostIsNotReusedAfterDelayedUnloadACK); FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc index ccf0287..d2fea7e 100644 --- a/content/browser/security_exploit_browsertest.cc +++ b/content/browser/security_exploit_browsertest.cc
@@ -1160,6 +1160,51 @@ EXPECT_EQ(1u, Shell::windows().size()); } +// Regression test for browser-side validation of the allow-modals sandbox +// attribute. A sandboxed frame without allow-modals should not be able to show +// modal dialogs (alert, confirm, prompt). This is a variant of +// WindowOpenDisallowedFromSandboxedFrame for the kModals sandbox flag. +IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, + ModalDialogDisallowedFromSandboxedFrame) { + IsolateOrigin("b.com"); + + GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + WebContentsImpl* web_contents = + static_cast<WebContentsImpl*>(shell()->web_contents()); + FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root(); + RenderFrameHostImpl* main_frame = root->current_frame_host(); + + // Create cross-site sandboxed child frame. The frame lacks the allow-modals + // attribute, so it should not be allowed to show modal dialogs. + GURL child_url(embedded_test_server()->GetURL("b.com", "/title2.html")); + { + std::string js_str = base::StringPrintf( + "var frame = document.createElement('iframe'); " + "frame.sandbox = 'allow-scripts'; " + "frame.src = '%s'; " + "document.body.appendChild(frame);", + child_url.spec().c_str()); + EXPECT_TRUE(ExecJs(main_frame, js_str)); + ASSERT_TRUE(WaitForLoadStop(web_contents)); + } + + RenderFrameHostImpl* subframe = root->child_at(0)->current_frame_host(); + EXPECT_TRUE( + subframe->GetSiteInstance()->GetSecurityPrincipal().IsSandboxed()); + EXPECT_TRUE(subframe->IsSandboxed(network::mojom::WebSandboxFlags::kModals)); + + // Simulate that the b.com renderer is compromised and sends an IPC to show + // a modal dialog, bypassing renderer-side checks in LocalDOMWindow::alert(). + // The browser process should detect this and terminate the renderer. + RenderProcessHostBadIpcMessageWaiter kill_waiter(subframe->GetProcess()); + subframe->RunModalAlertDialog(u"test", false, base::DoNothing()); + EXPECT_EQ(bad_message::RFH_MODAL_DIALOG_FROM_SANDBOXED_FRAME, + kill_waiter.Wait()); + EXPECT_FALSE(subframe->IsRenderFrameLive()); +} + // Regression test for browser-side validation of POST submissions in // CreateNewWindow. See https://crbug.com/487768779. IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
diff --git a/docs/webui/webui_lit_style_guide.md b/docs/webui/webui_lit_style_guide.md index 4f56915..3a11be0 100644 --- a/docs/webui/webui_lit_style_guide.md +++ b/docs/webui/webui_lit_style_guide.md
@@ -374,7 +374,8 @@ Where the pattern to derive the class and file names from the DOM name is * DOM name: `foo-bar-baz` -* Class name candidates: `FooBarBazElement`, or `BarBazElement`, or `BazElement` +* Class name candidates: `<OptionalPrefix>FooBarBazElement`, or `BarBazElement`, + or `BazElement` * File name candidates: `foo_bar_baz.{css,html,ts}`, `bar_baz.{css,html,ts}` or `baz.{css,html,ts}`,
diff --git a/extensions/browser/service_worker/service_worker_state.cc b/extensions/browser/service_worker/service_worker_state.cc index b29eaeb..d6000569 100644 --- a/extensions/browser/service_worker/service_worker_state.cc +++ b/extensions/browser/service_worker/service_worker_state.cc
@@ -181,9 +181,6 @@ } if (renderer_state() != RendererState::kNotActive) { - // If the new token is different from the preexisting token, and it's still - // live, we are in an unexpected situation in which the content layer is - // tracking two different service workers. auto renderer_state_debug = renderer_state_; base::debug::Alias(&renderer_state_debug); CHECK(worker_id_.has_value()); @@ -194,13 +191,18 @@ base::debug::Alias(&preexisting_version_id); base::debug::Alias(&new_version_id); if (preexisting_token != new_token && + preexisting_version_id >= new_version_id && service_worker_context_->IsLiveServiceWorkerWithToken( preexisting_version_id, preexisting_token)) { + // We received an initialization signal for an older (or equal) worker + // version with a different token, while the worker we are currently + // tracking is still live. Record a crash dump for investigation and drop + // the stale IPC to prevent overwriting the current worker's state. base::debug::DumpWithoutCrashing(); return; } - // However, if the preexisting instance is actually not running anymore, - // then we should set a new `worker_id` here. + // TODO(andreaorru): if the preexisting version_id / service worker token is + // not valid anymore, should we untrack it from ProcessManager here? } SetWorkerId(worker_id);
diff --git a/infra/inclusive_language_presubmit_exempt_dirs.txt b/infra/inclusive_language_presubmit_exempt_dirs.txt index 5bf36bf..7395788 100644 --- a/infra/inclusive_language_presubmit_exempt_dirs.txt +++ b/infra/inclusive_language_presubmit_exempt_dirs.txt
@@ -669,7 +669,7 @@ third_party/rust/chromium_crates_io/vendor/icu_normalizer-v2/benches/data third_party/rust/chromium_crates_io/vendor/image-v0_25 third_party/rust/chromium_crates_io/vendor/indexmap-v2/.github/workflows -third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/workflows +third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows third_party/rust/chromium_crates_io/vendor/itoa-v1 third_party/rust/chromium_crates_io/vendor/itoa-v1/.github/workflows third_party/rust/chromium_crates_io/vendor/itoa-v1/src
diff --git a/internal b/internal index 5f3259b..926c416 160000 --- a/internal +++ b/internal
@@ -1 +1 @@ -Subproject commit 5f3259bc268e83f385b3d235a66f8fbd380336f8 +Subproject commit 926c416205c73ac88c808786ed9f9f1e659f6bfc
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm index 63d456c..c100dccb9 100644 --- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm +++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm
@@ -99,13 +99,19 @@ } } - [self - registerForTraitChanges: - @[ UITraitVerticalSizeClass.class, UITraitHorizontalSizeClass.class ] - withAction:@selector(updateLayout)]; + NSArray<UITrait>* traits = TraitCollectionSetForTraits( + @[ UITraitVerticalSizeClass.class, UITraitHorizontalSizeClass.class ]); + [self registerForTraitChanges:traits withAction:@selector(setNeedsLayout)]; [super didMoveToSuperview]; } +- (void)layoutSubviews { + [super layoutSubviews]; + // Perform updates during the layout phase to avoid re-entrancy issues + // during trait collection changes. + [self updateLayout]; +} + // Returns intrinsicContentSize based on the content of the toolbar. // When showing the floating Button the contentsize for the toolbar should be // zero so that the toolbar isn't accounted for when calculating the bottom
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm index a4603056..3277b92 100644 --- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm +++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
@@ -336,19 +336,23 @@ } } + NSArray<UITrait>* traits = TraitCollectionSetForTraits( + @[ UITraitVerticalSizeClass.class, UITraitHorizontalSizeClass.class ]); __weak TabGridTopToolbar* weakSelf = self; - [weakSelf - registerForTraitChanges: - @[ UITraitVerticalSizeClass.class, UITraitHorizontalSizeClass.class ] - withHandler:^(id<UITraitEnvironment> traitEnvironment, - UITraitCollection* previousCollection) { - [weakSelf - setButtonsForTraitCollection:weakSelf.traitCollection]; - }]; + [weakSelf registerForTraitChanges:traits + withAction:@selector(setNeedsLayout)]; [super didMoveToSuperview]; } +- (void)layoutSubviews { + [super layoutSubviews]; + // Perform updates during the layout phase to avoid re-entrancy issues + // during trait collection changes. + __weak TabGridTopToolbar* weakSelf = self; + [weakSelf setButtonsForTraitCollection:weakSelf.traitCollection]; +} + #pragma mark - Private // Returns a new button to be used.
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index d488ef9..e922fe8 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc
@@ -1123,6 +1123,10 @@ BASE_FEATURE(kUseAudioManagerMaxChannelLayout, base::FEATURE_DISABLED_BY_DEFAULT); +// Allows native temporal layer ID retrieval for NdkVideoEncodeAccelerator. +BASE_FEATURE(kNdkVideoEncodeAcceleratorNativeSvc, + base::FEATURE_DISABLED_BY_DEFAULT); + #endif // BUILDFLAG(IS_ANDROID) // TODO(crbug.com/414430336): Consider restricting to IS_CHROMEOS.
diff --git a/media/base/media_switches.h b/media/base/media_switches.h index a9986de1..839047ef 100644 --- a/media/base/media_switches.h +++ b/media/base/media_switches.h
@@ -388,6 +388,7 @@ MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseAudioLatencyFromHAL); MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseAudioManagerMaxChannelLayout); MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseSecurityLevelWhenCheckingMediaDrmVersion); +MEDIA_EXPORT BASE_DECLARE_FEATURE(kNdkVideoEncodeAcceleratorNativeSvc); #endif // BUILDFLAG(IS_ANDROID) #if BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
diff --git a/media/gpu/android/ndk_video_encode_accelerator.cc b/media/gpu/android/ndk_video_encode_accelerator.cc index 47dc148a..3cc62dd59 100644 --- a/media/gpu/android/ndk_video_encode_accelerator.cc +++ b/media/gpu/android/ndk_video_encode_accelerator.cc
@@ -475,6 +475,15 @@ GetOptimalLayeringSchema(log, codec_name, num_temporal_layers); AMediaFormat_setString(result.get(), AMEDIAFORMAT_KEY_TEMPORAL_LAYERING, svc_layer_config.c_str()); + + // Signal that we want to receive the temporal layer ID in the output + // format. + if (NdkVideoEncodeAcceleratorSvcApi::IsTemporalLayerIdSupported()) { + AMediaFormat_setInt32( + result.get(), + NdkVideoEncodeAcceleratorSvcApi::AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, + 0); + } } return result; @@ -1567,25 +1576,59 @@ input_since_keyframe_count_ = 0; } - TemporalScalabilityIdExtractor::BitstreamMetadata bits_md; - if (!svc_parser_->ParseChunk(out_buffer_data, input_since_keyframe_count_, - bits_md)) { - NotifyErrorStatus({EncoderStatus::Codes::kEncoderHardwareDriverError, - "Parse bitstream failed"}); + int32_t temporal_id = -1; + if (NdkVideoEncodeAcceleratorSvcApi::IsTemporalLayerIdSupported()) { + // For supported Android versions, retrieve the temporal layer ID + // natively from the output buffer format. + MediaFormatPtr buffer_format(AMediaCodec_getBufferFormat( + media_codec_->codec(), output_buffer.buffer_index)); + if (buffer_format) { + AMediaFormat_getInt32( + buffer_format.get(), + NdkVideoEncodeAcceleratorSvcApi::AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, + &temporal_id); + } + } + + if (temporal_id < 0 && VideoCodecProfileToVideoCodec( + config_.output_profile) == VideoCodec::kH264) { + // For H.264, if native retrieval is not supported or failed, + // fallback to parsing the bitstream. + TemporalScalabilityIdExtractor::BitstreamMetadata bits_md; + if (!svc_parser_->ParseChunk(out_buffer_data, input_since_keyframe_count_, + bits_md)) { + NotifyErrorStatus({EncoderStatus::Codes::kEncoderHardwareDriverError, + "Parse bitstream failed"}); + return; + } + temporal_id = bits_md.temporal_id; + } + + if (temporal_id < 0 && + NdkVideoEncodeAcceleratorSvcApi::IsTemporalLayerIdSupported()) { + // If native retrieval was expected but failed, treat it as a hardware + // error. For AV1/VP9 on older Android versions, we don't error out + // because these codecs follow standard scalability modes + // (follow_svc_spec = true), where layer information is available in the + // bitstream itself. + NotifyErrorStatus( + {EncoderStatus::Codes::kEncoderHardwareDriverError, + "Failed to retrieve temporal layer ID for SVC stream"}); return; } switch (VideoCodecProfileToVideoCodec(config_.output_profile)) { case VideoCodec::kH264: - metadata.h264.emplace().temporal_idx = bits_md.temporal_id; + metadata.h264.emplace().temporal_idx = temporal_id; break; case VideoCodec::kAV1: case VideoCodec::kVP9: - // TODO(b/432558680): We should query for this from the new temporal - // layer encoding API once it's available. Currently, the only encoders - // on Android that implement AV1 and VP9 temporal layer encoding are the - // cros-codecs ones, which we know to support SVC spec. metadata.svc_generic.emplace().follow_svc_spec = true; + if (NdkVideoEncodeAcceleratorSvcApi::IsTemporalLayerIdSupported()) { + // Native temporal ID is only expected for AV1/VP9 when supported + // by the platform. + metadata.svc_generic->temporal_idx = temporal_id; + } break; default: NOTIMPLEMENTED() << "SVC is only supported for AV1, H.264, and VP9.";
diff --git a/media/gpu/android/ndk_video_encode_accelerator_svc_api.cc b/media/gpu/android/ndk_video_encode_accelerator_svc_api.cc index be56be82..6314dea 100644 --- a/media/gpu/android/ndk_video_encode_accelerator_svc_api.cc +++ b/media/gpu/android/ndk_video_encode_accelerator_svc_api.cc
@@ -8,6 +8,7 @@ #include "base/files/file_path.h" #include "base/logging.h" #include "base/native_library.h" +#include "media/base/media_switches.h" namespace media { @@ -55,4 +56,14 @@ } } +// static +bool NdkVideoEncodeAcceleratorSvcApi::IsTemporalLayerIdSupported() { + if (__builtin_available(android 37, *)) { + return base::FeatureList::IsEnabled( + media::kNdkVideoEncodeAcceleratorNativeSvc); + } + + return false; +} + } // namespace media
diff --git a/media/gpu/android/ndk_video_encode_accelerator_svc_api.h b/media/gpu/android/ndk_video_encode_accelerator_svc_api.h index 22a80a0..e55b2609 100644 --- a/media/gpu/android/ndk_video_encode_accelerator_svc_api.h +++ b/media/gpu/android/ndk_video_encode_accelerator_svc_api.h
@@ -6,6 +6,7 @@ #define MEDIA_GPU_ANDROID_NDK_VIDEO_ENCODE_ACCELERATOR_SVC_API_H_ #include <media/NdkMediaCodec.h> +#include <stdint.h> #include "base/no_destructor.h" #include "media/gpu/media_gpu_export.h" @@ -46,6 +47,11 @@ ACodecEncoderCapabilities_getSupportedLayeringSchemas_Type ACodecEncoderCapabilities_getSupportedLayeringSchemas = nullptr; + static bool IsTemporalLayerIdSupported(); + + static constexpr const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID = + "temporal-layer-id"; + private: friend class base::NoDestructor<NdkVideoEncodeAcceleratorSvcApi>;
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index ea8172d..a451f16 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -118,26 +118,6 @@ ] } ], - "AXBlockFlowIterator": [ - { - "platforms": [ - "android", - "chromeos", - "ios", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "AccessibilityBlockFlowIterator" - ] - } - ] - } - ], "AXRandomizedStressTests": [ { "platforms": [ @@ -645,21 +625,6 @@ ] } ], - "AllowChangingSelectedContent": [ - { - "platforms": [ - "mac" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "AllowChangingSelectedContent" - ] - } - ] - } - ], "AllowDatapipeDrainedAsBytesConsumerInBFCache": [ { "platforms": [ @@ -769,21 +734,6 @@ ] } ], - "AndroidAudioDeviceListener": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "AndroidAudioDeviceListener" - ] - } - ] - } - ], "AndroidBookmarkHistoryPane": [ { "platforms": [ @@ -1377,21 +1327,6 @@ ] } ], - "AndroidUseCorrectWindowBounds": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "AndroidUseCorrectWindowBounds" - ] - } - ] - } - ], "AndroidUseDisplayTopology": [ { "platforms": [ @@ -1557,51 +1492,6 @@ ] } ], - "AnnotatorMode": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "AnnotatorMode" - ] - } - ] - } - ], - "ApiPrintingMarginsAndScale": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ApiPrintingMarginsAndScale" - ] - } - ] - } - ], - "ApnRevamp": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ApnRevamp" - ] - } - ] - } - ], "AppListLaunchRecorder": [ { "platforms": [ @@ -2060,26 +1950,6 @@ ] } ], - "AudioDecoderAudioFileReader": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "AudioDecoderAudioFileReader" - ] - } - ] - } - ], "AudioFocusEnforcementStudy": [ { "platforms": [ @@ -2125,21 +1995,6 @@ ] } ], - "AuthPanelInSession": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "UseAuthPanelInSession" - ] - } - ] - } - ], "AutoDisableAccessibility": [ { "platforms": [ @@ -3624,28 +3479,6 @@ ] } ], - "AvoidTrustedParamsCopies": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "fuchsia", - "ios", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "AvoidTrustedParamsCopies" - ] - } - ] - } - ], "AvoidUnnecessaryForcedLayoutMeasurements": [ { "platforms": [ @@ -3784,28 +3617,6 @@ ] } ], - "BackForwardCacheNotRestoredReasons": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "fuchsia", - "ios", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "BackForwardCacheSendNotRestoredReasons" - ] - } - ] - } - ], "BackForwardCachePauseMicrotasks": [ { "platforms": [ @@ -3872,27 +3683,6 @@ ] } ], - "BackgroundResourceFetch": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "BackgroundResourceFetch", - "ReduceTransferSizeUpdatedIPC" - ] - } - ] - } - ], "BackgroundTabsInterventions": [ { "platforms": [ @@ -3956,21 +3746,6 @@ ] } ], - "BatteryBadgeIconEnabled": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "BatteryBadgeIcon" - ] - } - ] - } - ], "BatterySaverModeAlignWakeUps": [ { "platforms": [ @@ -4216,53 +3991,6 @@ ] } ], - "BocaScreenSharingStudent": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "BocaScreenSharingStudent" - ] - } - ] - } - ], - "BocaScreenSharingTeacher": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "BocaScreenSharingTeacher" - ] - } - ] - } - ], - "BookmarksImportOnFirstRun": [ - { - "platforms": [ - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "BookmarksImportOnFirstRun" - ] - } - ] - } - ], "BookmarksMigrateUiChanges": [ { "platforms": [ @@ -4328,21 +4056,6 @@ ] } ], - "BorealisZinkGlDriver": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "BorealisZinkGlDriver" - ] - } - ] - } - ], "BrowserControlsEarlyResize": [ { "platforms": [ @@ -4667,26 +4380,6 @@ ] } ], - "CSSReadingFlow": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "CSSReadingFlow" - ] - } - ] - } - ], "CacheIdentityListInChrome": [ { "platforms": [ @@ -4702,21 +4395,6 @@ ] } ], - "CacheIsMultiInstanceApi31Enabled": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "CacheIsMultiInstanceApi31Enabled" - ] - } - ] - } - ], "CacheSharingForPervasiveResources": [ { "platforms": [ @@ -4737,21 +4415,6 @@ ] } ], - "CacheStylusSettings": [ - { - "platforms": [ - "android_webview" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "CacheStylusSettings" - ] - } - ] - } - ], "Canvas2DAutoFlushParams": [ { "platforms": [ @@ -4934,24 +4597,6 @@ ] } ], - "ChromeCompose": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "Compose" - ] - } - ] - } - ], "ChromeOSARCVMLmkPerceptibleMinState": [ { "platforms": [ @@ -5878,42 +5523,6 @@ ] } ], - "ChromnientUpdatedFeedbackEntrypoint": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "LensUpdatedFeedbackEntrypoint" - ] - } - ] - } - ], - "ChromnientVideoCitations": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "LensVideoCitations" - ] - } - ] - } - ], "ChromnientZeroStateCsb": [ { "platforms": [ @@ -5980,21 +5589,6 @@ ] } ], - "ClankMostVisitedTilesCustomization": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "MostVisitedTilesCustomization" - ] - } - ] - } - ], "ClankMostVisitedTilesNewScoring": [ { "platforms": [ @@ -6228,25 +5822,6 @@ ] } ], - "ClientSideDetectionSamplePing": [ - { - "platforms": [ - "android", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ClientSideDetectionSamplePing" - ] - } - ] - } - ], "ClientSideDetectionSendLlamaForcedTriggerInfo": [ { "platforms": [ @@ -6340,21 +5915,6 @@ ] } ], - "CloudProfileReporting": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "CloudProfileReporting" - ] - } - ] - } - ], "Collections": [ { "platforms": [ @@ -7030,21 +6590,6 @@ ] } ], - "ControlsVisibilityFromNavigations": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ControlsVisibilityFromNavigations" - ] - } - ] - } - ], "CookieDeprecationFacilitatedTestingCookieDeprecation": [ { "platforms": [ @@ -7098,21 +6643,6 @@ ] } ], - "CopyClientKeysCertsToChaps": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "CopyClientKeysCertsToChaps" - ] - } - ] - } - ], "CpssUseTfliteSignatureRunnerKillSwitch": [ { "platforms": [ @@ -7796,50 +7326,6 @@ ] } ], - "CursiveManagedStylusPreinstall": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "CursiveManagedStylusPreinstall" - ] - } - ] - } - ], - "CustomizableSelect": [ - { - "platforms": [ - "android", - "linux", - "mac", - "windows", - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "CustomizableSelect", - "InputClosesSelect", - "SelectParserRelaxation" - ] - }, - { - "name": "Disabled", - "disable_features": [ - "CustomizableSelect", - "InputClosesSelect", - "SelectParserRelaxation" - ] - } - ] - } - ], "CustomizeChromeExperiments": [ { "platforms": [ @@ -8109,26 +7595,6 @@ ] } ], - "DefaultSiteInstanceGroups": [ - { - "platforms": [ - "android", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "DefaultSiteInstanceGroups" - ] - } - ] - } - ], "DeferSpeculativeRFHCreation": [ { "platforms": [ @@ -8211,36 +7677,6 @@ ] } ], - "DemoModeSignInNonUS": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "DemoModeSignIn" - ] - } - ] - } - ], - "DemoModeSignInUS": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "DemoModeSignIn" - ] - } - ] - } - ], "DeprecateUnload": [ { "platforms": [ @@ -8303,21 +7739,6 @@ ] } ], - "DeskButton": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled_20231025", - "enable_features": [ - "DeskButton" - ] - } - ] - } - ], "DesktopCapturePermissionCheckerPreMacos14_4": [ { "platforms": [ @@ -8372,25 +7793,6 @@ ] } ], - "DesktopNtpMiddleSlotPromoDismissal": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "MiddleSlotPromoDismissal", - "enable_features": [ - "NtpMiddleSlotPromoDismissal" - ] - } - ] - } - ], "DesktopNtpModules": [ { "platforms": [ @@ -8591,35 +7993,6 @@ ] } ], - "DesktopOmniboxShortcutBoost": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "ShortcutBoostGroupWithSearches": "true", - "ShortcutBoostNonTopHitSearchThreshold": "2", - "ShortcutBoostNonTopHitThreshold": "2", - "ShortcutBoostSearchScore": "1414", - "ShortcutBoostUrlScore": "1414", - "omnibox_history_cluster_provider_inherit_search_match_score": "true", - "omnibox_history_cluster_provider_score": "1414" - }, - "enable_features": [ - "JourneysOmniboxHistoryClusterProvider", - "OmniboxShortcutBoost" - ] - } - ] - } - ], "DesktopOmniboxStarterPackExpansion": [ { "platforms": [ @@ -8758,21 +8131,6 @@ ] } ], - "DexFixer": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "DexFixer" - ] - } - ] - } - ], "DiacriticsOnPhysicalKeyboardLongpressOnByDefault": [ { "platforms": [ @@ -8897,21 +8255,6 @@ ] } ], - "DisableGeoLanguageModel": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "DisableGeoLanguageModel" - ] - } - ] - } - ], "DisableNonYUVOverlaysFromExo": [ { "platforms": [ @@ -9063,25 +8406,6 @@ ] } ], - "DlpRegionalizedEndpoints": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "DlpRegionalizedEndpoints" - ] - } - ] - } - ], "DnsHttpsSvcbTimeout": [ { "platforms": [ @@ -9110,28 +8434,6 @@ ] } ], - "DoNotEvictOnAXLocationChange": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "fuchsia", - "ios", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "DoNotEvictOnAXLocationChange" - ] - } - ] - } - ], "DomStorageSqliteInMemory": [ { "platforms": [ @@ -9152,33 +8454,6 @@ ] } ], - "DownloadLater": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "EnabledWithDateTimePicker", - "params": { - "show_date_time_picker": "true" - }, - "enable_features": [ - "DownloadLater" - ] - }, - { - "name": "EnabledWithoutDateTimePicker", - "params": { - "show_date_time_picker": "false" - }, - "enable_features": [ - "DownloadLater" - ] - } - ] - } - ], "DownloadWarningSurvey": [ { "platforms": [ @@ -9992,24 +9267,6 @@ ] } ], - "EnablePolicyPromotionBanner": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "EnablePolicyPromotionBanner" - ] - } - ] - } - ], "EnablePreferencesAccountStorageChromeOS": [ { "platforms": [ @@ -10094,42 +9351,6 @@ ] } ], - "EnableWatermarkCustomization": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "EnableWatermarkCustomization" - ] - } - ] - } - ], - "EnableWatermarkTestPage": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "EnableWatermarkTestPage" - ] - } - ] - } - ], "EnhancedFieldsForSecOps": [ { "platforms": [ @@ -10172,24 +9393,6 @@ ] } ], - "EnterpriseFileObfuscation": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "EnterpriseFileObfuscation" - ] - } - ] - } - ], "EnterpriseFileObfuscationArchiveAnalyzer": [ { "platforms": [ @@ -10359,24 +9562,6 @@ ] } ], - "ExtensionManifestV2Deprecation": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled_MV2ExtensionsUnsupported", - "enable_features": [ - "ExtensionManifestV2Unsupported" - ] - } - ] - } - ], "ExtensionsToolbarAndMenuRedesign": [ { "platforms": [ @@ -10449,21 +9634,6 @@ ] } ], - "ExternalDisplayEventTelemetry": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ExternalDisplayEventTelemetry" - ] - } - ] - } - ], "ExternalHDR10": [ { "platforms": [ @@ -10578,21 +9748,6 @@ ] } ], - "FastPairAdvertisingFormat2025": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "FastPairAdvertisingFormat2025" - ] - } - ] - } - ], "FastPairHandshakeLongTermRefactor": [ { "platforms": [ @@ -11068,72 +10223,6 @@ ] } ], - "ForceOffTextAutosizing": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ForceOffTextAutosizing" - ] - } - ] - } - ], - "ForceOffTextAutosizingWebView": [ - { - "platforms": [ - "android_webview" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ForceOffTextAutosizing" - ] - } - ] - } - ], - "ForestFeature": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ForestFeature" - ] - } - ] - } - ], - "FrameRoutingCache": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "FrameRoutingCache" - ] - } - ] - } - ], "FreezeSharedWorker": [ { "platforms": [ @@ -11299,22 +10388,6 @@ ] } ], - "Glic1PTools": [ - { - "platforms": [ - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "GlicExtensions" - ] - } - ] - } - ], "GlicActorObservationDelayExcludeAdFrameLoading": [ { "platforms": [ @@ -11552,21 +10625,6 @@ ] } ], - "GlicExplicitBackgroundColor": [ - { - "platforms": [ - "windows" - ], - "experiments": [ - { - "name": "Enabled_Dogfood", - "enable_features": [ - "GlicExplicitBackgroundColor" - ] - } - ] - } - ], "GlicForceNonSkSLBorder": [ { "platforms": [ @@ -11601,22 +10659,6 @@ ] } ], - "GlicFreWarming": [ - { - "platforms": [ - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "GlicFreWarming" - ] - } - ] - } - ], "GlicGA": [ { "platforms": [ @@ -11810,23 +10852,6 @@ ] } ], - "GlicTieredRollout": [ - { - "platforms": [ - "mac", - "windows", - "linux" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "GlicTieredRollout" - ] - } - ] - } - ], "GlicTrustFirstOnboarding": [ { "platforms": [ @@ -12001,115 +11026,6 @@ ] } ], - "GoogleLensDesktopImageFormatOptimizations": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "WebpQualityBackendV6", - "params": { - "dismiss-loading-state-on-document-on-load-completed-in-primary-main-frame": "false", - "dismiss-loading-state-on-navigation-entry-committed": "true", - "encoding-quality-image-search": "45", - "encoding-quality-region-search": "45", - "lens-homepage-url": "https://lens.google.com/v3/", - "lens-html-redirect-fix": "false", - "use-jpeg-for-image-search": "false", - "use-webp-for-image-search": "true" - }, - "enable_features": [ - "LensImageFormatOptimizations", - "LensStandalone" - ] - }, - { - "name": "WebpBackendOnlyV6", - "params": { - "encoding-quality-image-search": "90", - "encoding-quality-region-search": "90", - "lens-homepage-url": "https://lens.google.com/v3/", - "lens-html-redirect-fix": "false", - "use-jpeg-for-image-search": "false", - "use-webp-for-image-search": "true" - }, - "enable_features": [ - "LensImageFormatOptimizations", - "LensStandalone" - ] - }, - { - "name": "JpegQualityBackendV6", - "params": { - "dismiss-loading-state-on-document-on-load-completed-in-primary-main-frame": "false", - "dismiss-loading-state-on-navigation-entry-committed": "true", - "encoding-quality-image-search": "40", - "encoding-quality-region-search": "40", - "lens-homepage-url": "https://lens.google.com/v3/", - "lens-html-redirect-fix": "false", - "use-jpeg-for-image-search": "true", - "use-webp-for-image-search": "false" - }, - "enable_features": [ - "LensImageFormatOptimizations", - "LensStandalone" - ] - }, - { - "name": "JpegBackendOnlyV6", - "params": { - "encoding-quality-image-search": "90", - "encoding-quality-region-search": "90", - "lens-homepage-url": "https://lens.google.com/v3/", - "lens-html-redirect-fix": "false", - "use-jpeg-for-image-search": "true", - "use-webp-for-image-search": "false" - }, - "enable_features": [ - "LensImageFormatOptimizations", - "LensStandalone" - ] - }, - { - "name": "JpegQualityOnlyV6", - "params": { - "encoding-quality-image-search": "40", - "encoding-quality-region-search": "40", - "use-jpeg-for-image-search": "true", - "use-webp-for-image-search": "false" - }, - "enable_features": [ - "LensImageFormatOptimizations" - ] - } - ] - } - ], - "GoogleLensTabletIntegration": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled_110422", - "params": { - "enableCameraAssistedSearchOnTablet": "true", - "enableCameraAssistedSearchOnTabletWidget": "true", - "enableContextMenuSearchOnTablet": "true" - }, - "enable_features": [ - "LensCameraAssistedSearch", - "LensOnQuickActionSearchWidget" - ] - } - ] - } - ], "GpuPersistentCache": [ { "platforms": [ @@ -12232,49 +11148,6 @@ ] } ], - "HTMLParserYieldByUserTiming": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "EnableWithTimeout5ms", - "params": { - "timeout_ms": "5" - }, - "enable_features": [ - "SearchAboveFoldBytesReceived" - ] - } - ] - } - ], - "HTTP2": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "fuchsia", - "ios", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled6", - "params": { - "http2_grease_settings": "true" - }, - "enable_features": [ - "Http2Grease" - ] - } - ] - } - ], "HangoutsExtensionV3": [ { "platforms": [ @@ -12935,27 +11808,6 @@ ] } ], - "Hotspot": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled_Dogfood", - "enable_features": [ - "Hotspot" - ] - }, - { - "name": "Enabled", - "enable_features": [ - "Hotspot" - ] - } - ] - } - ], "HttpCacheInitializeDiskCacheBackendEarly": [ { "platforms": [ @@ -13141,22 +11993,6 @@ ] } ], - "IOSChromnientEscapeHatch": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "IPH_iOSLensOverlayEscapeHatchTip", - "LensOverlayEnableLVFEscapeHatch" - ] - } - ] - } - ], "IOSCloseOtherTabs": [ { "platforms": [ @@ -13448,36 +12284,6 @@ ] } ], - "IOSFeedCUI1Refinements": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "EnableCUI1Refinements" - ] - } - ] - } - ], - "IOSFeedCUI3NextPageAppFlow": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "EnableCUI3NextPageAppFlow" - ] - } - ] - } - ], "IOSFullscreenScrollThreshold": [ { "platforms": [ @@ -13736,21 +12542,6 @@ ] } ], - "IOSMemoryAblation": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "IOSMemoryAblation" - ] - } - ] - } - ], "IOSMostVisitedTilesCustomization": [ { "platforms": [ @@ -13781,21 +12572,6 @@ ] } ], - "IOSOmahaResyncTimerOnForeground": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "OmahaResyncTimerOnForeground" - ] - } - ] - } - ], "IOSOneTapMiniMapRestrictions": [ { "platforms": [ @@ -13900,21 +12676,6 @@ ] } ], - "IOSPasskeyShim": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "IOSPasskeyShim" - ] - } - ] - } - ], "IOSProactivePasswordGenerationBottomSheet": [ { "platforms": [ @@ -14018,24 +12779,6 @@ ] } ], - "IOSStart4Hour": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled4Hour", - "params": { - "ReturnToStartSurfaceInactiveDurationInSeconds": "14400" - }, - "enable_features": [ - "StartSurface" - ] - } - ] - } - ], "IOSStatelessFormSuggestionController": [ { "platforms": [ @@ -14071,22 +12814,6 @@ ] } ], - "IOSTFLiteLanguageDetection": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled_20220429", - "enable_features": [ - "DownloadServiceForegroundSessionIOSFeature", - "TFLiteLanguageDetectionEnabled" - ] - } - ] - } - ], "IOSTabGridDragAndDrop": [ { "platforms": [ @@ -14606,24 +13333,6 @@ ] } ], - "InlineFullscreenPerfExperiment": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "InlineFullscreenPerfExperiment" - ] - } - ] - } - ], "InputVizard": [ { "platforms": [ @@ -14716,22 +13425,6 @@ ] } ], - "IsolatedWebApps": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "IsolatedWebAppAutomaticUpdates", - "IsolatedWebApps" - ] - } - ] - } - ], "IsolatesPriorityUseProcessPriority": [ { "platforms": [ @@ -15260,26 +13953,6 @@ ] } ], - "LazyUpdateTranslateModel": [ - { - "platforms": [ - "android", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "LazyUpdateTranslateModel" - ] - } - ] - } - ], "LegacyKeyRepeatSynthesis": [ { "platforms": [ @@ -15788,26 +14461,6 @@ ] } ], - "MediaDeviceIdStoragePartitioning": [ - { - "platforms": [ - "android", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "MediaDeviceIdPartitioning" - ] - } - ] - } - ], "MediaFoundationBatchRead": [ { "platforms": [ @@ -15998,63 +14651,6 @@ ] } ], - "MemoryReducerGcCount": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "compaction_max_evacuated_bytes_mb_for_reduce_memory": "36", - "compaction_target_fragmentation_percent_for_reduce_memory": "20", - "memory_reducer_gc_count": "1" - }, - "enable_features": [ - "V8Flag_memory_reducer" - ] - } - ] - } - ], - "MessagesPreinstall": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "MessagesPreinstall" - ] - } - ] - } - ], - "MetricsClusteringSurvey": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled_CANARY_DEV_50_20240918", - "enable_features": [ - "SegmentationPlatformMetricsClustering", - "SegmentationSurveyPage" - ] - } - ] - } - ], "MetricsDataQuality": [ { "platforms": [ @@ -16108,44 +14704,6 @@ ] } ], - "MigrateDefaultChromeAppToWebAppsGSuite": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled_20210111", - "enable_features": [ - "MigrateDefaultChromeAppToWebAppsGSuite" - ] - } - ] - } - ], - "MigrateDefaultChromeAppToWebAppsNonGSuite": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled_20210111", - "enable_features": [ - "MigrateDefaultChromeAppToWebAppsNonGSuite" - ] - } - ] - } - ], "MigrateIOSKeychainAccessibility": [ { "platforms": [ @@ -16320,21 +14878,6 @@ ] } ], - "MultiCalendarSupport": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "MultiCalendarSupport" - ] - } - ] - } - ], "MultipleSpareRPHs": [ { "platforms": [ @@ -16371,21 +14914,6 @@ ] } ], - "NTPBackgroundCustomization": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "NTPBackgroundCustomization" - ] - } - ] - } - ], "NTPBackgroundImageCache": [ { "platforms": [ @@ -16401,25 +14929,6 @@ ] } ], - "NTPPopularSites": [ - { - "platforms": [ - "android", - "ios" - ], - "experiments": [ - { - "name": "Version7", - "params": { - "version": "7" - }, - "enable_features": [ - "NTPPopularSites" - ] - } - ] - } - ], "NavigationThrottleRegistryAttributeCache": [ { "platforms": [ @@ -16439,36 +14948,6 @@ ] } ], - "NearbyBleV2": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "EnableNearbyBleV2" - ] - } - ] - } - ], - "NearbyShareNameEnabled": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "IsNameEnabled" - ] - } - ] - } - ], "NearbySharingRemoveRestrictToContacts": [ { "platforms": [ @@ -16484,21 +14963,6 @@ ] } ], - "NearbySharingWifiDirect": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "NearbySharingWifiDirect" - ] - } - ] - } - ], "NetworkQualityEstimatorAsync": [ { "platforms": [ @@ -16894,21 +15358,6 @@ ] } ], - "NotificationLimit": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "NotificationLimit" - ] - } - ] - } - ], "NotificationTelemetrySwb": [ { "platforms": [ @@ -16948,21 +15397,6 @@ ] } ], - "NotificationWidthIncrease": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "NotificationWidthIncrease" - ] - } - ] - } - ], "NotificationsIgnoreRequireInteraction": [ { "platforms": [ @@ -17170,21 +15604,6 @@ ] } ], - "OfferPinToTaskbarInSettings": [ - { - "platforms": [ - "windows" - ], - "experiments": [ - { - "name": "MostlyLaunched", - "enable_features": [ - "OfferPinToTaskbarInSettings" - ] - } - ] - } - ], "OfferPinToTaskbarWhenSettingDefault": [ { "platforms": [ @@ -17238,23 +15657,6 @@ ] } ], - "OidcAuthProfileManagement": [ - { - "platforms": [ - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "OidcAuthProfileManagement" - ] - } - ] - } - ], "OmahaMinSdkVersionAndroid": [ { "platforms": [ @@ -17327,21 +15729,6 @@ }, { "platforms": [ - "android" - ], - "experiments": [ - { - "name": "AndroidExperiments", - "enable_features": [ - "OmniboxDynamicMaxAutocomplete", - "OmniboxMaxURLMatches", - "OmniboxUIExperimentMaxAutocompleteMatches" - ] - } - ] - }, - { - "platforms": [ "ios" ], "experiments": [ @@ -17368,27 +15755,6 @@ ] } ], - "OmniboxInspireMePhase2": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "EnabledCarouselAboveTrends", - "params": { - "QueryTilesShowAboveTrends": "true", - "QueryTilesShowAsCarousel": "false", - "experiment_tag": "{maxLevels: 1, rankTiles: true}" - }, - "enable_features": [ - "OmniboxQueryTilesInZPSOnNTP", - "QueryTiles" - ] - } - ] - } - ], "OmniboxLogURLScoringSignals": [ { "platforms": [ @@ -17581,21 +15947,6 @@ ] } ], - "OnDeviceStorage": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "OnDeviceStorage" - ] - } - ] - } - ], "OnTaskStatusCheck": [ { "platforms": [ @@ -17614,21 +15965,6 @@ ] } ], - "OngoingProcesses": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "OngoingProcesses" - ] - } - ] - } - ], "OptGuideBatchSRPTuning": [ { "platforms": [ @@ -17675,23 +16011,6 @@ ] } ], - "OptimizationGuideOnDeviceModelCpuBackend": [ - { - "platforms": [ - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "OnDeviceModelCpuBackend" - ] - } - ] - } - ], "OptimizeAssociateWindowsAndroid": [ { "platforms": [ @@ -17865,44 +16184,6 @@ ] } ], - "OzonePlatformAuto": [ - { - "platforms": [ - "linux" - ], - "experiments": [ - { - "name": "Enabled_Dogfood", - "enable_features": [ - "AcceleratedVideoDecodeLinuxGL", - "AcceleratedVideoDecodeLinuxZeroCopyGL", - "OverrideDefaultOzonePlatformHintToAuto" - ] - }, - { - "name": "Default", - "disable_features": [ - "OverrideDefaultOzonePlatformHintToAuto" - ] - } - ] - } - ], - "PWAIconAndTitleInNativeNotificationsWin": [ - { - "platforms": [ - "windows" - ], - "experiments": [ - { - "name": "EnablePWAIconAndTitleInNativeNotificationsWin", - "enable_features": [ - "AppSpecificNotifications" - ] - } - ] - } - ], "PageActionsMigration": [ { "platforms": [ @@ -18011,27 +16292,6 @@ ] } ], - "ParkableStringsLessAggressiveAndZstd": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "LessAggressiveParkableString", - "UseZstdForParkableStrings" - ] - } - ] - } - ], "PartialPageZeroing": [ { "platforms": [ @@ -18901,25 +17161,6 @@ ] } ], - "PermissionsAIP92": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows", - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "PermissionsAIP92" - ] - } - ] - } - ], "PermissionsAIv4": [ { "platforms": [ @@ -19015,22 +17256,6 @@ ] } ], - "PickerUI": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ModifierSplit", - "Picker" - ] - } - ] - } - ], "PinTimeoutMigration": [ { "platforms": [ @@ -19094,21 +17319,6 @@ ] } ], - "PlayReadyHardwareSecureDecryption": [ - { - "platforms": [ - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "HardwareSecureDecryption" - ] - } - ] - } - ], "PlusAddressAcceptedFirstTimeCreateSurvey": [ { "platforms": [ @@ -19339,30 +17549,6 @@ ] } ], - "PompanoEnabled": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "EnabledeWithSummarizeSelected", - "enable_features": [ - "MahiPanelResizable", - "MahiSummarizeSelected", - "Pompano" - ] - }, - { - "name": "EnabledWithoutSummarizeSelected", - "enable_features": [ - "MahiPanelResizable", - "Pompano" - ] - } - ] - } - ], "PostDelayedTaskFocusTab": [ { "platforms": [ @@ -19550,24 +17736,6 @@ ] } ], - "PrefetchProxyDesktop": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "PrefetchProxy" - ] - } - ] - } - ], "PreloadTopChromeWebUILessNavigations": [ { "platforms": [ @@ -19845,38 +18013,6 @@ ] } ], - "PrivacySandboxAdsAPIs": [ - { - "platforms": [ - "android", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled_Notice_M1_AllAPIs_Expanded_NoOT_Stable", - "params": { - "implementation_type": "mparch" - }, - "enable_features": [ - "AllowURNsInIframes", - "BrowsingTopics", - "FencedFrames", - "FencedFramesAPIChanges", - "InterestGroupStorage", - "KAnonymityService", - "PrivacySandboxAdsAPIs", - "PrivacySandboxAdsAPIsM1Override", - "PrivateAggregationApi", - "SharedStorageAPI" - ] - } - ] - } - ], "PrivacySandboxInternalsDevUI": [ { "platforms": [ @@ -20139,42 +18275,6 @@ ] } ], - "ProfileRemoteCommands": [ - { - "platforms": [ - "android", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "UserRemoteCommands", - "UserRemoteCommandsInvalidationWithDirectMessagesEnabled" - ] - } - ] - } - ], - "ProfileSignalsReportingEnabled": [ - { - "platforms": [ - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ProfileSignalsReportingEnabled" - ] - } - ] - } - ], "ProfilesReordering": [ { "platforms": [ @@ -20212,38 +18312,6 @@ ] } ], - "PropagateWebViewNetworkingSignals": [ - { - "platforms": [ - "android_webview" - ], - "experiments": [ - { - "name": "SignalAndConnecMigration_20230706", - "enable_features": [ - "MigrateSessionsOnNetworkChangeV2", - "webViewPropagateNetworkSignals" - ] - }, - { - "name": "Control_20230706", - "disable_features": [ - "MigrateSessionsOnNetworkChangeV2", - "webViewPropagateNetworkSignals" - ] - }, - { - "name": "SignalNoConnecMigration_20230706", - "enable_features": [ - "webViewPropagateNetworkSignals" - ], - "disable_features": [ - "MigrateSessionsOnNetworkChangeV2" - ] - } - ] - } - ], "ProtoBasedEnterpriseReporting": [ { "platforms": [ @@ -20359,31 +18427,6 @@ ] } ], - "QUIC": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "EnabledNoId", - "params": { - "channel": "D", - "epoch": "20250423", - "retransmittable_on_wire_timeout_milliseconds": "200" - }, - "enable_features": [ - "QuicDoesNotUseFeatures" - ] - } - ] - } - ], "QuicLongerIdleConnectionTimeout": [ { "platforms": [ @@ -20404,45 +18447,6 @@ ] } ], - "QuickShareV2": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled_Dogfood", - "enable_features": [ - "QuickShareV2" - ] - }, - { - "name": "Enabled", - "enable_features": [ - "QuickShareV2" - ] - } - ] - } - ], - "RawDrawAndDrDc": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "DrDc_vulkan", - "params": { - "BlockListByDevice": "amber|chopin|secret|a03|SO-51B|on7xelte|j7xelte|F41B|doha|HWYAL|a20s|begonia|b2q|t2s|channel|galahad|rk322x_box|a32|ellis|dandelion|tonga|RMX3231" - }, - "disable_features": [ - "RawDraw" - ] - } - ] - } - ], "RazeOldHistoryDatabase": [ { "platforms": [ @@ -20707,21 +18711,6 @@ ] } ], - "ReduceCpuUtilization2": [ - { - "platforms": [ - "android_webview" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ReduceCpuUtilization2" - ] - } - ] - } - ], "ReduceIPCCombined": [ { "platforms": [ @@ -20777,21 +18766,6 @@ ] } ], - "RegisterOsUpdateHandlerWin": [ - { - "platforms": [ - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "RegisterOsUpdateHandlerWin" - ] - } - ] - } - ], "RejectWeakCiphertext": [ { "platforms": [ @@ -20807,69 +18781,6 @@ ] } ], - "RemotePageMetadataAndroid": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled_20240514", - "params": { - "supported_countries": "*", - "supported_locales": "*" - }, - "enable_features": [ - "PageContentAnnotationsPersistSalientImageMetadata", - "RemotePageMetadata" - ] - } - ] - } - ], - "RemotePageMetadataBling": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled_20240514", - "params": { - "supported_countries": "*", - "supported_locales": "*" - }, - "enable_features": [ - "PageContentAnnotationsPersistSalientImageMetadata", - "RemotePageMetadata" - ] - } - ] - } - ], - "RemotePageMetadataDesktopExpansion": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled_20240514", - "params": { - "supported_countries": "*", - "supported_locales": "*" - }, - "enable_features": [ - "PageContentAnnotationsPersistSalientImageMetadata", - "RemotePageMetadata" - ] - } - ] - } - ], "RemoveCachedProcessFromBindingManager": [ { "platforms": [ @@ -20919,19 +18830,16 @@ ] } ], - "RemoveRendererProcessLimit": [ + "RemoveGreySnapshot": [ { "platforms": [ - "chromeos", - "linux", - "mac", - "windows" + "ios" ], "experiments": [ { "name": "Enabled", "enable_features": [ - "RemoveRendererProcessLimit" + "RemoveGreySnapshot" ] } ] @@ -21154,24 +19062,6 @@ ] } ], - "ResolutionBasedDecoderPriority": [ - { - "platforms": [ - "linux", - "mac", - "windows", - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ResolutionBasedDecoderPriority" - ] - } - ] - } - ], "RestrictAbusePorts": [ { "platforms": [ @@ -21303,21 +19193,6 @@ ] } ], - "RunDefaultStatusCheck": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "RunDefaultStatusCheck" - ] - } - ] - } - ], "RustyBmpFeature": [ { "platforms": [ @@ -21545,26 +19420,6 @@ ] } ], - "SavedTabGroupUrlRestriction": [ - { - "platforms": [ - "android", - "chromeos", - "ios", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "EnableUrlRestriction" - ] - } - ] - } - ], "ScaleScrollbarAnimationTiming": [ { "platforms": [ @@ -21708,51 +19563,6 @@ ] } ], - "SchoolToolsSpotlight": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "BocaSpotlight" - ] - } - ] - } - ], - "SchoolToolsSpotlightRobotRequester": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "BocaSpotlightRobotRequester" - ] - } - ] - } - ], - "ScreenCaptureKitMacScreen": [ - { - "platforms": [ - "mac" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ScreenCaptureKitMacScreen" - ] - } - ] - } - ], "ScreencastForceEnableServerSideSpeechRecognition": [ { "platforms": [ @@ -21865,57 +19675,6 @@ ] } ], - "SeaPenQueryRewrite": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SeaPenQueryRewrite" - ] - } - ] - } - ], - "SeaPenTextInput": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SeaPenTextInput" - ] - } - ] - } - ], - "SeaPenTextInputI18n": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SeaPenTextInputTranslation" - ] - }, - { - "name": "Enabled_Dogfood", - "enable_features": [ - "SeaPenTextInputTranslation" - ] - } - ] - } - ], "SeaPenUseExptTemplate": [ { "platforms": [ @@ -21931,26 +19690,6 @@ ] } ], - "SeamlessRenderFrameSwap": [ - { - "platforms": [ - "android", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SeamlessRenderFrameSwap" - ] - } - ] - } - ], "SeamlessSigninClank": [ { "platforms": [ @@ -22168,21 +19907,6 @@ ] } ], - "SelectFileOpenDocument": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SelectFileOpenDocument" - ] - } - ] - } - ], "SelectivePermissionsIntervention": [ { "platforms": [ @@ -22626,21 +20350,6 @@ ] } ], - "ShareDefaultBrowserStatus": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ShareDefaultBrowserStatus" - ] - } - ] - } - ], "SharedHighlightingIphClank": [ { "platforms": [ @@ -22750,24 +20459,6 @@ ] } ], - "SharedWorkerBlobURLFix": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SharedWorkerBlobURLFix" - ] - } - ] - } - ], "SharingHubDesktopScreenshots": [ { "platforms": [ @@ -22856,80 +20547,6 @@ ] } ], - "SidePanelCompanionDesktopM116Plus": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "EnableCompanionChromeOS_20240222", - "params": { - "open-companion-for-image-search": "false", - "open-companion-for-web-search": "false", - "open-contextual-lens-panel": "false", - "open-links-in-current-tab": "false" - }, - "enable_features": [ - "SidePanelCompanion", - "SidePanelCompanionChromeOS", - "VisualQuerySuggestions" - ], - "disable_features": [ - "SideSearch" - ] - } - ] - } - ], - "SidePanelFlyoverAnimation": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SidePanelFlyoverAnimation" - ] - } - ] - } - ], - "SidePanelPinningWithResponsiveToolbar": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "EnabledWithSidePanelPinning", - "enable_features": [ - "IPH_SidePanelGenericPinnableFeature", - "ResponsiveToolbar", - "SidePanelPinning" - ] - }, - { - "name": "EnabledWithoutSidePanelPinning", - "enable_features": [ - "ResponsiveToolbar" - ] - } - ] - } - ], "SigninPromoLimitsExperiment": [ { "platforms": [ @@ -23091,83 +20708,6 @@ ] } ], - "SimpleCachePrioritizedCaching": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SimpleCachePrioritizedCaching" - ] - } - ] - } - ], - "SingleCaCertVerificationPhase1": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SingleCaCertVerificationPhase1" - ] - } - ] - } - ], - "SingleCaCertVerificationPhase2": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SingleCaCertVerificationPhase2" - ] - } - ] - } - ], - "SingleNTPRemoveExtraNTPs": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled_SingleNTP_RemoveExtraNTP", - "enable_features": [ - "RemoveExcessNTPs", - "SingleNTP" - ] - }, - { - "name": "Enabled_SingleNTP", - "enable_features": [ - "SingleNTP" - ] - }, - { - "name": "Enabled_RemoveExtraNTP", - "enable_features": [ - "RemoveExcessNTPs" - ] - } - ] - } - ], "SingleVideoFrameRateThrottling": [ { "platforms": [ @@ -23187,27 +20727,6 @@ ] } ], - "SiteInstanceGroupsForDataUrls": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "SiteInstanceGroupsForDataUrls" - ] - } - ] - } - ], "SkiaGraphite": [ { "platforms": [ @@ -23377,36 +20896,6 @@ ] } ], - "SkyVaultGA": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled_SkyVaultV2", - "enable_features": [ - "SkyVaultV2" - ] - } - ] - } - ], - "SkyVaultTTBeta": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled_SkyVault", - "enable_features": [ - "SkyVault" - ] - } - ] - } - ], "SlopBucket": [ { "platforms": [ @@ -23754,24 +21243,6 @@ ] } ], - "StringWidthCache": [ - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "StringWidthCache" - ] - } - ] - } - ], "StructuredCloningForMessaging": [ { "platforms": [ @@ -24038,63 +21509,6 @@ ] } ], - "SysUiHoldbackStudy": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Default", - "disable_features": [ - "SysUiShouldHoldbackGifRecording", - "SysUiShouldHoldbackTaskManagement" - ] - }, - { - "name": "SysUiHoldbackStudy", - "enable_features": [ - "SysUiShouldHoldbackGifRecording", - "SysUiShouldHoldbackTaskManagement" - ] - }, - { - "name": "NoFirstPartyIntegrations", - "enable_features": [ - "SysUiShouldHoldbackTaskManagement" - ], - "disable_features": [ - "SysUiShouldHoldbackGifRecording" - ] - } - ] - } - ], - "SysUiHoldbackStudyM129": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Default", - "disable_features": [ - "SysUiShouldHoldbackDriveIntegration", - "SysUiShouldHoldbackFocusMode", - "SysUiShouldHoldbackForest" - ] - }, - { - "name": "EnrolledInHoldback", - "enable_features": [ - "SysUiShouldHoldbackDriveIntegration", - "SysUiShouldHoldbackFocusMode", - "SysUiShouldHoldbackForest" - ] - } - ] - } - ], "SystemEmojiPickerGIFSupportByDefault": [ { "platforms": [ @@ -24153,32 +21567,6 @@ ] } ], - "TabAudioMuting": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "availability": "any", - "event_trigger": "name:tab_audio_muting_iph_triggered;comparator:==0;window:120;storage:365", - "event_used": "name:tab_audio_muting_toggle_viewed;comparator:==0;window:120;storage:365", - "session_rate": "==0" - }, - "enable_features": [ - "IPH_TabAudioMuting", - "TabAudioMuting" - ] - } - ] - } - ], "TabGroupInteractionsDesktop": [ { "platforms": [ @@ -24262,46 +21650,6 @@ ] } ], - "TabModelInitFixes": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "TabModelInitFixes" - ] - } - ] - } - ], - "TabSearchInProductHelp": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "TabSearchIPH", - "params": { - "availability": "any", - "event_trigger": "name:tab_search_iph_tgr;comparator:==0;window:90;storage:360", - "event_used": "name:tab_search_opened;comparator:==0;window:90;storage:360", - "session_rate": "<3" - }, - "enable_features": [ - "IPH_TabSearch" - ] - } - ] - } - ], "TabStorageSqlitePrototype": [ { "platforms": [ @@ -24336,40 +21684,6 @@ ] } ], - "TailoredSecurityIntegration": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "TailoredSecurityIntegration", - "enable_features": [ - "TailoredSecurityIntegration" - ] - } - ] - } - ], - "TailoredSecurityIntegrationAndroid": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled_20220302", - "enable_features": [ - "TailoredSecurityIntegration" - ] - } - ] - } - ], "TaiyakiAllUsers": [ { "platforms": [ @@ -24823,29 +22137,6 @@ ] } ], - "TrustTokenOriginTrial": [ - { - "platforms": [ - "android", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "TrustTokenOperationsRequiringOriginTrial": "all-operations-require-origin-trial" - }, - "enable_features": [ - "TrustTokens" - ] - } - ] - } - ], "UMA-Pseudo-Metrics-Effect-Injection-25-Percent": [ { "platforms": [ @@ -25079,21 +22370,6 @@ ] } ], - "UploadOfficeToCloud": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "UploadOfficeToCloud" - ] - } - ] - } - ], "UseBoringSSLForRandBytes": [ { "platforms": [ @@ -25180,21 +22456,6 @@ ] } ], - "UseEncryptedReportingPipelineToReportArcAppInstallEvents": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "UseEncryptedReportingPipelineToReportArcAppInstallEvents" - ] - } - ] - } - ], "UseHeuristicForFindingEditor": [ { "platforms": [ @@ -25421,153 +22682,6 @@ ] } ], - "V8BytecodeVerification": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "V8Flag_verify_bytecode_light" - ] - } - ] - } - ], - "V8CodeFlushing": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Time180", - "params": { - "bytecode_old_time": "180" - }, - "enable_features": [ - "V8Flag_flush_code_based_on_time", - "V8Flag_ineffective_gcs_forces_last_resort" - ] - } - ] - } - ], - "V8DisableEagerAllocationFailures": [ - { - "platforms": [ - "android", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Disabled", - "disable_features": [ - "V8Flag_enable_allocation_failures_optimize_memory", - "V8Flag_enable_allocation_failures_optimize_memory_ignoring_priority" - ] - } - ] - } - ], - "V8DisableScavengerUpdatesAllocationLimit": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "disable_features": [ - "V8Flag_scavenger_updates_allocation_limit" - ] - } - ] - } - ], - "V8EnforceGlobalHeapLimit": [ - { - "platforms": [ - "android", - "android_webview" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "maximum_global_heap_limit_factor": "2" - }, - "enable_features": [ - "V8Flag_enforce_global_heap_limit" - ] - } - ] - }, - { - "platforms": [ - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "maximum_global_heap_limit_factor": "8" - }, - "enable_features": [ - "V8Flag_enforce_global_heap_limit" - ] - } - ] - } - ], - "V8ExternalMemoryAccountedInGlobalLimit": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "external_memory_max_growing_factor": "1.3" - }, - "enable_features": [ - "V8Flag_external_memory_accounted_in_global_limit" - ] - } - ] - } - ], "V8HighEndAndroid": [ { "platforms": [ @@ -25587,71 +22701,6 @@ ] } ], - "V8IneffectiveMarkCompact": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "HighSizeThreshold", - "params": { - "ineffective_gc_size_threshold": "0.95" - }, - "enable_features": [ - "V8Flag_detect_ineffective_gcs_near_heap_limit" - ] - } - ] - } - ], - "V8LargePagePool": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "V8Flag_large_page_pool" - ] - } - ] - } - ], - "V8LateHeapLimitCheck": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "V8Flag_late_heap_limit_check" - ] - } - ] - } - ], "V8ManagedZoneMemory": [ { "platforms": [ @@ -25736,43 +22785,6 @@ ] } ], - "V8NoTrimDescriptorArray": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "NoTrimWithStack", - "enable_features": [ - "V8Flag_trim_descriptor_arrays_in_gc" - ], - "disable_features": [ - "V8Flag_trim_descriptor_arrays_in_gc_with_stack" - ] - }, - { - "name": "NoTrim", - "disable_features": [ - "V8Flag_trim_descriptor_arrays_in_gc", - "V8Flag_trim_descriptor_arrays_in_gc_with_stack" - ] - }, - { - "name": "Default", - "enable_features": [ - "V8Flag_trim_descriptor_arrays_in_gc", - "V8Flag_trim_descriptor_arrays_in_gc_with_stack" - ] - } - ] - } - ], "V8ParserAblation": [ { "platforms": [ @@ -26161,21 +23173,6 @@ ] } ], - "VideoConferenceDLCUIRollout": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "VcDlcUi" - ] - } - ] - } - ], "VideoConferenceRollout": [ { "platforms": [ @@ -26260,21 +23257,6 @@ ] } ], - "VulkanV2": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "VulkanV2", - "enable_features": [ - "VulkanV2" - ] - } - ] - } - ], "WebApkBackupAndRestore": [ { "platforms": [ @@ -26465,25 +23447,6 @@ ] } ], - "WebProtectDlpScanPastedImages": [ - { - "platforms": [ - "chromeos", - "fuchsia", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "DlpScanPastedImages" - ] - } - ] - } - ], "WebRTC-Aec3BufferingMaxAllowedExcessRenderBlocksOverride": [ { "platforms": [ @@ -26711,26 +23674,6 @@ ] } ], - "WebRTC-DataChannelMessageInterleaving": [ - { - "platforms": [ - "android", - "android_webview", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "WebRTC-DataChannelMessageInterleaving" - ] - } - ] - } - ], "WebRTC-EncoderSpeed": [ { "platforms": [ @@ -26975,49 +23918,6 @@ ] } ], - "WebRTC-Video-H26xPacketBuffer": [ - { - "platforms": [ - "android", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "WebRTC-Video-H26xPacketBuffer" - ] - } - ] - } - ], - "WebRTC-Video-ReceiveAndSendH265": [ - { - "platforms": [ - "android", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled_SendReceive", - "enable_features": [ - "WebRtcAllowH265Receive", - "WebRtcAllowH265Send" - ] - }, - { - "name": "Enabled_ReceiveOnly", - "enable_features": [ - "WebRtcAllowH265Receive" - ] - } - ] - } - ], "WebRTC-Vp9ExternalRefCtrl": [ { "platforms": [ @@ -27722,21 +24622,6 @@ ] } ], - "WebXrEnableCardboard": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "Cardboard" - ] - } - ] - } - ], "WebrtcEncodeReadbackOptimization": [ { "platforms": [ @@ -27915,21 +24800,6 @@ ] } ], - "WindowsSystemTracing": [ - { - "platforms": [ - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "WindowsSystemTracing" - ] - } - ] - } - ], "XMLParsingRustNonXslt": [ { "platforms": [ @@ -28089,26 +24959,6 @@ ] } ], - "ZeroStateSuggestionsV2": [ - { - "platforms": [ - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "ZSSMaxPinnedPagesForTriggeringSuggestions": "10" - }, - "enable_features": [ - "ZeroStateSuggestionsV2" - ] - } - ] - } - ], "ZstdForCrossSiteSpeculationRulesPrefetch": [ { "platforms": [
diff --git a/third_party/blink/renderer/core/html/forms/form_mcp_schema.cc b/third_party/blink/renderer/core/html/forms/form_mcp_schema.cc index 293a717..d541730 100644 --- a/third_party/blink/renderer/core/html/forms/form_mcp_schema.cc +++ b/third_party/blink/renderer/core/html/forms/form_mcp_schema.cc
@@ -889,10 +889,12 @@ } if (auto* input = DynamicTo<HTMLInputElement>( controls_for_name.front()->ToHTMLElement())) { - input->SetValue(string); + input->SetValue(string, + TextFieldEventBehavior::kDispatchInputAndChangeEvent); } else if (auto* textarea = DynamicTo<HTMLTextAreaElement>( controls_for_name.front()->ToHTMLElement())) { - textarea->SetValue(string); + textarea->SetValue(string, + TextFieldEventBehavior::kDispatchInputAndChangeEvent); } } @@ -903,7 +905,8 @@ String number_string; bool success = ToString(value, number_string); CHECK(success) << "ValidateNumberData should be called first"; - input->SetValue(number_string); + input->SetValue(number_string, + TextFieldEventBehavior::kDispatchInputAndChangeEvent); } } @@ -912,8 +915,13 @@ if (controls_for_name.size() == 1u) { bool checked; CHECK(ToBoolean(value, checked)); - To<HTMLInputElement>(controls_for_name.front()->ToHTMLElement()) - .SetChecked(checked); + HTMLInputElement& input = + To<HTMLInputElement>(controls_for_name.front()->ToHTMLElement()); + input.SetChecked(checked, + TextFieldEventBehavior::kDispatchInputAndChangeEvent); + // SetChecked only dispatches `input`. Usually, `change` is dispatched by + // CheckboxInputType::RunInputActivationBehavior. + input.DispatchChangeEvent(); return; } @@ -935,7 +943,11 @@ // Check (or uncheck) each value. for (ListedElement* control : controls_for_name) { HTMLInputElement& input = To<HTMLInputElement>(control->ToHTMLElement()); - input.SetChecked(checked_values.Contains(input.Value())); + input.SetChecked(checked_values.Contains(input.Value()), + TextFieldEventBehavior::kDispatchInputAndChangeEvent); + // SetChecked only dispatches `input`. Usually, `change` is dispatched by + // CheckboxInputType::RunInputActivationBehavior. + input.DispatchChangeEvent(); } } @@ -948,7 +960,11 @@ for (ListedElement* control : controls_for_name) { HTMLInputElement& input = To<HTMLInputElement>(control->ToHTMLElement()); if (input.Value() == string) { - input.SetChecked(true, TextFieldEventBehavior::kDispatchChangeEvent); + input.SetChecked(true, + TextFieldEventBehavior::kDispatchInputAndChangeEvent); + // SetChecked only dispatches `input`. Usually, `change` is dispatched by + // RadioInputType::RunInputActivationBehavior. + input.DispatchChangeEvent(); } } }
diff --git a/third_party/blink/renderer/core/html/html_tag_names.json5 b/third_party/blink/renderer/core/html/html_tag_names.json5 index 6ebddbe..beeee6f 100644 --- a/third_party/blink/renderer/core/html/html_tag_names.json5 +++ b/third_party/blink/renderer/core/html/html_tag_names.json5
@@ -120,6 +120,8 @@ name: "colgroup", interfaceName: "HTMLTableColElement", }, + // TODO(crbug.com/491158089): This should be removed in M150, once the + // HTMLCommandElementRemoval flag was removed. { name: "command", interfaceName: "HTMLUnknownElement",
diff --git a/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc b/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc index d7af6f7..20bceda4 100644 --- a/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc +++ b/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc
@@ -340,11 +340,10 @@ MakeGarbageCollected<ConsoleMessage>( mojom::ConsoleMessageSource::kOther, mojom::ConsoleMessageLevel::kWarning, - StrCat( - {"Dropped srcset candidate ", - JSONValue::QuoteString(String(attribute_span.subspan( - image_url_start, - image_url_end - image_url_start)))}))); + StrCat({"Dropped srcset candidate ", + JSONValue::QuoteString(attribute.subview( + image_url_start, + image_url_end - image_url_start))}))); } } continue;
diff --git a/third_party/blink/renderer/core/html/parser/html_stack_item.h b/third_party/blink/renderer/core/html/parser/html_stack_item.h index d884b20e..9f2927a5 100644 --- a/third_party/blink/renderer/core/html/parser/html_stack_item.h +++ b/third_party/blink/renderer/core/html/parser/html_stack_item.h
@@ -229,7 +229,6 @@ case html_names::HTMLTag::kCenter: case html_names::HTMLTag::kCol: case html_names::HTMLTag::kColgroup: - case html_names::HTMLTag::kCommand: case html_names::HTMLTag::kDd: case html_names::HTMLTag::kDetails: case html_names::HTMLTag::kDir: @@ -294,6 +293,8 @@ case html_names::HTMLTag::kWbr: case html_names::HTMLTag::kXmp: return true; + case html_names::HTMLTag::kCommand: + return !RuntimeEnabledFeatures::HTMLCommandElementRemovalEnabled(); default: return false; }
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc index 827f1815..c90b5957 100644 --- a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc +++ b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
@@ -693,7 +693,6 @@ case HTMLTag::kBase: case HTMLTag::kBasefont: case HTMLTag::kBgsound: - case HTMLTag::kCommand: case HTMLTag::kLink: case HTMLTag::kMeta: case HTMLTag::kNoframes: @@ -1081,6 +1080,13 @@ case HTMLTag::kTr: ParseError(token); break; + case HTMLTag::kCommand: + if (!RuntimeEnabledFeatures::HTMLCommandElementRemovalEnabled()) { + bool did_process = ProcessStartTagForInHead(token); + DCHECK(did_process); + break; + } + [[fallthrough]]; default: if (token->GetName() == mathml_names::kMathTag.LocalName()) { tree_.ReconstructTheActiveFormattingElements(); @@ -2733,13 +2739,18 @@ case HTMLTag::kBase: case HTMLTag::kBasefont: case HTMLTag::kBgsound: - case HTMLTag::kCommand: case HTMLTag::kLink: case HTMLTag::kMeta: tree_.InsertSelfClosingHTMLElementDestroyingToken(token); // Note: The custom processing for the <meta> tag is done in // HTMLMetaElement::process(). return true; + case html_names::HTMLTag::kCommand: + if (RuntimeEnabledFeatures::HTMLCommandElementRemovalEnabled()) { + return false; + } + tree_.InsertSelfClosingHTMLElementDestroyingToken(token); + return true; case HTMLTag::kTitle: ProcessGenericRCDATAStartTag(token); return true;
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc index 2f7e705..8dff9474 100644 --- a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc +++ b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
@@ -1023,10 +1023,9 @@ InspectorStyle::LonghandProperties( const CSSPropertySourceData& property_entry) { DCHECK(style_); - String property_value = property_entry.value; + StringView property_value = property_entry.value; if (property_entry.important) { - property_value = property_value.substr( - 0, property_value.length() - 10 /* length of "!important" */); + property_value.remove_suffix(10 /* length of "!important" */); } CSSParserTokenStream stream(property_value); stream.EnsureLookAhead(); // Several parsers expect this.
diff --git a/third_party/blink/renderer/core/layout/box_fragment_builder.h b/third_party/blink/renderer/core/layout/box_fragment_builder.h index 52415f7..4f528e63 100644 --- a/third_party/blink/renderer/core/layout/box_fragment_builder.h +++ b/third_party/blink/renderer/core/layout/box_fragment_builder.h
@@ -690,9 +690,8 @@ table_section_row_offsets_ = std::move(row_offsets); } - void TransferGridLayoutData( - std::unique_ptr<GridLayoutData> grid_layout_data) { - grid_layout_data_ = std::move(grid_layout_data); + void SetGridLayoutData(const GridLayoutData* grid_layout_data) { + grid_layout_data_ = grid_layout_data; } void TransferFlexLayoutData( std::unique_ptr<DevtoolsFlexInfo> flex_layout_data) { @@ -707,7 +706,7 @@ const GridLayoutData& GetGridLayoutData() const { DCHECK(grid_layout_data_); - return *grid_layout_data_.get(); + return *grid_layout_data_; } BreakTokenAlgorithmData* GetBreakTokenData() { return break_token_data_; } @@ -854,9 +853,7 @@ Vector<LayoutUnit> table_section_row_offsets_; BreakTokenAlgorithmData* break_token_data_ = nullptr; - - // Grid specific types. - std::unique_ptr<GridLayoutData> grid_layout_data_; + const GridLayoutData* grid_layout_data_ = nullptr; std::unique_ptr<DevtoolsFlexInfo> flex_layout_data_; std::unique_ptr<FrameSetLayoutData> frame_set_layout_data_;
diff --git a/third_party/blink/renderer/core/layout/grid/grid_data.h b/third_party/blink/renderer/core/layout/grid/grid_data.h index 05c091de..e48f18a 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_data.h +++ b/third_party/blink/renderer/core/layout/grid/grid_data.h
@@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_GRID_DATA_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_GRID_DATA_H_ +#include "base/memory/values_equivalent.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/grid/grid_line_resolver.h" #include "third_party/blink/renderer/core/layout/grid/grid_subtree.h" @@ -60,42 +61,40 @@ wtf_size_t row_start_offset{0}; }; -namespace { - -bool AreEqual(const std::unique_ptr<GridLayoutTrackCollection>& lhs, - const std::unique_ptr<GridLayoutTrackCollection>& rhs) { - return (lhs && rhs) ? *lhs == *rhs : !lhs && !rhs; -} - -} // namespace - // This struct contains the column and row data necessary to layout grid items. // For grid sizing, it will store |GridSizingTrackCollection| pointers, which // are able to modify the geometry of its sets. However, after sizing is done, // it should only copy |GridLayoutTrackCollection| immutable data. -class CORE_EXPORT GridLayoutData { - USING_FAST_MALLOC(GridLayoutData); - +class CORE_EXPORT GridLayoutData : public GarbageCollected<GridLayoutData> { public: GridLayoutData() = default; GridLayoutData(GridLayoutData&&) = default; GridLayoutData& operator=(GridLayoutData&&) = default; GridLayoutData(const GridLayoutData& other) { + // Do a deep copy of the track collections if they have baselines, since + // they may be mutated per-subgrid with baseline alignment; otherwise, + // shallow copy is sufficient. if (other.columns_) { - columns_ = std::make_unique<GridLayoutTrackCollection>(other.Columns()); + if (other.columns_->HasBaselines()) { + columns_ = + MakeGarbageCollected<GridLayoutTrackCollection>(other.Columns()); + } else { + columns_ = other.columns_; + } } if (other.rows_) { - rows_ = std::make_unique<GridLayoutTrackCollection>(other.Rows()); + if (other.rows_->HasBaselines()) { + rows_ = MakeGarbageCollected<GridLayoutTrackCollection>(other.Rows()); + } else { + rows_ = other.rows_; + } } } - GridLayoutData& operator=(const GridLayoutData& other) { - return *this = GridLayoutData(other); - } - bool operator==(const GridLayoutData& other) const { - return AreEqual(columns_, other.columns_) && AreEqual(rows_, other.rows_); + return base::ValuesEquivalent(columns_, other.columns_) && + base::ValuesEquivalent(rows_, other.rows_); } bool HasSubgriddedAxis(GridTrackSizingDirection track_direction) const { @@ -134,21 +133,20 @@ // This method is intended for subgrids with both a standalone and a // subgridded axis. Returns the only subgridded track collection. - const GridLayoutTrackCollection& OnlySubgriddedCollection() const { + const GridLayoutTrackCollection* OnlySubgriddedCollection() const { DCHECK(columns_); DCHECK(rows_); DCHECK_NE(columns_->IsForSizing(), rows_->IsForSizing()); - return columns_->IsForSizing() ? *rows_ : *columns_; + return columns_->IsForSizing() ? rows_.Get() : columns_.Get(); } - void SetTrackCollection( - std::unique_ptr<GridLayoutTrackCollection> track_collection) { + void SetTrackCollection(GridLayoutTrackCollection* track_collection) { DCHECK(track_collection); if (track_collection->Direction() == kForColumns) { - columns_ = std::move(track_collection); + columns_ = track_collection; } else { - rows_ = std::move(track_collection); + rows_ = track_collection; } } @@ -160,10 +158,14 @@ (rows_ && Rows().HasIndefiniteSet()); } + void Trace(Visitor* visitor) const { + visitor->Trace(columns_); + visitor->Trace(rows_); + } + private: - // TODO(7154891): Oilpanify GridLayoutTrackCollection. - std::unique_ptr<GridLayoutTrackCollection> columns_; - std::unique_ptr<GridLayoutTrackCollection> rows_; + Member<GridLayoutTrackCollection> columns_; + Member<GridLayoutTrackCollection> rows_; }; // Subgrid layout relies on the root grid to perform the track sizing algorithm @@ -178,15 +180,15 @@ class GridLayoutTree : public GarbageCollected<GridLayoutTree> { public: struct GridTreeNode : public GarbageCollected<GridTreeNode> { - GridTreeNode(const GridLayoutData& layout_data, wtf_size_t subtree_size) - : has_unresolved_geometry(layout_data.HasIndefiniteSet()), + GridTreeNode(const GridLayoutData* layout_data, wtf_size_t subtree_size) + : has_unresolved_geometry(layout_data->HasIndefiniteSet()), layout_data(layout_data), subtree_size(subtree_size) {} - void Trace(Visitor* visitor) const {} + void Trace(Visitor* visitor) const { visitor->Trace(layout_data); } + bool has_unresolved_geometry; - // TODO(7154891): Oilpanify GridLayoutData. - GridLayoutData layout_data; + Member<const GridLayoutData> layout_data; wtf_size_t subtree_size; }; @@ -203,8 +205,8 @@ } for (wtf_size_t i = 0; i < subtree_size; ++i) { - if (other.LayoutData(other_subtree_root + i) != - LayoutData(subtree_root + i)) { + if (*other.LayoutData(other_subtree_root + i) != + *LayoutData(subtree_root + i)) { return false; } } @@ -216,7 +218,7 @@ return tree_data_[index]->has_unresolved_geometry; } - const GridLayoutData& LayoutData(wtf_size_t index) const { + const GridLayoutData* LayoutData(wtf_size_t index) const { DCHECK_LT(index, tree_data_.size()); return tree_data_[index]->layout_data; } @@ -274,12 +276,13 @@ return LayoutTree().HasUnresolvedGeometry(subtree_root_); } - const GridLayoutData& LayoutData() const { + const GridLayoutData* LayoutData() const { return LayoutTree().LayoutData(subtree_root_); } void Trace(Visitor* visitor) const { visitor->Trace(layout_tree_); } + private: const GridLayoutTree& LayoutTree() const { DCHECK(layout_tree_); return *layout_tree_;
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc index aadd1f5..dfa1e03 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
@@ -95,7 +95,7 @@ ComputeGridGeometry(&grid_items, &intrinsic_block_size, &oof_children); } - const auto& layout_data = layout_subtree->LayoutData(); + const auto* layout_data = layout_subtree->LayoutData(); LayoutUnit offset_in_stitched_container; LayoutUnit previous_offset_in_stitched_container; Vector<GridItemPlacementData> grid_items_placement_data; @@ -131,12 +131,12 @@ first_unprocessed_row_gap_idx = grid_data->first_unprocessed_row_gap_idx; } else { row_offset_adjustments = - Vector<LayoutUnit>(layout_data.Rows().GetSetCount() + 1); + Vector<LayoutUnit>(layout_data->Rows().GetSetCount() + 1); // `EndLineOfImplicitGrid()` is equivalent to the total track count. // TODO(samomekarajr): Add this and number of gaps to the // `GridTrackCollection` API. const wtf_size_t total_column_track_count = - layout_data.Columns().EndLineOfImplicitGrid(); + layout_data->Columns().EndLineOfImplicitGrid(); if (total_column_track_count > 1) { column_gaps_segment_ranges_start_indices = Vector<wtf_size_t>(total_column_track_count - 1, 0); @@ -168,11 +168,11 @@ // are the size of the grid, and *not* where the inflow grid items are placed. // Explicitly set the inflow-bounds to the grid size. if (node.IsScrollContainer()) { - LogicalOffset offset = {layout_data.Columns().GetSetOffset(0), - layout_data.Rows().GetSetOffset(0)}; + LogicalOffset offset = {layout_data->Columns().GetSetOffset(0), + layout_data->Rows().GetSetOffset(0)}; - LogicalSize size = {layout_data.Columns().CalculateSetSpanSize(), - layout_data.Rows().CalculateSetSpanSize()}; + LogicalSize size = {layout_data->Columns().CalculateSetSpanSize(), + layout_data->Rows().CalculateSetSpanSize()}; container_builder_.SetInflowBounds(LogicalRect(offset, size)); } @@ -230,12 +230,12 @@ container_builder_.SetPreviousBreakAfter(row_break_between.back()); } - if (!oof_children.empty()) - PlaceOutOfFlowItems(layout_data, block_size, oof_children); + if (!oof_children.empty()) { + PlaceOutOfFlowItems(*layout_data, block_size, oof_children); + } - // Copy grid layout data for use in computed style and devtools. - container_builder_.TransferGridLayoutData( - std::make_unique<GridLayoutData>(layout_data)); + // Store grid layout data for use in computed style and devtools. + container_builder_.SetGridLayoutData(layout_data); SetReadingFlowNodes(grid_items); @@ -272,7 +272,7 @@ if (const auto* layout_subtree = GetConstraintSpace().GetGridLayoutSubtree()) { return FixedMinMaxSizes( - layout_subtree->LayoutData().Columns().CalculateSetSpanSize()); + layout_subtree->LayoutData()->Columns().CalculateSetSpanSize()); } // If we have inline size containment ignore all children. @@ -374,7 +374,7 @@ // subgrid whose geometry is already computed. We can exit early by simply // copying the layout data and constructing our grid items. if (const auto* layout_subtree = constraint_space.GetGridLayoutSubtree()) { - const auto& layout_data = layout_subtree->LayoutData(); + const auto* layout_data = layout_subtree->LayoutData(); if (!node.ChildLayoutBlockedByDisplayLock()) { bool must_invalidate_placement_cache = false; @@ -386,14 +386,14 @@ << "We shouldn't need to invalidate the placement cache if we relied " "on the cached line resolver; it must produce the same placement."; - GridTrackSizingAlgorithm::CacheGridItemsProperties(layout_data.Columns(), + GridTrackSizingAlgorithm::CacheGridItemsProperties(layout_data->Columns(), grid_items); - GridTrackSizingAlgorithm::CacheGridItemsProperties(layout_data.Rows(), + GridTrackSizingAlgorithm::CacheGridItemsProperties(layout_data->Rows(), grid_items); } *intrinsic_block_size = - CalculateIntrinsicBlockSize(*grid_items, layout_data); + CalculateIntrinsicBlockSize(*grid_items, *layout_data); return layout_subtree; } @@ -611,7 +611,7 @@ const auto subgridded_item = grid_item->is_subgridded_to_parent_grid ? sizing_subtree.LookupSubgriddedItemData(*grid_item) - : SubgriddedItemData(*grid_item, sizing_subtree.LayoutData(), + : SubgriddedItemData(*grid_item, &sizing_subtree.LayoutData(), writing_mode); // TODO(ikilpatrick): We'll need to record if any child used an indefinite @@ -855,8 +855,10 @@ } } - layout_data.SetTrackCollection(std::make_unique<GridSizingTrackCollection>( - range_builder.FinalizeRanges(), track_direction, must_create_baselines)); + layout_data.SetTrackCollection( + MakeGarbageCollected<GridSizingTrackCollection>( + range_builder.FinalizeRanges(), track_direction, + must_create_baselines)); } GridLineResolver GridLayoutAlgorithm::BuildGridLineResolver( @@ -989,7 +991,7 @@ const auto subgridded_item = grid_item.is_subgridded_to_parent_grid ? sizing_subtree.LookupSubgriddedItemData(grid_item) - : SubgriddedItemData(grid_item, sizing_subtree.LayoutData(), + : SubgriddedItemData(grid_item, &sizing_subtree.LayoutData(), writing_mode); LayoutUnit inline_offset, block_offset; @@ -1890,13 +1892,13 @@ DCHECK(out_row_break_between); const auto& container_space = GetConstraintSpace(); - const auto& layout_data = layout_subtree.LayoutData(); + const auto* layout_data = layout_subtree.LayoutData(); const bool should_propagate_child_break_values = container_space.ShouldPropagateChildBreakValues(); if (should_propagate_child_break_values) { *out_row_break_between = Vector<EBreakBetween>( - layout_data.Rows().GetSetCount() + 1, EBreakBetween::kAuto); + layout_data->Rows().GetSetCount() + 1, EBreakBetween::kAuto); } GridBaselineAccumulator baseline_accumulator(Style().GetFontBaseline()); @@ -1918,11 +1920,11 @@ (RuntimeEnabledFeatures::CSSGridGapSuppressionEnabled() && out_unfragmented_gap_geometry)) { gap_accumulator = GapAccumulator(); - gap_accumulator->BuildGapGeometry(layout_data); + gap_accumulator->BuildGapGeometry(*layout_data); if (out_track_idx_to_set_idx) { *out_track_idx_to_set_idx = - gap_accumulator->GetRowGapToSetIndicesMap(layout_data); + gap_accumulator->GetRowGapToSetIndicesMap(*layout_data); } } @@ -1937,7 +1939,7 @@ LogicalRect containing_grid_area; const auto space = CreateConstraintSpaceForLayout( - grid_item, layout_data, child_layout_subtree, &containing_grid_area); + grid_item, *layout_data, child_layout_subtree, &containing_grid_area); const auto& item_style = grid_item.node.Style(); const auto margins = ComputeMarginsFor(space, item_style, container_space); @@ -1948,13 +1950,13 @@ LogicalBoxFragment fragment(container_writing_direction, physical_fragment); LayoutUnit inline_baseline_offset = ComputeBaselineOffset( - grid_item, layout_data.Columns(), + grid_item, layout_data->Columns(), LogicalBoxFragment(grid_item.BaselineWritingDirection(kForColumns), physical_fragment), fragment, grid_item.parent_grid_font_baseline, kForColumns, containing_grid_area.size.inline_size); LayoutUnit block_baseline_offset = ComputeBaselineOffset( - grid_item, layout_data.Rows(), + grid_item, layout_data->Rows(), LogicalBoxFragment(grid_item.BaselineWritingDirection(kForRows), physical_fragment), fragment, grid_item.parent_grid_font_baseline, kForRows, @@ -2031,8 +2033,8 @@ } // Propagate the baselines. - if (layout_data.Rows().HasBaselines()) { - baseline_accumulator.AccumulateRows(layout_data.Rows()); + if (layout_data->Rows().HasBaselines()) { + baseline_accumulator.AccumulateRows(layout_data->Rows()); } if (auto first_baseline = baseline_accumulator.FirstBaseline()) container_builder_.SetFirstBaseline(*first_baseline); @@ -2162,7 +2164,7 @@ } const auto fragmentainer_block_size = FragmentainerCapacityForChildren(); - const auto& layout_data = layout_subtree.LayoutData(); + const auto& layout_data = *layout_subtree.LayoutData(); base::span<const Member<const BreakToken>> child_break_tokens; if (GetBreakToken()) {
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm_test.cc index 767e21b..09074735 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm_test.cc +++ b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm_test.cc
@@ -43,12 +43,12 @@ algorithm.CompleteTrackSizingAlgorithm(kForRows, SizingConstraint::kLayout, &grid_sizing_tree); - layout_data_ = std::move(grid_sizing_tree.LayoutData()); + layout_data_ = &grid_sizing_tree.LayoutData(); for (const auto& grid_item : grid_sizing_tree.GetGridItems()) { GridItemCachedData item_data; item_data.available_row_size = - grid_item.CalculateAvailableSize(layout_data_.Rows()); + grid_item.CalculateAvailableSize(layout_data_->Rows()); item_data.column_span_properties = grid_item.column_span_properties; item_data.row_span_properties = grid_item.row_span_properties; item_data.resolved_position = grid_item.resolved_position; @@ -59,8 +59,8 @@ const GridSizingTrackCollection& TrackCollection( GridTrackSizingDirection track_direction) { const auto& track_collection = (track_direction == kForColumns) - ? layout_data_.Columns() - : layout_data_.Rows(); + ? layout_data_->Columns() + : layout_data_->Rows(); return To<GridSizingTrackCollection>(track_collection); } @@ -163,7 +163,7 @@ }; Vector<GridItemCachedData> grid_items_data_; - GridLayoutData layout_data_; + Persistent<GridLayoutData> layout_data_; }; TEST_F(GridLayoutAlgorithmTest, GridLayoutAlgorithmAvailableRowSizes) {
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_utils.cc b/third_party/blink/renderer/core/layout/grid/grid_layout_utils.cc index 645a3e97..d541ae9 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_layout_utils.cc +++ b/third_party/blink/renderer/core/layout/grid/grid_layout_utils.cc
@@ -686,7 +686,7 @@ const auto writing_mode = constraint_space.GetWritingMode(); GridItems grid_items; - GridLayoutData layout_data; + GridLayoutData* layout_data = MakeGarbageCollected<GridLayoutData>(); bool has_nested_subgrid = false; if (!must_ignore_children) { @@ -702,18 +702,17 @@ if (has_standalone_columns) { algorithm.BuildSizingCollection(kForColumns, line_resolver, grid_items, - layout_data, sizing_constraint, + *layout_data, sizing_constraint, needs_intrinsic_track_size); } if (has_standalone_rows) { algorithm.BuildSizingCollection(kForRows, line_resolver, grid_items, - layout_data, sizing_constraint, + *layout_data, sizing_constraint, needs_intrinsic_track_size); } if (!has_nested_subgrid) { - sizing_tree->SetSizingNodeData(node, std::move(grid_items), - std::move(layout_data)); + sizing_tree->SetSizingNodeData(node, std::move(grid_items), layout_data); return; } @@ -721,17 +720,17 @@ InitializeTrackCollection(opt_subgrid_data, style, constraint_space, algorithm.BorderScrollbarPadding(), algorithm.GetGridAvailableSize(), kForColumns, - &layout_data); + layout_data); InitializeTrackCollection(opt_subgrid_data, style, constraint_space, algorithm.BorderScrollbarPadding(), algorithm.GetGridAvailableSize(), kForRows, - &layout_data); + layout_data); if (has_standalone_columns) { - layout_data.SizingCollection(kForColumns).CacheDefiniteSetsGeometry(); + layout_data->SizingCollection(kForColumns).CacheDefiniteSetsGeometry(); } if (has_standalone_rows) { - layout_data.SizingCollection(kForRows).CacheDefiniteSetsGeometry(); + layout_data->SizingCollection(kForRows).CacheDefiniteSetsGeometry(); } // `AppendSubgriddedItems` rely on the cached placement data of a subgrid to @@ -749,11 +748,11 @@ // the set indices of this grid item to determine its available space. This // happens because subgridded items are not considered by the range builder // since they can't be placed before we recurse into subgrids. - grid_item.ComputeSetIndices(layout_data.Columns()); - grid_item.ComputeSetIndices(layout_data.Rows()); + grid_item.ComputeSetIndices(layout_data->Columns()); + grid_item.ComputeSetIndices(layout_data->Rows()); const auto space = - algorithm.CreateConstraintSpaceForLayout(grid_item, layout_data); + algorithm.CreateConstraintSpaceForLayout(grid_item, *layout_data); const auto fragment_geometry = CalculateInitialFragmentGeometryForSubgrid(grid_item, space); @@ -788,17 +787,16 @@ // repetitions, so we do this process twice to avoid a cyclic dependency. if (has_standalone_columns) { algorithm.BuildSizingCollection(kForColumns, line_resolver, grid_items, - layout_data, sizing_constraint, + *layout_data, sizing_constraint, needs_intrinsic_track_size); } if (has_standalone_rows) { algorithm.BuildSizingCollection(kForRows, line_resolver, grid_items, - layout_data, sizing_constraint, + *layout_data, sizing_constraint, needs_intrinsic_track_size); } - sizing_tree->SetSizingNodeData(node, std::move(grid_items), - std::move(layout_data)); + sizing_tree->SetSizingNodeData(node, std::move(grid_items), layout_data); } // TODO(almaher): Need to add an instance for GridLanesLayoutAlgorithm. @@ -913,7 +911,7 @@ return fragment_geometry; } -std::unique_ptr<GridLayoutTrackCollection> CreateSubgridTrackCollection( +GridLayoutTrackCollection* CreateSubgridTrackCollection( const SubgriddedItemData& subgrid_data, const ComputedStyle& style, const ConstraintSpace& space, @@ -932,17 +930,16 @@ ? subgrid_data->column_range_indices : subgrid_data->row_range_indices; - return std::make_unique<GridLayoutTrackCollection>( - parent_track_collection.CreateSubgridTrackCollection( - range_indices.begin, range_indices.end, - GridTrackSizingAlgorithm::CalculateGutterSize( - style, grid_available_size, track_direction, - parent_track_collection.GutterSize()), - ComputeMarginsForSelf(space, style), border_scrollbar_padding, - track_direction, - is_for_columns_in_parent - ? subgrid_data->is_opposite_direction_in_root_grid_columns - : subgrid_data->is_opposite_direction_in_root_grid_rows)); + return parent_track_collection.CreateSubgridTrackCollection( + range_indices.begin, range_indices.end, + GridTrackSizingAlgorithm::CalculateGutterSize( + style, grid_available_size, track_direction, + parent_track_collection.GutterSize()), + ComputeMarginsForSelf(space, style), border_scrollbar_padding, + track_direction, + is_for_columns_in_parent + ? subgrid_data->is_opposite_direction_in_root_grid_columns + : subgrid_data->is_opposite_direction_in_root_grid_rows); } void InitializeTrackCollection(const SubgriddedItemData& opt_subgrid_data,
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_utils.h b/third_party/blink/renderer/core/layout/grid/grid_layout_utils.h index 7d2b1a1..b443ce8 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_layout_utils.h +++ b/third_party/blink/renderer/core/layout/grid/grid_layout_utils.h
@@ -244,14 +244,14 @@ DCHECK(next_subgrid_subtree); callback_func( subgrid_algorithm, next_subgrid_subtree, - SubgriddedItemData(grid_item, layout_data, + SubgriddedItemData(grid_item, &layout_data, algorithm.GetConstraintSpace().GetWritingMode())); next_subgrid_subtree = next_subgrid_subtree.NextSibling(); } } -std::unique_ptr<GridLayoutTrackCollection> CreateSubgridTrackCollection( +GridLayoutTrackCollection* CreateSubgridTrackCollection( const SubgriddedItemData& subgrid_data, const ComputedStyle& style, const ConstraintSpace& space,
diff --git a/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.cc b/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.cc index 11c148cd..bc1a899 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.cc +++ b/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.cc
@@ -18,12 +18,12 @@ void GridSizingTree::SetSizingNodeData(const BlockNode& grid_node, GridItems&& grid_items, - GridLayoutData&& layout_data) { + GridLayoutData* layout_data) { DCHECK(grid_node.IsGrid()); const bool has_standalone_columns = - !layout_data.HasSubgriddedAxis(kForColumns); - const bool has_standalone_rows = !layout_data.HasSubgriddedAxis(kForRows); + !layout_data->HasSubgriddedAxis(kForColumns); + const bool has_standalone_rows = !layout_data->HasSubgriddedAxis(kForRows); const auto grid_node_index = LookupSubgridIndex(grid_node); auto child_subgrid_index = grid_node_index + 1; @@ -60,7 +60,7 @@ } tree_node.grid_items = std::move(grid_items); - tree_node.layout_data = std::move(layout_data); + tree_node.layout_data = layout_data; tree_node.writing_mode = grid_node.Style().GetWritingMode(); } @@ -73,7 +73,8 @@ for (const auto& grid_tree_node : tree_data_) { layout_tree_data.emplace_back( MakeGarbageCollected<GridLayoutTree::GridTreeNode>( - grid_tree_node.layout_data, grid_tree_node.subtree_size)); + MakeGarbageCollected<GridLayoutData>(*grid_tree_node.layout_data), + grid_tree_node.subtree_size)); } for (wtf_size_t i = tree_size; i; --i) {
diff --git a/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.h b/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.h index b61028b..48c5065 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.h +++ b/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.h
@@ -25,10 +25,10 @@ SubgriddedItemData() = default; SubgriddedItemData(const GridItemData& item_data_in_parent, - const GridLayoutData& parent_layout_data, + const GridLayoutData* parent_layout_data, WritingMode parent_writing_mode) : item_data_in_parent_(&item_data_in_parent), - parent_layout_data_(&parent_layout_data), + parent_layout_data_(parent_layout_data), parent_writing_mode_(parent_writing_mode) {} explicit operator bool() const { return item_data_in_parent_ != nullptr; } @@ -83,10 +83,14 @@ struct GridTreeNode { DISALLOW_NEW(); - void Trace(Visitor* visitor) const { visitor->Trace(grid_items); } + void Trace(Visitor* visitor) const { + visitor->Trace(grid_items); + visitor->Trace(layout_data); + } GridItems grid_items; - GridLayoutData layout_data; + // TODO(crbug.com/460491953): Make this Member<const GridLayoutData> + Member<GridLayoutData> layout_data; wtf_size_t subtree_size{1}; WritingMode writing_mode; }; @@ -101,12 +105,12 @@ void SetSizingNodeData(const BlockNode& grid_node, GridItems&& grid_items, - GridLayoutData&& layout_data); + GridLayoutData* layout_data); GridItems& GetGridItems(wtf_size_t index = 0) { return At(index).grid_items; } GridLayoutData& LayoutData(wtf_size_t index = 0) { - return At(index).layout_data; + return *At(index).layout_data; } // Creates a copy of the current grid geometry for the entire tree in a new
diff --git a/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc b/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc index 7cd74a0a..50411a2 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc +++ b/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc
@@ -626,7 +626,7 @@ begin_set_index <= last_indefinite_index; } -GridLayoutTrackCollection +GridLayoutTrackCollection* GridLayoutTrackCollection::CreateSubgridTrackCollection( wtf_size_t begin_range_index, wtf_size_t end_range_index, @@ -638,7 +638,8 @@ DCHECK_LE(begin_range_index, end_range_index); DCHECK_LT(end_range_index, ranges_.size()); - GridLayoutTrackCollection subgrid_track_collection(subgrid_track_direction); + GridLayoutTrackCollection* subgrid_track_collection = + MakeGarbageCollected<GridLayoutTrackCollection>(subgrid_track_direction); const wtf_size_t begin_set_index = ranges_[begin_range_index].begin_set_index; const wtf_size_t end_set_index = ranges_[end_range_index].begin_set_index + @@ -649,8 +650,8 @@ // Copy and translate the ranges in the subgrid's span. { - auto& subgrid_properties = subgrid_track_collection.properties_; - auto& subgrid_ranges = subgrid_track_collection.ranges_; + auto& subgrid_properties = subgrid_track_collection->properties_; + auto& subgrid_ranges = subgrid_track_collection->ranges_; const wtf_size_t range_count = end_range_index - begin_range_index; wtf_size_t current_begin_set_index = 0; @@ -701,22 +702,22 @@ // Accumulate the extra margin from the spanned sets in the parent track // collection and this subgrid's margins and gutter size delta. { - subgrid_track_collection.accumulated_gutter_size_delta_ = + subgrid_track_collection->accumulated_gutter_size_delta_ = subgrid_gutter_size_delta + accumulated_gutter_size_delta_; - auto& subgrid_sets_geometry = subgrid_track_collection.sets_geometry_; + auto& subgrid_sets_geometry = subgrid_track_collection->sets_geometry_; subgrid_sets_geometry.ReserveInitialCapacity(set_span_size + 1); subgrid_sets_geometry.emplace_back( /* offset */ subgrid_border_scrollbar_padding_start); // Opposite direction subgrids adjust extra margin from the opposite side. - subgrid_track_collection.accumulated_start_extra_margin_ = + subgrid_track_collection->accumulated_start_extra_margin_ = subgrid_margin_border_scrollbar_padding_start + (is_opposite_direction_in_root_grid ? EndExtraMargin(end_set_index) : StartExtraMargin(begin_set_index)); - subgrid_track_collection.accumulated_end_extra_margin_ = + subgrid_track_collection->accumulated_end_extra_margin_ = subgrid_margin_border_scrollbar_padding_end + (is_opposite_direction_in_root_grid ? StartExtraMargin(begin_set_index) : EndExtraMargin(end_set_index)); @@ -776,7 +777,7 @@ // Copy the last indefinite indices in the subgrid's span. if (!last_indefinite_index_.empty()) { auto& subgrid_last_indefinite_index = - subgrid_track_collection.last_indefinite_index_; + subgrid_track_collection->last_indefinite_index_; subgrid_last_indefinite_index.ReserveInitialCapacity(set_span_size + 1); subgrid_last_indefinite_index.push_back(kNotFound); @@ -833,10 +834,10 @@ std::swap(subgrid_baselines.major, subgrid_baselines.minor); } - subgrid_track_collection.baselines_.emplace(std::move(subgrid_baselines)); + subgrid_track_collection->baselines_.emplace(std::move(subgrid_baselines)); } - subgrid_track_collection.gutter_size_ = subgrid_gutter_size; + subgrid_track_collection->gutter_size_ = subgrid_gutter_size; return subgrid_track_collection; }
diff --git a/third_party/blink/renderer/core/layout/grid/grid_track_collection.h b/third_party/blink/renderer/core/layout/grid/grid_track_collection.h index e45cb5b0..6275bed 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_track_collection.h +++ b/third_party/blink/renderer/core/layout/grid/grid_track_collection.h
@@ -148,9 +148,9 @@ Vector<TrackBoundaryToRangePair, 16> end_lines_; }; -class CORE_EXPORT GridLayoutTrackCollection : public GridTrackCollectionBase { - USING_FAST_MALLOC(GridLayoutTrackCollection); - +class CORE_EXPORT GridLayoutTrackCollection + : public GarbageCollected<GridLayoutTrackCollection>, + public GridTrackCollectionBase { public: struct SetGeometry { explicit SetGeometry(LayoutUnit offset, wtf_size_t track_count = 0) @@ -160,6 +160,9 @@ wtf_size_t track_count; }; + explicit GridLayoutTrackCollection(GridTrackSizingDirection track_direction) + : track_direction_(track_direction) {} + GridLayoutTrackCollection() = delete; // Don't allow this class to be used for grid sizing. @@ -207,7 +210,7 @@ // Creates a track collection containing every |Range| with index in the range // [begin, end], including their respective |SetGeometry| and baselines. - GridLayoutTrackCollection CreateSubgridTrackCollection( + GridLayoutTrackCollection* CreateSubgridTrackCollection( wtf_size_t begin_range_index, wtf_size_t end_range_index, LayoutUnit subgrid_gutter_size, @@ -231,6 +234,8 @@ return collapsed_track_indexes_; } + virtual void Trace(Visitor* visitor) const {} + protected: friend class GridLanesLayoutAlgorithmTest; @@ -239,9 +244,6 @@ Vector<LayoutUnit, 16> minor; }; - explicit GridLayoutTrackCollection(GridTrackSizingDirection track_direction) - : track_direction_(track_direction) {} - // Checks whether any set in the range [begin, end) is indefinite. bool IsSpanningIndefiniteSet(wtf_size_t begin_set_index, wtf_size_t end_set_index) const; @@ -374,11 +376,11 @@ class CORE_EXPORT GridSizingTrackCollection final : public GridLayoutTrackCollection { - USING_FAST_MALLOC(GridSizingTrackCollection); - public: template <bool is_const> class CORE_EXPORT SetIteratorBase { + STACK_ALLOCATED(); + public: using TrackCollectionPtr = typename std::conditional<is_const,
diff --git a/third_party/blink/renderer/core/layout/grid/grid_track_collection_test.cc b/third_party/blink/renderer/core/layout/grid/grid_track_collection_test.cc index 57a68cf..e0c62cd 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_track_collection_test.cc +++ b/third_party/blink/renderer/core/layout/grid/grid_track_collection_test.cc
@@ -327,14 +327,16 @@ auto range_builder = CreateRangeBuilder(explicit_tracks, implicit_tracks, /* auto_repetitions */ 0); - GridSizingTrackCollection track_collection(range_builder.FinalizeRanges()); + GridSizingTrackCollection* track_collection = + MakeGarbageCollected<GridSizingTrackCollection>( + range_builder.FinalizeRanges()); InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks, - &track_collection); - const auto& ranges = GetRangesFrom(track_collection); + track_collection); + const auto& ranges = GetRangesFrom(*track_collection); // Test the set iterator for the entire collection. wtf_size_t set_count = 0; - for (auto set_iterator = track_collection.GetSetIterator(); + for (auto set_iterator = track_collection->GetSetIterator(); !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) { EXPECT_SET(GridTrackSize(Length::Flex(set_count++)), 1u, set_iterator); } @@ -347,7 +349,7 @@ EXPECT_RANGE(set_count, set_counts[range_count], ranges[i]); wtf_size_t current_range_set_count = 0; - for (auto set_iterator = IteratorForRange(track_collection, i); + for (auto set_iterator = IteratorForRange(*track_collection, i); !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) { EXPECT_SET(GridTrackSize(Length::Flex(set_count++)), 1u, set_iterator); ++current_range_set_count; @@ -387,10 +389,12 @@ range_builder.EnsureTrackCoverage(17, 3, &range3_start, &range3_end); range_builder.EnsureTrackCoverage(22, 5, &range4_start, &range4_end); - GridSizingTrackCollection track_collection(range_builder.FinalizeRanges()); + GridSizingTrackCollection* track_collection = + MakeGarbageCollected<GridSizingTrackCollection>( + range_builder.FinalizeRanges()); InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks, - &track_collection); - const auto& ranges = GetRangesFrom(track_collection); + track_collection); + const auto& ranges = GetRangesFrom(*track_collection); EXPECT_EQ(1u, range1_start); EXPECT_EQ(1u, range1_end); @@ -403,14 +407,14 @@ EXPECT_EQ(10u, ranges.size()); EXPECT_RANGE(0u, 2u, ranges[0]); - auto set_iterator = IteratorForRange(track_collection, /* range_index */ 0); + auto set_iterator = IteratorForRange(*track_collection, /* range_index */ 0); EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_RANGE(2u, 4u, ranges[1]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 1); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 1); EXPECT_SET(GridTrackSize(Length::Fixed(3)), 2u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator); @@ -419,7 +423,7 @@ EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_RANGE(6u, 3u, ranges[2]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 2); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 2); EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator); @@ -428,39 +432,39 @@ EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_COLLAPSED_RANGE(9u, 3u, ranges[3]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 3); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 3); EXPECT_TRUE(set_iterator.IsAtEnd()); EXPECT_RANGE(12u, 4u, ranges[4]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 4); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 4); EXPECT_SET(GridTrackSize(Length::Fixed(5)), 2u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(4)), 2u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_COLLAPSED_RANGE(16u, 1u, ranges[5]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 5); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 5); EXPECT_TRUE(set_iterator.IsAtEnd()); EXPECT_RANGE(17u, 2u, ranges[6]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 6); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 6); EXPECT_SET(GridTrackSize(Length::Fixed(4)), 1u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(5)), 1u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_RANGE(19u, 1u, ranges[7]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 7); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 7); EXPECT_SET(GridTrackSize(Length::Auto()), 1u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_RANGE(20u, 2u, ranges[8]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 8); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 8); EXPECT_SET(GridTrackSize(Length::Auto()), 2u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_RANGE(22u, 5u, ranges[9]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 9); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 9); EXPECT_SET(GridTrackSize(Length::Auto()), 5u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); } @@ -492,10 +496,12 @@ range_builder.EnsureTrackCoverage(2, 13, &range1_start, &range1_end); range_builder.EnsureTrackCoverage(23, 2, &range2_start, &range2_end); - GridSizingTrackCollection track_collection(range_builder.FinalizeRanges()); + GridSizingTrackCollection* track_collection = + MakeGarbageCollected<GridSizingTrackCollection>( + range_builder.FinalizeRanges()); InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks, - &track_collection); - const auto& ranges = GetRangesFrom(track_collection); + track_collection); + const auto& ranges = GetRangesFrom(*track_collection); EXPECT_EQ(1u, range1_start); EXPECT_EQ(2u, range1_end); @@ -504,21 +510,21 @@ EXPECT_EQ(5u, ranges.size()); EXPECT_RANGE(0u, 2u, ranges[0]); - auto set_iterator = IteratorForRange(track_collection, /* range_index */ 0); + auto set_iterator = IteratorForRange(*track_collection, /* range_index */ 0); EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_RANGE(2u, 2u, ranges[1]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 1); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 1); EXPECT_SET(GridTrackSize(Length::Fixed(3)), 1u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(4)), 1u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_RANGE(4u, 11u, ranges[2]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 2); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 2); EXPECT_SET(GridTrackSize(Length::Fixed(5)), 4u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(6)), 4u, set_iterator); @@ -527,7 +533,7 @@ EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_RANGE(15u, 8u, ranges[3]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 3); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 3); EXPECT_SET(GridTrackSize(Length::Fixed(7)), 3u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(5)), 3u, set_iterator); @@ -536,7 +542,7 @@ EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_RANGE(23u, 2u, ranges[4]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 4); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 4); EXPECT_SET(GridTrackSize(Length::Fixed(6)), 1u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(7)), 1u, set_iterator); @@ -564,10 +570,12 @@ range_builder.EnsureTrackCoverage(1, 2, &range1_start, &range1_end); range_builder.EnsureTrackCoverage(7, 4, &range2_start, &range2_end); - GridSizingTrackCollection track_collection(range_builder.FinalizeRanges()); + GridSizingTrackCollection* track_collection = + MakeGarbageCollected<GridSizingTrackCollection>( + range_builder.FinalizeRanges()); InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks, - &track_collection); - const auto& ranges = GetRangesFrom(track_collection); + track_collection); + const auto& ranges = GetRangesFrom(*track_collection); EXPECT_EQ(1u, range1_start); EXPECT_EQ(1u, range1_end); @@ -576,7 +584,7 @@ EXPECT_EQ(5u, ranges.size()); EXPECT_RANGE(0u, 1u, ranges[0]); - auto set_iterator = IteratorForRange(track_collection, /* range_index */ 0); + auto set_iterator = IteratorForRange(*track_collection, /* range_index */ 0); EXPECT_SET(GridTrackSize(Length::MinContent()), 1u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_FALSE( @@ -585,7 +593,7 @@ TrackSpanProperties::kHasIntrinsicTrack)); EXPECT_RANGE(1u, 2u, ranges[1]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 1); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 1); EXPECT_SET(GridTrackSize(Length::Flex(1.0)), 1u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator); @@ -596,7 +604,7 @@ TrackSpanProperties::kHasIntrinsicTrack)); EXPECT_RANGE(3u, 4u, ranges[2]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 2); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 2); EXPECT_SET(GridTrackSize(Length::Fixed(3)), 1u, set_iterator); EXPECT_TRUE(set_iterator.MoveToNextSet()); EXPECT_SET(GridTrackSize(Length::MinContent()), 1u, set_iterator); @@ -611,7 +619,7 @@ TrackSpanProperties::kHasIntrinsicTrack)); EXPECT_RANGE(7u, 1u, ranges[3]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 3); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 3); EXPECT_SET(GridTrackSize(Length::Fixed(3)), 1u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_FALSE( @@ -620,7 +628,7 @@ TrackSpanProperties::kHasIntrinsicTrack)); EXPECT_RANGE(8u, 3u, ranges[4]); - set_iterator = IteratorForRange(track_collection, /* range_index */ 4); + set_iterator = IteratorForRange(*track_collection, /* range_index */ 4); EXPECT_SET(GridTrackSize(Length::Auto()), 3u, set_iterator); EXPECT_FALSE(set_iterator.MoveToNextSet()); EXPECT_FALSE(
diff --git a/third_party/blink/renderer/core/layout/grid/layout_grid.cc b/third_party/blink/renderer/core/layout/grid/layout_grid.cc index 525d7d331..f22f0b3 100644 --- a/third_party/blink/renderer/core/layout/grid/layout_grid.cc +++ b/third_party/blink/renderer/core/layout/grid/layout_grid.cc
@@ -121,12 +121,13 @@ const MinMaxSizes& LayoutGrid::CachedSubgridMinMaxSizes() const { DCHECK(HasCachedSubgridMinMaxSizes()); - return **cached_subgrid_min_max_sizes_; + return cached_subgrid_min_max_sizes_->CachedMinMaxSizes(); } void LayoutGrid::SetSubgridMinMaxSizesCache(MinMaxSizes&& min_max_sizes, const GridLayoutData& layout_data) { - cached_subgrid_min_max_sizes_.emplace(std::move(min_max_sizes), layout_data); + cached_subgrid_min_max_sizes_ = MakeGarbageCollected<SubgridMinMaxSizesCache>( + std::move(min_max_sizes), layout_data); SetSubgridMinMaxSizesCacheDirty(false); }
diff --git a/third_party/blink/renderer/core/layout/grid/layout_grid.h b/third_party/blink/renderer/core/layout/grid/layout_grid.h index eac1de5..94dc392 100644 --- a/third_party/blink/renderer/core/layout/grid/layout_grid.h +++ b/third_party/blink/renderer/core/layout/grid/layout_grid.h
@@ -70,6 +70,11 @@ const GridLayoutData* LayoutData() const; + void Trace(Visitor* visitor) const override { + LayoutBlock::Trace(visitor); + visitor->Trace(cached_subgrid_min_max_sizes_); + } + private: bool IsLayoutGrid() const final { NOT_DESTROYED(); @@ -85,7 +90,7 @@ const StyleChangeContext&) override; std::optional<GridPlacementData> cached_placement_data_; - std::optional<const SubgridMinMaxSizesCache> cached_subgrid_min_max_sizes_; + Member<const SubgridMinMaxSizesCache> cached_subgrid_min_max_sizes_; }; // wtf/casting.h helper.
diff --git a/third_party/blink/renderer/core/layout/grid/subgrid_min_max_sizes_cache.h b/third_party/blink/renderer/core/layout/grid/subgrid_min_max_sizes_cache.h index d73f5b75..5f96052 100644 --- a/third_party/blink/renderer/core/layout/grid/subgrid_min_max_sizes_cache.h +++ b/third_party/blink/renderer/core/layout/grid/subgrid_min_max_sizes_cache.h
@@ -6,12 +6,12 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_SUBGRID_MIN_MAX_SIZES_CACHE_H_ #include "third_party/blink/renderer/core/layout/min_max_sizes.h" +#include "third_party/blink/renderer/platform/heap/garbage_collected.h" namespace blink { -class SubgridMinMaxSizesCache { - DISALLOW_NEW(); - +class SubgridMinMaxSizesCache + : public GarbageCollected<SubgridMinMaxSizesCache> { public: SubgridMinMaxSizesCache() = delete; SubgridMinMaxSizesCache(const SubgridMinMaxSizesCache&) = delete; @@ -23,18 +23,22 @@ layout_data.OnlySubgriddedCollection()), cached_min_max_sizes_(std::move(min_max_sizes)) {} - const MinMaxSizes& operator*() const { return cached_min_max_sizes_; } - bool IsValidFor(const GridLayoutData& layout_data) const { - return layout_data.OnlySubgriddedCollection() == - opposite_axis_subgridded_tracks_; + return *layout_data.OnlySubgriddedCollection() == + *opposite_axis_subgridded_tracks_; + } + + const MinMaxSizes& CachedMinMaxSizes() const { return cached_min_max_sizes_; } + + void Trace(Visitor* visitor) const { + visitor->Trace(opposite_axis_subgridded_tracks_); } private: // The intrinsic sizes of a subgrid's standalone axis might change when the // subgridded tracks in the opposite axis change. We keep a copy of these // tracks to check if the cache is reusable with the new layout data. - GridLayoutTrackCollection opposite_axis_subgridded_tracks_; + Member<const GridLayoutTrackCollection> opposite_axis_subgridded_tracks_; MinMaxSizes cached_min_max_sizes_; };
diff --git a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.cc b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.cc index 3786e02..cc3b9c6e 100644 --- a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.cc
@@ -81,7 +81,7 @@ const bool should_apply_inline_size_containment = node.ShouldApplyInlineSizeContainment(); - GridSizingTrackCollection track_collection = ComputeGridAxisTracks( + GridSizingTrackCollection* track_collection = ComputeGridAxisTracks( sizing_constraint, /*intrinsic_repeat_track_sizes=*/nullptr, should_apply_inline_size_containment, grid_lanes_items, needs_intrinsic_track_size); @@ -96,7 +96,7 @@ if (needs_intrinsic_track_size) { HashMap<GridTrackSize, LayoutUnit> intrinsic_repeat_track_sizes = GetIntrinsicRepeaterTrackSizes(!grid_lanes_items.IsEmpty(), - track_collection); + *track_collection); track_collection = ComputeGridAxisTracks( sizing_constraint, &intrinsic_repeat_track_sizes, should_apply_inline_size_containment, grid_lanes_items, @@ -107,7 +107,7 @@ // Track sizing is done during the guess placement step, which happens in // `BuildGridAxisTracks`, so at this point, getting the width of all of // the columns should correctly give us the intrinsic inline size. - return track_collection.CalculateSetSpanSize(); + return track_collection->CalculateSetSpanSize(); } else { if (grid_lanes_items.IsEmpty()) { // If there are no grid-lanes items, the intrinsic inline size is only @@ -116,11 +116,11 @@ } GridLanesRunningPositions running_positions( - track_collection, style, + *track_collection, style, ResolveFlowToleranceForGridLanes(style, grid_lanes_available_size_)); - PlaceGridLanesItems(track_collection, grid_lanes_items, running_positions, - sizing_constraint); + PlaceGridLanesItems(*track_collection, grid_lanes_items, + running_positions, sizing_constraint); // `stacking_axis_gap` represents the space between each of the items // in the row. We need to subtract this as it is always added to // `running_positions` whenever an item is placed, but the very last @@ -131,7 +131,7 @@ return running_positions.GetMaxPositionForSpan( GridSpan::TranslatedDefiniteGridSpan( /*start_line=*/0, - /*end_line=*/track_collection.EndLineOfImplicitGrid())) - + /*end_line=*/track_collection->EndLineOfImplicitGrid())) - stacking_axis_gap; } }; @@ -162,7 +162,7 @@ // Never apply inline size containment during the layout pass because size // containment is independent from layout. - GridSizingTrackCollection track_collection = ComputeGridAxisTracks( + GridSizingTrackCollection* track_collection = ComputeGridAxisTracks( SizingConstraint::kLayout, /*intrinsic_repeat_track_sizes=*/nullptr, /*should_apply_inline_size_containment=*/false, grid_lanes_items, needs_intrinsic_track_size, &oof_children); @@ -177,7 +177,7 @@ if (needs_intrinsic_track_size) { HashMap<GridTrackSize, LayoutUnit> intrinsic_repeat_track_sizes = GetIntrinsicRepeaterTrackSizes(!grid_lanes_items.IsEmpty(), - track_collection); + *track_collection); track_collection = ComputeGridAxisTracks( SizingConstraint::kLayout, &intrinsic_repeat_track_sizes, /*should_apply_inline_size_containment=*/false, grid_lanes_items, @@ -186,10 +186,10 @@ if (!grid_lanes_items.IsEmpty()) { GridLanesRunningPositions running_positions( - track_collection, Style(), + *track_collection, Style(), ResolveFlowToleranceForGridLanes(Style(), grid_lanes_available_size_)); - PlaceGridLanesItems(track_collection, grid_lanes_items, running_positions, + PlaceGridLanesItems(*track_collection, grid_lanes_items, running_positions, SizingConstraint::kLayout); } @@ -201,10 +201,8 @@ } // Create track layout data to support grid-lanes overlay in DevTools. - std::unique_ptr<GridLayoutData> layout_data( - std::make_unique<GridLayoutData>()); - layout_data->SetTrackCollection( - std::make_unique<GridLayoutTrackCollection>(track_collection)); + GridLayoutData* layout_data = MakeGarbageCollected<GridLayoutData>(); + layout_data->SetTrackCollection(track_collection); // Account for border, scrollbar, and padding in the intrinsic block size. intrinsic_block_size_ += BorderScrollbarPadding().BlockSum(); @@ -224,7 +222,7 @@ PlaceOutOfFlowItems(*layout_data, block_size, oof_children); } - container_builder_.TransferGridLayoutData(std::move(layout_data)); + container_builder_.SetGridLayoutData(layout_data); container_builder_.HandleOofsAndSpecialDescendants(); return container_builder_.ToBoxFragment(); } @@ -509,10 +507,10 @@ // TODO(almaher): This should use the sizing tree eventually to get this // data. - std::unique_ptr<GridLayoutData> layout_data( - std::make_unique<GridLayoutData>()); + // TODO(kschmi): Do we need to deep copy the track collection? + GridLayoutData* layout_data = MakeGarbageCollected<GridLayoutData>(); layout_data->SetTrackCollection( - std::make_unique<GridLayoutTrackCollection>(track_collection)); + MakeGarbageCollected<GridLayoutTrackCollection>(track_collection)); const ConstraintSpace space = is_for_layout ? CreateConstraintSpaceForLayout( @@ -1138,7 +1136,7 @@ // TODO(almaher): Once support for subgrid is added and we have a sizing // tree similar to grid, we may need to implement and call something // similar to Grid's BuildGridSizingTreeIgnoringChildren() here. - GridSizingTrackCollection track_collection = + GridSizingTrackCollection* track_collection = ComputeGridAxisTracks(SizingConstraint::kLayout, /*intrinsic_repeat_track_sizes=*/nullptr, /*should_apply_inline_size_containment=*/false, @@ -1154,14 +1152,14 @@ if (needs_intrinsic_track_size) { HashMap<GridTrackSize, LayoutUnit> intrinsic_repeat_track_sizes = GetIntrinsicRepeaterTrackSizes(!grid_lanes_items.IsEmpty(), - track_collection); + *track_collection); track_collection = ComputeGridAxisTracks( SizingConstraint::kLayout, &intrinsic_repeat_track_sizes, /*should_apply_inline_size_containment=*/false, grid_lanes_items, needs_intrinsic_track_size); } - override_intrinsic_block_size = track_collection.CalculateSetSpanSize(); + override_intrinsic_block_size = track_collection->CalculateSetSpanSize(); } else { // If we are in columns, the block size should just be the size of an // empty container. @@ -1252,7 +1250,7 @@ return baseline_fragment.BlockSize(); } -GridSizingTrackCollection GridLanesLayoutAlgorithm::ComputeGridAxisTracks( +GridSizingTrackCollection* GridLanesLayoutAlgorithm::ComputeGridAxisTracks( const SizingConstraint sizing_constraint, const HashMap<GridTrackSize, LayoutUnit>* intrinsic_repeat_track_sizes, const bool should_apply_inline_size_containment, @@ -1284,7 +1282,7 @@ needs_intrinsic_track_size); } -GridSizingTrackCollection GridLanesLayoutAlgorithm::BuildGridAxisTracks( +GridSizingTrackCollection* GridLanesLayoutAlgorithm::BuildGridAxisTracks( const GridLineResolver& line_resolver, const GridItems& grid_lanes_items, SizingConstraint sizing_constraint, @@ -1325,26 +1323,26 @@ return range_builder.FinalizeRanges(needs_intrinsic_track_size); }; - GridSizingTrackCollection track_collection( - BuildRanges(), grid_axis_direction, - /*must_create_baselines=*/has_baseline_aligned_items_, - /*should_store_collapsed_track_indexes=*/true); - track_collection.BuildSets(style, grid_lanes_available_size_); + GridSizingTrackCollection* track_collection = + MakeGarbageCollected<GridSizingTrackCollection>( + BuildRanges(), grid_axis_direction, has_baseline_aligned_items_, + /*should_store_collapsed_track_indexes=*/true); + track_collection->BuildSets(style, grid_lanes_available_size_); // Allocate the major/minor baseline vectors now that we know the set count. if (has_baseline_aligned_items_) { - track_collection.ResetBaselines(); + track_collection->ResetBaselines(); } - if (track_collection.HasNonDefiniteTrack()) { - GridTrackSizingAlgorithm::CacheGridItemsProperties(track_collection, + if (track_collection->HasNonDefiniteTrack()) { + GridTrackSizingAlgorithm::CacheGridItemsProperties(*track_collection, &virtual_items); const GridTrackSizingAlgorithm track_sizing_algorithm( style, grid_lanes_available_size_, grid_lanes_min_available_size_, sizing_constraint); - track_collection.CacheInitializedSetsGeometry( + track_collection->CacheInitializedSetsGeometry( (grid_axis_direction == kForColumns) ? BorderScrollbarPadding().inline_start : BorderScrollbarPadding().block_start); @@ -1355,17 +1353,17 @@ [&](GridItemContributionType contribution_type, GridItemData* virtual_item) { return ContributionSizeForVirtualItem( - track_collection, contribution_type, virtual_item); + *track_collection, contribution_type, virtual_item); }, - &track_collection, &virtual_items, needs_intrinsic_track_size); + track_collection, &virtual_items, needs_intrinsic_track_size); } auto first_set_geometry = GridTrackSizingAlgorithm::ComputeFirstSetGeometry( - track_collection, style, grid_lanes_available_size_, + *track_collection, style, grid_lanes_available_size_, BorderScrollbarPadding()); - track_collection.FinalizeSetsGeometry(first_set_geometry.start_offset, - first_set_geometry.gutter_size); + track_collection->FinalizeSetsGeometry(first_set_geometry.start_offset, + first_set_geometry.gutter_size); return track_collection; } @@ -1545,10 +1543,11 @@ return range_builder.FinalizeRanges(needs_intrinsic_track_size); }; - layout_data.SetTrackCollection(std::make_unique<GridSizingTrackCollection>( - BuildRanges(), grid_axis_direction, - /*must_create_baselines=*/has_baseline_aligned_items_, - /*should_store_collapsed_track_indexes=*/true)); + layout_data.SetTrackCollection( + MakeGarbageCollected<GridSizingTrackCollection>( + BuildRanges(), grid_axis_direction, + /*must_create_baselines=*/has_baseline_aligned_items_, + /*should_store_collapsed_track_indexes=*/true)); } ConstraintSpace GridLanesLayoutAlgorithm::CreateConstraintSpace(
diff --git a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.h b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.h index 4e6eb8b3..b6f5467 100644 --- a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.h +++ b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.h
@@ -144,7 +144,7 @@ // required once we've computed the intrinsic track size. `opt_oof_children` // is an optional vector of out-of-flow direct children of the grid-lanes // container that this method will populate. - GridSizingTrackCollection ComputeGridAxisTracks( + GridSizingTrackCollection* ComputeGridAxisTracks( const SizingConstraint sizing_constraint, const HashMap<GridTrackSize, LayoutUnit>* intrinsic_repeat_track_sizes, const bool should_apply_inline_size_containment, @@ -152,7 +152,7 @@ bool& needs_intrinsic_track_size, HeapVector<Member<LayoutBox>>* opt_oof_children = nullptr); - GridSizingTrackCollection BuildGridAxisTracks( + GridSizingTrackCollection* BuildGridAxisTracks( const GridLineResolver& line_resolver, const GridItems& grid_lanes_items, SizingConstraint sizing_constraint,
diff --git a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm_test.cc index 03b5e21d..c0e8ae0 100644 --- a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm_test.cc +++ b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm_test.cc
@@ -40,7 +40,7 @@ if (needs_intrinsic_track_size) { HashMap<GridTrackSize, LayoutUnit> intrinsic_repeat_track_sizes = algorithm.GetIntrinsicRepeaterTrackSizes(!grid_lanes_items.IsEmpty(), - grid_axis_tracks_.value()); + *grid_axis_tracks_); grid_axis_tracks_ = algorithm.ComputeGridAxisTracks( SizingConstraint::kLayout, &intrinsic_repeat_track_sizes, /*should_apply_inline_size_containment=*/false, grid_lanes_items, @@ -123,7 +123,7 @@ return virtual_items_data_[index]; } - std::optional<GridSizingTrackCollection> grid_axis_tracks_; + Persistent<GridSizingTrackCollection> grid_axis_tracks_; // Virtual items represent the contributions of item groups in track sizing // and are not directly related to any children of the container.
diff --git a/third_party/blink/renderer/core/layout/layout_result.cc b/third_party/blink/renderer/core/layout/layout_result.cc index 5345222..a52c0eb 100644 --- a/third_party/blink/renderer/core/layout/layout_result.cc +++ b/third_party/blink/renderer/core/layout/layout_result.cc
@@ -103,8 +103,7 @@ builder->math_italic_correction_; } if (builder->grid_layout_data_) { - EnsureRareData()->EnsureGridData()->grid_layout_data = - std::move(builder->grid_layout_data_); + EnsureRareData()->grid_layout_data = builder->grid_layout_data_; } if (builder->flex_layout_data_) { EnsureRareData()->EnsureFlexData()->flex_layout_data = @@ -416,6 +415,7 @@ visitor->Trace(early_break); visitor->Trace(non_overflowing_scroll_ranges); visitor->Trace(column_spanner_path); + visitor->Trace(grid_layout_data); visitor->Trace(exclusion_space); visitor->Trace(line_clamp_after_layout_object); visitor->Trace(accessibility_anchor);
diff --git a/third_party/blink/renderer/core/layout/layout_result.h b/third_party/blink/renderer/core/layout/layout_result.h index a934af5..fabd6ad 100644 --- a/third_party/blink/renderer/core/layout/layout_result.h +++ b/third_party/blink/renderer/core/layout/layout_result.h
@@ -407,11 +407,7 @@ } const GridLayoutData* GetGridLayoutData() const { - if (!rare_data_) { - return nullptr; - } - const RareData::GridData* data = rare_data_->GetGridData(); - return data ? data->grid_layout_data.get() : nullptr; + return rare_data_ ? rare_data_->grid_layout_data.Get() : nullptr; } const DevtoolsFlexInfo* FlexLayoutData() const { @@ -680,16 +676,6 @@ std::unique_ptr<const DevtoolsFlexInfo> flex_layout_data; }; - struct GridData { - GridData() = default; - GridData(const GridData& other) { - grid_layout_data = - std::make_unique<GridLayoutData>(*other.grid_layout_data); - } - - std::unique_ptr<const GridLayoutData> grid_layout_data; - }; - // `LineSmallData` can save allocations When only fields in it are needed. struct LineSmallData { std::optional<LayoutUnit> ClearanceAfterLine() const { @@ -806,12 +792,6 @@ const FlexData* GetFlexData() const { return GetData<FlexData>(&flex_data, kFlexData); } - GridData* EnsureGridData() { - return EnsureData<GridData>(&grid_data, kGridData); - } - const GridData* GetGridData() const { - return GetData<GridData>(&grid_data, kGridData); - } // When both `EnsureLineData()` and `EnsureLineSmallData()` are needed, // `EnsureLineData()` must be done first. Upgrading `kLineSmallData` to // `kLineData` isn't supported due to the lack of the needs. @@ -852,6 +832,7 @@ RareData(const RareData& rare_data) : early_break(rare_data.early_break), column_spanner_path(rare_data.column_spanner_path), + grid_layout_data(rare_data.grid_layout_data), end_margin_strut(rare_data.end_margin_strut), // This will initialize "both" members of the union. tallest_unbreakable_block_size( @@ -875,9 +856,6 @@ case kFlexData: new (&flex_data) FlexData(rare_data.flex_data); break; - case kGridData: - new (&grid_data) GridData(rare_data.grid_data); - break; case kLineSmallData: new (&line_small_data) LineSmallData(rare_data.line_small_data); break; @@ -902,9 +880,6 @@ case kFlexData: flex_data.~FlexData(); break; - case kGridData: - grid_data.~GridData(); - break; case kLineSmallData: line_small_data.~LineSmallData(); break; @@ -967,6 +942,7 @@ Member<const EarlyBreak> early_break; Member<const ColumnSpannerPath> column_spanner_path; + Member<const GridLayoutData> grid_layout_data; MarginStrut end_margin_strut; union { // Only set in the initial column balancing layout pass, when we have no @@ -1005,7 +981,6 @@ union { FlexData flex_data; - GridData grid_data; LineSmallData line_small_data; LineDataPtr line_data; MathData math_data;
diff --git a/third_party/blink/renderer/core/layout/simplified_layout_algorithm.cc b/third_party/blink/renderer/core/layout/simplified_layout_algorithm.cc index 60053a7..5e327ba 100644 --- a/third_party/blink/renderer/core/layout/simplified_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/simplified_layout_algorithm.cc
@@ -148,8 +148,7 @@ } if (physical_fragment.IsGrid() || physical_fragment.IsGridLanes()) { - container_builder_.TransferGridLayoutData( - std::make_unique<GridLayoutData>(*result.GetGridLayoutData())); + container_builder_.SetGridLayoutData(result.GetGridLayoutData()); } else if (physical_fragment.IsFrameSet()) { container_builder_.TransferFrameSetLayoutData( std::make_unique<FrameSetLayoutData>(
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc index 89b45bf..d3b9aff 100644 --- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc +++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
@@ -26,12 +26,8 @@ namespace { // TODO(bokan): Move this into FragmentDirective after // https://crrev.com/c/3216206 lands. -String RemoveFragmentDirectives(const String& url_fragment) { - wtf_size_t directive_delimiter_ix = url_fragment.find(":~:"); - if (directive_delimiter_ix == kNotFound) - return url_fragment; - - return url_fragment.substr(0, directive_delimiter_ix); +StringView RemoveFragmentDirectives(const StringView& url_fragment) { + return url_fragment.substr(0, url_fragment.find(":~:")); } } // namespace @@ -51,9 +47,8 @@ if (!url.HasFragmentIdentifier() && !doc.CssTarget() && !doc.IsSVGDocument()) return nullptr; - String fragment = - RemoveFragmentDirectives(url.FragmentIdentifier().ToString()); - Node* anchor_node = doc.FindAnchor(fragment); + StringView fragment = RemoveFragmentDirectives(url.FragmentIdentifier()); + Node* anchor_node = doc.FindAnchor(fragment.ToString()); // Setting to null will clear the current target. auto* target = DynamicTo<Element>(anchor_node);
diff --git a/third_party/blink/renderer/core/script_tools/model_context.cc b/third_party/blink/renderer/core/script_tools/model_context.cc index 1d5fd24a..7b5857e 100644 --- a/third_party/blink/renderer/core/script_tools/model_context.cc +++ b/third_party/blink/renderer/core/script_tools/model_context.cc
@@ -4,7 +4,9 @@ #include "third_party/blink/renderer/core/script_tools/model_context.h" +#include "base/metrics/histogram_functions.h" #include "base/task/single_thread_task_runner.h" +#include "base/time/time.h" #include "third_party/blink/public/platform/browser_interface_broker_proxy.h" #include "third_party/blink/public/web/web_script_tool_types.h" #include "third_party/blink/renderer/bindings/core/v8/capture_source_location.h" @@ -21,6 +23,7 @@ #include "third_party/blink/renderer/core/probe/core_probes.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/source_location.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" #include "third_party/blink/renderer/platform/json/json_parser.h" #include "third_party/blink/renderer/platform/json/json_values.h" #include "third_party/blink/renderer/platform/wtf/functional.h" @@ -250,6 +253,7 @@ tool_map_.insert(tool->name(), tool_data); probe::WebMCPToolAdded(document_, *tool_data); + MaybeRecordToolCount(); OnToolChange(); } @@ -494,6 +498,7 @@ base::PassKey<ModelContext>(), std::move(script_tool), declarative_tool); tool_map_.insert(name, tool_data); probe::WebMCPToolAdded(document_, *tool_data); + MaybeRecordToolCount(); OnToolChange(); } @@ -520,6 +525,24 @@ } } +void ModelContext::MaybeRecordToolCount() { + if (!will_record_tool_count_) { + will_record_tool_count_ = true; + task_runner_->PostDelayedTask( + FROM_HERE, + blink::BindOnce( + [](ModelContext* context) { + if (context) { + base::UmaHistogramCounts100( + "Blink.ModelContext.DelayedToolCount", + context->tool_map_.size()); + } + }, + WrapWeakPersistent(this)), + base::Seconds(10)); + } +} + void ModelContext::PauseExecution() { if (!script_tool_host_remote_.is_bound()) { document_->GetExecutionContext()->GetBrowserInterfaceBroker().GetInterface(
diff --git a/third_party/blink/renderer/core/script_tools/model_context.h b/third_party/blink/renderer/core/script_tools/model_context.h index 1a679474..450590de 100644 --- a/third_party/blink/renderer/core/script_tools/model_context.h +++ b/third_party/blink/renderer/core/script_tools/model_context.h
@@ -159,6 +159,7 @@ void OnToolExecuted(uint32_t execution_id, std::optional<String> result); void OnToolChange(); + void MaybeRecordToolCount(); HeapHashMap<String, Member<ToolData>> tool_map_; @@ -176,6 +177,10 @@ Member<Document> document_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; HeapMojoRemote<mojom::blink::ScriptToolHost> script_tool_host_remote_; + + // true when there is a pending or completed task to record the number + // of registered tools. + bool will_record_tool_count_ = false; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/script_tools/model_context_test.cc b/third_party/blink/renderer/core/script_tools/model_context_test.cc index d601edd..d562cb4 100644 --- a/third_party/blink/renderer/core/script_tools/model_context_test.cc +++ b/third_party/blink/renderer/core/script_tools/model_context_test.cc
@@ -8,6 +8,7 @@ #include "base/run_loop.h" #include "base/test/bind.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/run_until.h" #include "mojo/public/cpp/bindings/receiver.h" #include "testing/gtest/include/gtest/gtest.h" @@ -1197,4 +1198,64 @@ EXPECT_EQ("leave-feedback", tools[1]->BackingFormElement()->GetIdAttribute()); } +class ModelContextMetricsTest : public SimTest { + public: + ModelContextMetricsTest() + : SimTest(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} + + protected: + void EvalJsString(std::string_view script) { + MainFrame().ExecuteScript(WebScriptSource(WebString::FromUTF8(script))); + } + + private: + ScopedWebMCPForTest scoped_webmcp_{true}; +}; + +TEST_F(ModelContextMetricsTest, RecordToolCountHistogram) { + base::HistogramTester histogram_tester; + + SimRequest main_resource("https://example.com/", "text/html"); + LoadURL("https://example.com/"); + + main_resource.Complete(R"(<!DOCTYPE html> + <form + id=book-table + toolname=book-table + tooldescription="Book a table"> + </form> + <form + id=leave-feedback + toolname=leave-feedback + tooldescription="leave-feedback"> + </form> + )"); + + v8::HandleScope handle_scope(Window().GetIsolate()); + ScriptState::Scope script_scope( + ToScriptStateForMainWorld(Window().GetFrame())); + + task_environment().FastForwardBy(base::Seconds(2)); + EvalJsString(R"JS(navigator.modelContext.registerTool({ + execute: () => "true", + name: "tool1", + description: "Tool1", + }))JS"); + + task_environment().FastForwardBy(base::Seconds(2)); + for (int i = 2; i <= 4; i++) { + // clang-format off + EvalJsString(String::Format(R"JS(navigator.modelContext.registerTool({ + execute: () => "true", + name: "tool%d", + description: "Tool%d", + }))JS", i, i).Utf8()); + // clang-format on + } + + task_environment().FastForwardBy(base::Seconds(8)); + histogram_tester.ExpectUniqueSample("Blink.ModelContext.DelayedToolCount", 6, + 1); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/svg/svg_parsing_error.cc b/third_party/blink/renderer/core/svg/svg_parsing_error.cc index 4ab5957..ad5fa967 100644 --- a/third_party/blink/renderer/core/svg/svg_parsing_error.cc +++ b/third_party/blink/renderer/core/svg/svg_parsing_error.cc
@@ -94,7 +94,7 @@ if (context_start != 0) builder.Append(uchar::kHorizontalEllipsis); EscapeStringForJSON( - value.GetString().Substring(context_start, context_end - context_start), + StringView(value, context_start, context_end - context_start), &builder); if (context_end != value.length()) builder.Append(uchar::kHorizontalEllipsis);
diff --git a/third_party/blink/renderer/platform/json/json_values.cc b/third_party/blink/renderer/platform/json/json_values.cc index c00f9a6..80341c9c 100644 --- a/third_party/blink/renderer/platform/json/json_values.cc +++ b/third_party/blink/renderer/platform/json/json_values.cc
@@ -88,7 +88,7 @@ } // anonymous namespace -void EscapeStringForJSON(const String& str, StringBuilder* dst) { +void EscapeStringForJSON(const StringView& str, StringBuilder* dst) { for (unsigned i = 0; i < str.length(); ++i) { UChar c = str[i]; if (!EscapeChar(c, dst)) { @@ -102,16 +102,16 @@ } } -void DoubleQuoteStringForJSON(const String& str, StringBuilder* dst) { +void DoubleQuoteStringForJSON(const StringView& str, StringBuilder* dst) { dst->Append('"'); EscapeStringForJSON(str, dst); dst->Append('"'); } -String JSONValue::QuoteString(const String& input) { +String JSONValue::QuoteString(const StringView& input) { StringBuilder builder; DoubleQuoteStringForJSON(input, &builder); - return builder.ToString(); + return builder.ReleaseString(); } bool JSONValue::AsBoolean(bool*) const {
diff --git a/third_party/blink/renderer/platform/json/json_values.h b/third_party/blink/renderer/platform/json/json_values.h index 92dd0e9..fcb5c5b9 100644 --- a/third_party/blink/renderer/platform/json/json_values.h +++ b/third_party/blink/renderer/platform/json/json_values.h
@@ -90,7 +90,7 @@ virtual void PrettyWriteJSON(StringBuilder* output) const; virtual std::unique_ptr<JSONValue> Clone() const; - static String QuoteString(const String&); + static String QuoteString(const StringView&); protected: JSONValue() : type_(kTypeNull) {} @@ -309,8 +309,8 @@ inline constexpr char kJSONTrueString[] = "true"; inline constexpr char kJSONFalseString[] = "false"; -PLATFORM_EXPORT void EscapeStringForJSON(const String&, StringBuilder*); -void DoubleQuoteStringForJSON(const String&, StringBuilder*); +PLATFORM_EXPORT void EscapeStringForJSON(const StringView&, StringBuilder*); +void DoubleQuoteStringForJSON(const StringView&, StringBuilder*); } // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index ceb205c..418667f 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -3091,6 +3091,15 @@ name: "HTMLCommandActionsV2", status: "test", }, + // Remove support for the <command> element from the HTML parsing logic. + // This shipped in M148 and the flag can be removed in M150. + // https://issues.chromium.org/issues/491158089 + // NOTE: when this flag is removed, be sure to also remove "command" + // from third_party/blink/renderer/core/html/html_tag_names.json5. + { + name: "HTMLCommandElementRemoval", + status: "stable", + }, // Adds support for scroll commands such as 'page-up' and 'page-block-end'. // https://github.com/danielsakhapov/declarative-scroll-commands-for-html-explainer {
diff --git a/third_party/blink/renderer/platform/weborigin/kurl.cc b/third_party/blink/renderer/platform/weborigin/kurl.cc index a0f511a9..0db8f005 100644 --- a/third_party/blink/renderer/platform/weborigin/kurl.cc +++ b/third_party/blink/renderer/platform/weborigin/kurl.cc
@@ -471,15 +471,14 @@ } // namespace -bool KURL::SetProtocol(const String& protocol) { +bool KURL::SetProtocol(const StringView& protocol) { // We should remove whitespace from |protocol| according to spec, but Firefox // and Safari don't do it. // - https://url.spec.whatwg.org/#dom-url-protocol // - https://github.com/whatwg/url/issues/609 // Firefox and IE remove everything after the first ':'. - wtf_size_t separator_position = protocol.find(':'); - String new_protocol = protocol.substr(0, separator_position); + StringView new_protocol = protocol.substr(0, protocol.find(':')); StringUtf8Adaptor new_protocol_utf8(new_protocol); // If KURL is given an invalid scheme, it returns failure without modifying @@ -493,13 +492,13 @@ return false; } - DCHECK_EQ(protocol_component.begin, 0); - const wtf_size_t protocol_length = - base::checked_cast<wtf_size_t>(protocol_component.len); - const String new_protocol_canon = - String(base::span(canon_protocol.view()).first(protocol_length)); - if (SchemeRegistry::IsSpecialScheme(Protocol())) { + DCHECK_EQ(protocol_component.begin, 0); + const wtf_size_t protocol_length = + base::checked_cast<wtf_size_t>(protocol_component.len); + const String new_protocol_canon( + base::span(canon_protocol.view()).first(protocol_length)); + // https://url.spec.whatwg.org/#scheme-state // 2.1.1 If url’s scheme is a special scheme and buffer is not a special // scheme, then return.
diff --git a/third_party/blink/renderer/platform/weborigin/kurl.h b/third_party/blink/renderer/platform/weborigin/kurl.h index 7dfb90b..37e3327 100644 --- a/third_party/blink/renderer/platform/weborigin/kurl.h +++ b/third_party/blink/renderer/platform/weborigin/kurl.h
@@ -198,7 +198,7 @@ bool IsAboutSrcdocUrl() const; // Is about:srcdoc, ignoring query/ref // strings.. - bool SetProtocol(const String&); + bool SetProtocol(const StringView&); void SetHost(const String&); void RemovePort();
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 23c35ef8..7d295bb4 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -6079,10 +6079,6 @@ # Sheriff 2021-05-03 crbug.com/1205012 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html [ Pass Timeout ] - -# Sheriff 2021-05-07 -# Some plzServiceWorker and plzDedicatedWorker singled out from generic sheriff rounds above - # For SkiaRenderer on MacOS crbug.com/1208173 [ Mac ] svg/custom/svg-root-with-opacity.html [ Failure ] crbug.com/1208173 [ Mac ] virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-render-surfaces-with-rotation.html [ Failure ] @@ -6343,11 +6339,6 @@ crbug.com/1066891 virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/legacy/requestDevice/sandboxed_iframe.https.window.html [ Failure Pass Timeout ] crbug.com/1066891 virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/legacy/requestLEScan/sandboxed_iframe.https.window.html [ Failure Pass Timeout ] -# Sheriff 2021-07-13 - -# Flakes that might be caused or aggravated by PlzServiceWorker -crbug.com/996511 external/wpt/service-workers/service-worker/getregistrations.https.html [ Crash Failure Pass Timeout ] - # Test fails due to more accurate size reporting in heap snapshots. crbug.com/40778216 inspector-protocol/heap-profiler/heap-samples-in-snapshot.js [ Failure Timeout ] @@ -9609,7 +9600,7 @@ # Gardener 2026-01-12 crbug.com/475047610 [ Debug Linux ] external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse.py [ Failure ] crbug.com/475126745 [ Debug Linux ] external/wpt/mathml/presentation-markup/scripts/underover-stretchy-003.html [ Failure ] -crbug.com/465849685 [ Debug Linux ] media/media-playback-while-not-visible-permission-policy-cant-play-if-hidden.html [ Failure Pass ] +crbug.com/465849685 [ Debug Linux ] media/media-playback-while-not-visible-permission-policy-cant-play-if-hidden.html [ Failure Pass Timeout ] # linux-blink-leak-rel failure crbug.com/476257809 [ Linux ] external/wpt/fedcm/fedcm-storage-access-api-autogrant.tentative.https.sub.html [ Failure Pass Skip ]
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=uri-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=uri-expected.txt deleted file mode 100644 index 96a5f706..0000000 --- a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=uri-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -[FAIL] html5lib_tests25.html de4aa726e09215ba9c50b97d257e6c6b880107f1 - assert_equals: expected "#document\\n| <!DOCTYPE html>\\n| <html>\\n| <head>\\n| <body>\\n| <command>\\n| \\"A\\"" but got "#document\\n| <!DOCTYPE html>\\n| <html>\\n| <head>\\n| <body>\\n| <command>\\n| \\"A\\"" -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write-expected.txt deleted file mode 100644 index 96a5f706..0000000 --- a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -[FAIL] html5lib_tests25.html de4aa726e09215ba9c50b97d257e6c6b880107f1 - assert_equals: expected "#document\\n| <!DOCTYPE html>\\n| <html>\\n| <head>\\n| <body>\\n| <command>\\n| \\"A\\"" but got "#document\\n| <!DOCTYPE html>\\n| <html>\\n| <head>\\n| <body>\\n| <command>\\n| \\"A\\"" -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write_single-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write_single-expected.txt deleted file mode 100644 index 96a5f706..0000000 --- a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write_single-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -[FAIL] html5lib_tests25.html de4aa726e09215ba9c50b97d257e6c6b880107f1 - assert_equals: expected "#document\\n| <!DOCTYPE html>\\n| <html>\\n| <head>\\n| <body>\\n| <command>\\n| \\"A\\"" but got "#document\\n| <!DOCTYPE html>\\n| <html>\\n| <head>\\n| <body>\\n| <command>\\n| \\"A\\"" -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/html5lib/generated/run-tests25-data-expected.txt b/third_party/blink/web_tests/html5lib/generated/run-tests25-data-expected.txt index 28b9eae..e08f1f6 100644 --- a/third_party/blink/web_tests/html5lib/generated/run-tests25-data-expected.txt +++ b/third_party/blink/web_tests/html5lib/generated/run-tests25-data-expected.txt
@@ -1,19 +1 @@ -../resources/tests25.dat: -8 - -Test 8 of 26 in ../resources/tests25.dat failed. Input: -<!DOCTYPE html><body><command>A -Got: -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <command> -| "A" -Expected: -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <command> -| "A" +../resources/tests25.dat: PASS
diff --git a/third_party/blink/web_tests/html5lib/generated/run-tests25-write-expected.txt b/third_party/blink/web_tests/html5lib/generated/run-tests25-write-expected.txt index 28b9eae..e08f1f6 100644 --- a/third_party/blink/web_tests/html5lib/generated/run-tests25-write-expected.txt +++ b/third_party/blink/web_tests/html5lib/generated/run-tests25-write-expected.txt
@@ -1,19 +1 @@ -../resources/tests25.dat: -8 - -Test 8 of 26 in ../resources/tests25.dat failed. Input: -<!DOCTYPE html><body><command>A -Got: -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <command> -| "A" -Expected: -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <command> -| "A" +../resources/tests25.dat: PASS
diff --git a/third_party/blink/web_tests/wpt_internal/webmcp/execute_tool_change_event.html b/third_party/blink/web_tests/wpt_internal/webmcp/execute_tool_change_event.html new file mode 100644 index 0000000..212e109 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/webmcp/execute_tool_change_event.html
@@ -0,0 +1,74 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="author" href="mailto:masonf@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<form toolname="fill_text" tooldescription="Fill some text" + toolautosubmit target="hidden_iframe" action="about:blank"> + <input id="myinput" name="text" type="text"> + <input id="mynumber" name="number" type="number"> + <input id="mycheckbox" name="checkbox" type="checkbox" value="check1"> + <input id="myradio" name="radio" type="radio" value="radio1"> + <input id="myradio2" name="radio" type="radio" value="radio2"> + <textarea id="mytextarea" name="textarea"></textarea> + <select id="myselect" name="select"> + <option value="opt1">Opt 1</option> + <option value="opt2">Opt 2</option> + </select> +</form> +<iframe name=hidden_iframe style="display:none"></iframe> + +<script> +promise_test(async t => { + assert_true(!!navigator.modelContextTesting); + + let form = document.querySelector('form'); + let inputs = form.querySelectorAll('input, textarea, select'); + let events = {}; + + for (let el of inputs) { + if (el.id) { + events[el.id] = { inputFired: false, changeFired: false }; + el.addEventListener('change', () => { events[el.id].changeFired = true; }); + el.addEventListener('input', () => { events[el.id].inputFired = true; }); + } + } + + let inputArgs = JSON.stringify({ + text: "regular text", + number: 42, + checkbox: true, + radio: "radio2", + textarea: "some text area content", + select: "opt2" + }); + + await navigator.modelContextTesting.executeTool('fill_text', inputArgs); + + assert_equals(document.getElementById('myinput').value, 'regular text'); + assert_true(events['myinput'].inputFired, 'text input event should fire'); + assert_true(events['myinput'].changeFired, 'text change event should fire'); + + assert_equals(document.getElementById('mynumber').value, '42'); + assert_true(events['mynumber'].inputFired, 'number input event should fire'); + assert_true(events['mynumber'].changeFired, 'number change event should fire'); + + assert_true(document.getElementById('mycheckbox').checked); + assert_true(events['mycheckbox'].inputFired, 'checkbox input event should fire'); + assert_true(events['mycheckbox'].changeFired, 'checkbox change event should fire'); + + assert_false(document.getElementById('myradio').checked); + assert_true(document.getElementById('myradio2').checked); + assert_true(events['myradio2'].inputFired, 'radio input event should fire'); + assert_true(events['myradio2'].changeFired, 'radio change event should fire'); + + assert_equals(document.getElementById('mytextarea').value, 'some text area content'); + assert_true(events['mytextarea'].inputFired, 'textarea input event should fire'); + assert_true(events['mytextarea'].changeFired, 'textarea change event should fire'); + + assert_equals(document.getElementById('myselect').value, 'opt2'); + assert_true(events['myselect'].inputFired, 'select input event should fire'); + assert_true(events['myselect'].changeFired, 'select change event should fire'); +}, 'executeTool triggers input and change events'); +</script>
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src index b2a86d0..48f9650 160000 --- a/third_party/devtools-frontend/src +++ b/third_party/devtools-frontend/src
@@ -1 +1 @@ -Subproject commit b2a86d09d5c4fa554a9a46cb1e94cf8ab52b2d0c +Subproject commit 48f965098f72daf2d89b5ded468d3264299fb095
diff --git a/third_party/glslang/src b/third_party/glslang/src index d1f52c8..959ff4a 160000 --- a/third_party/glslang/src +++ b/third_party/glslang/src
@@ -1 +1 @@ -Subproject commit d1f52c8993a501bd52d4fbd044bfeb9ecdceb9f4 +Subproject commit 959ff4a0dba11111c5c999f1235c798431475322
diff --git a/third_party/perfetto b/third_party/perfetto index 08b509b..3a764e6 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit 08b509bdad26c91f5f34d9ff97c9362a111f3a72 +Subproject commit 3a764e6f6339ae06c19540682dad4a1a9021f072
diff --git a/third_party/readability/README.chromium b/third_party/readability/README.chromium index d48458d..1f3daf09 100644 --- a/third_party/readability/README.chromium +++ b/third_party/readability/README.chromium
@@ -29,3 +29,5 @@ - Remove <label for="id-of-removed-input"> (crbug.com/483369387). - When extracting title from JSON-LD, fallback to checking if the original document title includes the name or headline (crbug.com/487369435). +- Add recipe keywords to regexes, and refine single-child promotion to ignore + insignificant siblings and include all content (crbug.com/483735928).
diff --git a/third_party/readability/modded_src/Readability-readerable.js b/third_party/readability/modded_src/Readability-readerable.js index fa24803..2455456 100644 --- a/third_party/readability/modded_src/Readability-readerable.js +++ b/third_party/readability/modded_src/Readability-readerable.js
@@ -24,7 +24,12 @@ // Readability.js. Please keep both copies in sync. unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i, - okMaybeItsACandidate: /and|article|body|column|content|main|mathjax|shadow/i, + okMaybeItsACandidate: new RegExp( + "and|article|body|column|content|main|mathjax|" + + "shadow|recipe|ingredients|instructions|" + + "directions|steps", + "i" + ), }; function isNodeVisible(node) {
diff --git a/third_party/readability/modded_src/Readability.js b/third_party/readability/modded_src/Readability.js index 9585afb6..d8d1543e 100644 --- a/third_party/readability/modded_src/Readability.js +++ b/third_party/readability/modded_src/Readability.js
@@ -139,11 +139,20 @@ // Readability-readerable.js. Please keep both copies in sync. unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i, - okMaybeItsACandidate: - /and|article|body|column|content|main|mathjax|shadow/i, + okMaybeItsACandidate: new RegExp( + "and|article|body|column|content|main|mathjax|" + + "shadow|recipe|ingredients|instructions|" + + "directions|steps", + "i" + ), - positive: - /article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/i, + positive: new RegExp( + "article|body|content|entry|hentry|h-entry|" + + "main|page|pagination|post|text|blog|story|" + + "recipe|ingredients|instructions|" + + "directions|steps", + "i" + ), negative: /-ad-|hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|footer|gdpr|masthead|media|meta|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|widget/i, extraneous: @@ -380,6 +389,25 @@ }, /** + * Check if the provided candidate has any significant siblings. + * + * @param Element candidate The candidate element. + * @param Number threshold The content score threshold. + * @return Boolean + */ + _hasSignificantSibling(candidate, threshold) { + return this._someNode(candidate.parentNode.children, function (sibling) { + if (sibling === candidate) { + return false; + } + if (sibling.readability) { + return sibling.readability.contentScore >= threshold; + } + return sibling.textContent.trim().length > 25; + }); + }, + + /** * Iterate over a NodeList, return true if all of the provided iterate * function calls return true, false otherwise. * @@ -1433,13 +1461,19 @@ parentOfTopCandidate = parentOfTopCandidate.parentNode; } - // If the top candidate is the only child, use parent instead. This will help sibling - // joining logic when adjacent content is actually located in parent's sibling node. + // If the top candidate is the only child (or only child with + // significant content), use parent instead. This will help sibling + // joining logic when adjacent content is actually located in parent's + // sibling node. parentOfTopCandidate = topCandidate.parentNode; - while ( - parentOfTopCandidate.tagName != "BODY" && - parentOfTopCandidate.children.length == 1 - ) { + const promotionThreshold = Math.max( + 10, + topCandidate.readability.contentScore * 0.2 + ); + while (parentOfTopCandidate.tagName !== "BODY") { + if (this._hasSignificantSibling(topCandidate, promotionThreshold)) { + break; + } topCandidate = parentOfTopCandidate; parentOfTopCandidate = topCandidate.parentNode; }
diff --git a/third_party/rust/chromium_crates_io/Cargo.lock b/third_party/rust/chromium_crates_io/Cargo.lock index 625ba99..48f5897 100644 --- a/third_party/rust/chromium_crates_io/Cargo.lock +++ b/third_party/rust/chromium_crates_io/Cargo.lock
@@ -764,7 +764,7 @@ [[package]] name = "itertools" -version = "0.11.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "either",
diff --git a/third_party/rust/chromium_crates_io/Cargo.toml b/third_party/rust/chromium_crates_io/Cargo.toml index 2a894f12..bd166d0 100644 --- a/third_party/rust/chromium_crates_io/Cargo.toml +++ b/third_party/rust/chromium_crates_io/Cargo.toml
@@ -28,7 +28,7 @@ hex = "0.4" hmac-sha256 = "1.1.12" indexmap = "2.12.1" -itertools = "0.11.0" +itertools = "0.14.0" lazy_static = "1" libc = "0.2" minijinja = "2.14.0"
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.cargo_vcs_info.json deleted file mode 100644 index 432bb37..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.cargo_vcs_info.json +++ /dev/null
@@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "62a6401afd6d45e1c2aea94c05cb5c70076b2ca4" - }, - "path_in_vcs": "" -} \ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/workflows/ci.yml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/workflows/ci.yml deleted file mode 100644 index 92122cca..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/workflows/ci.yml +++ /dev/null
@@ -1,57 +0,0 @@ -name: CI - -on: - pull_request: - push: - branches: - - staging - - trying - -jobs: - check: - name: check - runs-on: ubuntu-latest - strategy: - matrix: - build: [msrv, stable] - features: - [ - "", - "--no-default-features", - "--no-default-features --features use_alloc", - "--all-targets --all-features", - ] - include: - - build: msrv - rust: 1.62.1 - - build: stable - rust: stable - exclude: - - build: msrv - # we only care about the MSRV with respect to the lib target - features: "--all-targets --all-features" - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - - run: cargo check ${{ matrix.features }} - - test: - name: test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - - run: cargo test --all-features - - # https://github.com/rust-lang/crater/blob/9ab6f9697c901c4a44025cf0a39b73ad5b37d198/.github/workflows/bors.yml#L125-L149 - end-success: - name: bors build finished - if: success() - runs-on: ubuntu-latest - needs: [check, test] - - steps: - - name: Mark the job as successful - run: exit 0
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.rustfmt.toml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.rustfmt.toml deleted file mode 100644 index 06eb57a17..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.rustfmt.toml +++ /dev/null
@@ -1,3 +0,0 @@ -# Temporarily disable rustfmt completely to avoid conflicts of newly formatted -# code with old PRs. -ignore = ["/"]
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml deleted file mode 100644 index df3cbd8..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml +++ /dev/null
@@ -1,101 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. -# -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2018" -rust-version = "1.36.0" -name = "itertools" -version = "0.11.0" -authors = ["bluss"] -exclude = ["/bors.toml"] -description = "Extra iterator adaptors, iterator methods, free functions, and macros." -documentation = "https://docs.rs/itertools/" -readme = "README.md" -keywords = [ - "iterator", - "data-structure", - "zip", - "product", - "group-by", -] -categories = [ - "algorithms", - "rust-patterns", -] -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-itertools/itertools" - -[profile.bench] -debug = 2 - -[lib] -test = false -bench = false - -[[bench]] -name = "tuple_combinations" -harness = false - -[[bench]] -name = "tuples" -harness = false - -[[bench]] -name = "fold_specialization" -harness = false - -[[bench]] -name = "combinations_with_replacement" -harness = false - -[[bench]] -name = "tree_fold1" -harness = false - -[[bench]] -name = "bench1" -harness = false - -[[bench]] -name = "combinations" -harness = false - -[[bench]] -name = "powerset" -harness = false - -[dependencies.either] -version = "1.0" -default-features = false - -[dev-dependencies.criterion] -version = "0.4.0" - -[dev-dependencies.paste] -version = "1.0.0" - -[dev-dependencies.permutohedron] -version = "0.2" - -[dev-dependencies.quickcheck] -version = "0.9" -default_features = false - -[dev-dependencies.rand] -version = "0.7" - -[features] -default = ["use_std"] -use_alloc = [] -use_std = [ - "use_alloc", - "either/use_std", -]
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/README.md b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/README.md deleted file mode 100644 index 626d10d..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/README.md +++ /dev/null
@@ -1,44 +0,0 @@ -# Itertools - -Extra iterator adaptors, functions and macros. - -Please read the [API documentation here](https://docs.rs/itertools/). - -[](https://github.com/rust-itertools/itertools/actions) -[](https://crates.io/crates/itertools) - -How to use with Cargo: - -```toml -[dependencies] -itertools = "0.11.0" -``` - -How to use in your crate: - -```rust -use itertools::Itertools; -``` - -## How to contribute - -- Fix a bug or implement a new thing -- Include tests for your new feature, preferably a QuickCheck test -- Make a Pull Request - -For new features, please first consider filing a PR to [rust-lang/rust](https://github.com/rust-lang/rust), -adding your new feature to the `Iterator` trait of the standard library, if you believe it is reasonable. -If it isn't accepted there, proposing it for inclusion in ``itertools`` is a good idea. -The reason for doing is this is so that we avoid future breakage as with ``.flatten()``. -However, if your feature involves heap allocation, such as storing elements in a ``Vec<T>``, -then it can't be accepted into ``libcore``, and you should propose it for ``itertools`` directly instead. - -## License - -Dual-licensed to be compatible with the Rust project. - -Licensed under the Apache License, Version 2.0 -https://www.apache.org/licenses/LICENSE-2.0 or the MIT license -https://opensource.org/licenses/MIT, at your -option. This file may not be copied, modified, or distributed -except according to those terms.
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/mod.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/mod.rs deleted file mode 100644 index 52fe5cc3..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/mod.rs +++ /dev/null
@@ -1,2 +0,0 @@ -pub use self::zipslices::ZipSlices; -mod zipslices;
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/zipslices.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/zipslices.rs deleted file mode 100644 index 633be59..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/zipslices.rs +++ /dev/null
@@ -1,188 +0,0 @@ -use std::cmp; - -// Note: There are different ways to implement ZipSlices. -// This version performed the best in benchmarks. -// -// I also implemented a version with three pointers (tptr, tend, uptr), -// that mimiced slice::Iter and only checked bounds by using tptr == tend, -// but that was inferior to this solution. - -/// An iterator which iterates two slices simultaneously. -/// -/// `ZipSlices` acts like a double-ended `.zip()` iterator. -/// -/// It was intended to be more efficient than `.zip()`, and it was, then -/// rustc changed how it optimizes so it can not promise improved performance -/// at this time. -/// -/// Note that elements past the end of the shortest of the two slices are ignored. -/// -/// Iterator element type for `ZipSlices<T, U>` is `(T::Item, U::Item)`. For example, -/// for a `ZipSlices<&'a [A], &'b mut [B]>`, the element type is `(&'a A, &'b mut B)`. -#[derive(Clone)] -pub struct ZipSlices<T, U> { - t: T, - u: U, - len: usize, - index: usize, -} - -impl<'a, 'b, A, B> ZipSlices<&'a [A], &'b [B]> { - /// Create a new `ZipSlices` from slices `a` and `b`. - /// - /// Act like a double-ended `.zip()` iterator, but more efficiently. - /// - /// Note that elements past the end of the shortest of the two slices are ignored. - #[inline(always)] - pub fn new(a: &'a [A], b: &'b [B]) -> Self { - let minl = cmp::min(a.len(), b.len()); - ZipSlices { - t: a, - u: b, - len: minl, - index: 0, - } - } -} - -impl<T, U> ZipSlices<T, U> - where T: Slice, - U: Slice -{ - /// Create a new `ZipSlices` from slices `a` and `b`. - /// - /// Act like a double-ended `.zip()` iterator, but more efficiently. - /// - /// Note that elements past the end of the shortest of the two slices are ignored. - #[inline(always)] - pub fn from_slices(a: T, b: U) -> Self { - let minl = cmp::min(a.len(), b.len()); - ZipSlices { - t: a, - u: b, - len: minl, - index: 0, - } - } -} - -impl<T, U> Iterator for ZipSlices<T, U> - where T: Slice, - U: Slice -{ - type Item = (T::Item, U::Item); - - #[inline(always)] - fn next(&mut self) -> Option<Self::Item> { - unsafe { - if self.index >= self.len { - None - } else { - let i = self.index; - self.index += 1; - Some(( - self.t.get_unchecked(i), - self.u.get_unchecked(i))) - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - let len = self.len - self.index; - (len, Some(len)) - } -} - -impl<T, U> DoubleEndedIterator for ZipSlices<T, U> - where T: Slice, - U: Slice -{ - #[inline(always)] - fn next_back(&mut self) -> Option<Self::Item> { - unsafe { - if self.index >= self.len { - None - } else { - self.len -= 1; - let i = self.len; - Some(( - self.t.get_unchecked(i), - self.u.get_unchecked(i))) - } - } - } -} - -impl<T, U> ExactSizeIterator for ZipSlices<T, U> - where T: Slice, - U: Slice -{} - -unsafe impl<T, U> Slice for ZipSlices<T, U> - where T: Slice, - U: Slice -{ - type Item = (T::Item, U::Item); - - fn len(&self) -> usize { - self.len - self.index - } - - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item { - (self.t.get_unchecked(i), - self.u.get_unchecked(i)) - } -} - -/// A helper trait to let `ZipSlices` accept both `&[T]` and `&mut [T]`. -/// -/// Unsafe trait because: -/// -/// - Implementors must guarantee that `get_unchecked` is valid for all indices `0..len()`. -pub unsafe trait Slice { - /// The type of a reference to the slice's elements - type Item; - #[doc(hidden)] - fn len(&self) -> usize; - #[doc(hidden)] - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item; -} - -unsafe impl<'a, T> Slice for &'a [T] { - type Item = &'a T; - #[inline(always)] - fn len(&self) -> usize { (**self).len() } - #[inline(always)] - unsafe fn get_unchecked(&mut self, i: usize) -> &'a T { - debug_assert!(i < self.len()); - (**self).get_unchecked(i) - } -} - -unsafe impl<'a, T> Slice for &'a mut [T] { - type Item = &'a mut T; - #[inline(always)] - fn len(&self) -> usize { (**self).len() } - #[inline(always)] - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut T { - debug_assert!(i < self.len()); - // override the lifetime constraints of &mut &'a mut [T] - (*(*self as *mut [T])).get_unchecked_mut(i) - } -} - -#[test] -fn zipslices() { - - let xs = [1, 2, 3, 4, 5, 6]; - let ys = [1, 2, 3, 7]; - ::itertools::assert_equal(ZipSlices::new(&xs, &ys), xs.iter().zip(&ys)); - - let xs = [1, 2, 3, 4, 5, 6]; - let mut ys = [0; 6]; - for (x, y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) { - *y = *x; - } - ::itertools::assert_equal(&xs, &ys); -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/powerset.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/powerset.rs deleted file mode 100644 index 074550b..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/powerset.rs +++ /dev/null
@@ -1,44 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use itertools::Itertools; - -// Keep aggregate generated elements the same, regardless of powerset length. -const TOTAL_ELEMENTS: usize = 1 << 12; -const fn calc_iters(n: usize) -> usize { - TOTAL_ELEMENTS / (1 << n) -} - -fn powerset_n(c: &mut Criterion, n: usize) { - let id = format!("powerset {}", n); - c.bench_function(id.as_str(), move |b| { - b.iter(|| { - for _ in 0..calc_iters(n) { - for elt in (0..n).powerset() { - black_box(elt); - } - } - }) - }); -} - -fn powerset_0(c: &mut Criterion) { powerset_n(c, 0); } - -fn powerset_1(c: &mut Criterion) { powerset_n(c, 1); } - -fn powerset_2(c: &mut Criterion) { powerset_n(c, 2); } - -fn powerset_4(c: &mut Criterion) { powerset_n(c, 4); } - -fn powerset_8(c: &mut Criterion) { powerset_n(c, 8); } - -fn powerset_12(c: &mut Criterion) { powerset_n(c, 12); } - -criterion_group!( - benches, - powerset_0, - powerset_1, - powerset_2, - powerset_4, - powerset_8, - powerset_12, -); -criterion_main!(benches); \ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tree_fold1.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tree_fold1.rs deleted file mode 100644 index f12995d..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tree_fold1.rs +++ /dev/null
@@ -1,144 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use itertools::{Itertools, cloned}; - -trait IterEx : Iterator { - // Another efficient implementation against which to compare, - // but needs `std` so is less desirable. - fn tree_fold1_vec<F>(self, mut f: F) -> Option<Self::Item> - where F: FnMut(Self::Item, Self::Item) -> Self::Item, - Self: Sized, - { - let hint = self.size_hint().0; - let cap = std::mem::size_of::<usize>() * 8 - hint.leading_zeros() as usize; - let mut stack = Vec::with_capacity(cap); - self.enumerate().for_each(|(mut i, mut x)| { - while (i & 1) != 0 { - x = f(stack.pop().unwrap(), x); - i >>= 1; - } - stack.push(x); - }); - stack.into_iter().fold1(f) - } -} -impl<T:Iterator> IterEx for T {} - -macro_rules! def_benchs { - ($N:expr, - $FUN:ident, - $BENCH_NAME:ident, - ) => ( - mod $BENCH_NAME { - use super::*; - - pub fn sum(c: &mut Criterion) { - let v: Vec<u32> = (0.. $N).collect(); - - c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " sum"), move |b| { - b.iter(|| { - cloned(&v).$FUN(|x, y| x + y) - }) - }); - } - - pub fn complex_iter(c: &mut Criterion) { - let u = (3..).take($N / 2); - let v = (5..).take($N / 2); - let it = u.chain(v); - - c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " complex iter"), move |b| { - b.iter(|| { - it.clone().map(|x| x as f32).$FUN(f32::atan2) - }) - }); - } - - pub fn string_format(c: &mut Criterion) { - // This goes quadratic with linear `fold1`, so use a smaller - // size to not waste too much time in travis. The allocations - // in here are so expensive anyway that it'll still take - // way longer per iteration than the other two benchmarks. - let v: Vec<u32> = (0.. ($N/4)).collect(); - - c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " string format"), move |b| { - b.iter(|| { - cloned(&v).map(|x| x.to_string()).$FUN(|x, y| format!("{} + {}", x, y)) - }) - }); - } - } - - criterion_group!( - $BENCH_NAME, - $BENCH_NAME::sum, - $BENCH_NAME::complex_iter, - $BENCH_NAME::string_format, - ); - ) -} - -def_benchs!{ - 10_000, - fold1, - fold1_10k, -} - -def_benchs!{ - 10_000, - tree_fold1, - tree_fold1_stack_10k, -} - -def_benchs!{ - 10_000, - tree_fold1_vec, - tree_fold1_vec_10k, -} - -def_benchs!{ - 100, - fold1, - fold1_100, -} - -def_benchs!{ - 100, - tree_fold1, - tree_fold1_stack_100, -} - -def_benchs!{ - 100, - tree_fold1_vec, - tree_fold1_vec_100, -} - -def_benchs!{ - 8, - fold1, - fold1_08, -} - -def_benchs!{ - 8, - tree_fold1, - tree_fold1_stack_08, -} - -def_benchs!{ - 8, - tree_fold1_vec, - tree_fold1_vec_08, -} - -criterion_main!( - fold1_10k, - tree_fold1_stack_10k, - tree_fold1_vec_10k, - fold1_100, - tree_fold1_stack_100, - tree_fold1_vec_100, - fold1_08, - tree_fold1_stack_08, - tree_fold1_vec_08, -);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/coalesce.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/coalesce.rs deleted file mode 100644 index 3df7cc5..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/coalesce.rs +++ /dev/null
@@ -1,235 +0,0 @@ -use std::fmt; -use std::iter::FusedIterator; - -use crate::size_hint; - -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct CoalesceBy<I, F, T> -where - I: Iterator, -{ - iter: I, - last: Option<T>, - f: F, -} - -impl<I: Clone, F: Clone, T: Clone> Clone for CoalesceBy<I, F, T> -where - I: Iterator, -{ - clone_fields!(last, iter, f); -} - -impl<I, F, T> fmt::Debug for CoalesceBy<I, F, T> -where - I: Iterator + fmt::Debug, - T: fmt::Debug, -{ - debug_fmt_fields!(CoalesceBy, iter); -} - -pub trait CoalescePredicate<Item, T> { - fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)>; -} - -impl<I, F, T> Iterator for CoalesceBy<I, F, T> -where - I: Iterator, - F: CoalescePredicate<I::Item, T>, -{ - type Item = T; - - fn next(&mut self) -> Option<Self::Item> { - // this fuses the iterator - let last = self.last.take()?; - - let self_last = &mut self.last; - let self_f = &mut self.f; - Some( - self.iter - .try_fold(last, |last, next| match self_f.coalesce_pair(last, next) { - Ok(joined) => Ok(joined), - Err((last_, next_)) => { - *self_last = Some(next_); - Err(last_) - } - }) - .unwrap_or_else(|x| x), - ) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), self.last.is_some() as usize); - ((low > 0) as usize, hi) - } - - fn fold<Acc, FnAcc>(self, acc: Acc, mut fn_acc: FnAcc) -> Acc - where - FnAcc: FnMut(Acc, Self::Item) -> Acc, - { - if let Some(last) = self.last { - let mut f = self.f; - let (last, acc) = self.iter.fold((last, acc), |(last, acc), elt| { - match f.coalesce_pair(last, elt) { - Ok(joined) => (joined, acc), - Err((last_, next_)) => (next_, fn_acc(acc, last_)), - } - }); - fn_acc(acc, last) - } else { - acc - } - } -} - -impl<I: Iterator, F: CoalescePredicate<I::Item, T>, T> FusedIterator for CoalesceBy<I, F, T> {} - -/// An iterator adaptor that may join together adjacent elements. -/// -/// See [`.coalesce()`](crate::Itertools::coalesce) for more information. -pub type Coalesce<I, F> = CoalesceBy<I, F, <I as Iterator>::Item>; - -impl<F, Item, T> CoalescePredicate<Item, T> for F -where - F: FnMut(T, Item) -> Result<T, (T, T)>, -{ - fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)> { - self(t, item) - } -} - -/// Create a new `Coalesce`. -pub fn coalesce<I, F>(mut iter: I, f: F) -> Coalesce<I, F> -where - I: Iterator, -{ - Coalesce { - last: iter.next(), - iter, - f, - } -} - -/// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function. -/// -/// See [`.dedup_by()`](crate::Itertools::dedup_by) or [`.dedup()`](crate::Itertools::dedup) for more information. -pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, <I as Iterator>::Item>; - -#[derive(Clone)] -pub struct DedupPred2CoalescePred<DP>(DP); - -impl<DP> fmt::Debug for DedupPred2CoalescePred<DP> { - debug_fmt_fields!(DedupPred2CoalescePred,); -} - -pub trait DedupPredicate<T> { - // TODO replace by Fn(&T, &T)->bool once Rust supports it - fn dedup_pair(&mut self, a: &T, b: &T) -> bool; -} - -impl<DP, T> CoalescePredicate<T, T> for DedupPred2CoalescePred<DP> -where - DP: DedupPredicate<T>, -{ - fn coalesce_pair(&mut self, t: T, item: T) -> Result<T, (T, T)> { - if self.0.dedup_pair(&t, &item) { - Ok(t) - } else { - Err((t, item)) - } - } -} - -#[derive(Clone, Debug)] -pub struct DedupEq; - -impl<T: PartialEq> DedupPredicate<T> for DedupEq { - fn dedup_pair(&mut self, a: &T, b: &T) -> bool { - a == b - } -} - -impl<T, F: FnMut(&T, &T) -> bool> DedupPredicate<T> for F { - fn dedup_pair(&mut self, a: &T, b: &T) -> bool { - self(a, b) - } -} - -/// Create a new `DedupBy`. -pub fn dedup_by<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupBy<I, Pred> -where - I: Iterator, -{ - DedupBy { - last: iter.next(), - iter, - f: DedupPred2CoalescePred(dedup_pred), - } -} - -/// An iterator adaptor that removes repeated duplicates. -/// -/// See [`.dedup()`](crate::Itertools::dedup) for more information. -pub type Dedup<I> = DedupBy<I, DedupEq>; - -/// Create a new `Dedup`. -pub fn dedup<I>(iter: I) -> Dedup<I> -where - I: Iterator, -{ - dedup_by(iter, DedupEq) -} - -/// An iterator adaptor that removes repeated duplicates, while keeping a count of how many -/// repeated elements were present. This will determine equality using a comparison function. -/// -/// See [`.dedup_by_with_count()`](crate::Itertools::dedup_by_with_count) or -/// [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information. -pub type DedupByWithCount<I, Pred> = - CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, (usize, <I as Iterator>::Item)>; - -#[derive(Clone, Debug)] -pub struct DedupPredWithCount2CoalescePred<DP>(DP); - -impl<DP, T> CoalescePredicate<T, (usize, T)> for DedupPredWithCount2CoalescePred<DP> -where - DP: DedupPredicate<T>, -{ - fn coalesce_pair( - &mut self, - (c, t): (usize, T), - item: T, - ) -> Result<(usize, T), ((usize, T), (usize, T))> { - if self.0.dedup_pair(&t, &item) { - Ok((c + 1, t)) - } else { - Err(((c, t), (1, item))) - } - } -} - -/// An iterator adaptor that removes repeated duplicates, while keeping a count of how many -/// repeated elements were present. -/// -/// See [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information. -pub type DedupWithCount<I> = DedupByWithCount<I, DedupEq>; - -/// Create a new `DedupByWithCount`. -pub fn dedup_by_with_count<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupByWithCount<I, Pred> -where - I: Iterator, -{ - DedupByWithCount { - last: iter.next().map(|v| (1, v)), - iter, - f: DedupPredWithCount2CoalescePred(dedup_pred), - } -} - -/// Create a new `DedupWithCount`. -pub fn dedup_with_count<I>(iter: I) -> DedupWithCount<I> -where - I: Iterator, -{ - dedup_by_with_count(iter, DedupEq) -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/mod.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/mod.rs deleted file mode 100644 index 1695bbd..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/mod.rs +++ /dev/null
@@ -1,1151 +0,0 @@ -//! Licensed under the Apache License, Version 2.0 -//! <https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -//! <https://opensource.org/licenses/MIT>, at your -//! option. This file may not be copied, modified, or distributed -//! except according to those terms. - -mod coalesce; -mod map; -mod multi_product; -pub use self::coalesce::*; -pub use self::map::{map_into, map_ok, MapInto, MapOk}; -#[allow(deprecated)] -pub use self::map::MapResults; -#[cfg(feature = "use_alloc")] -pub use self::multi_product::*; - -use std::fmt; -use std::iter::{Fuse, Peekable, FromIterator, FusedIterator}; -use std::marker::PhantomData; -use crate::size_hint; - -/// An iterator adaptor that alternates elements from two iterators until both -/// run out. -/// -/// This iterator is *fused*. -/// -/// See [`.interleave()`](crate::Itertools::interleave) for more information. -#[derive(Clone, Debug)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Interleave<I, J> { - a: Fuse<I>, - b: Fuse<J>, - flag: bool, -} - -/// Create an iterator that interleaves elements in `i` and `j`. -/// -/// [`IntoIterator`] enabled version of `[Itertools::interleave]`. -pub fn interleave<I, J>(i: I, j: J) -> Interleave<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter> - where I: IntoIterator, - J: IntoIterator<Item = I::Item> -{ - Interleave { - a: i.into_iter().fuse(), - b: j.into_iter().fuse(), - flag: false, - } -} - -impl<I, J> Iterator for Interleave<I, J> - where I: Iterator, - J: Iterator<Item = I::Item> -{ - type Item = I::Item; - #[inline] - fn next(&mut self) -> Option<Self::Item> { - self.flag = !self.flag; - if self.flag { - match self.a.next() { - None => self.b.next(), - r => r, - } - } else { - match self.b.next() { - None => self.a.next(), - r => r, - } - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - size_hint::add(self.a.size_hint(), self.b.size_hint()) - } -} - -impl<I, J> FusedIterator for Interleave<I, J> - where I: Iterator, - J: Iterator<Item = I::Item> -{} - -/// An iterator adaptor that alternates elements from the two iterators until -/// one of them runs out. -/// -/// This iterator is *fused*. -/// -/// See [`.interleave_shortest()`](crate::Itertools::interleave_shortest) -/// for more information. -#[derive(Clone, Debug)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct InterleaveShortest<I, J> - where I: Iterator, - J: Iterator<Item = I::Item> -{ - it0: I, - it1: J, - phase: bool, // false ==> it0, true ==> it1 -} - -/// Create a new `InterleaveShortest` iterator. -pub fn interleave_shortest<I, J>(a: I, b: J) -> InterleaveShortest<I, J> - where I: Iterator, - J: Iterator<Item = I::Item> -{ - InterleaveShortest { - it0: a, - it1: b, - phase: false, - } -} - -impl<I, J> Iterator for InterleaveShortest<I, J> - where I: Iterator, - J: Iterator<Item = I::Item> -{ - type Item = I::Item; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - let e = if self.phase { self.it1.next() } else { self.it0.next() }; - if e.is_some() { - self.phase = !self.phase; - } - e - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - let (curr_hint, next_hint) = { - let it0_hint = self.it0.size_hint(); - let it1_hint = self.it1.size_hint(); - if self.phase { - (it1_hint, it0_hint) - } else { - (it0_hint, it1_hint) - } - }; - let (curr_lower, curr_upper) = curr_hint; - let (next_lower, next_upper) = next_hint; - let (combined_lower, combined_upper) = - size_hint::mul_scalar(size_hint::min(curr_hint, next_hint), 2); - let lower = - if curr_lower > next_lower { - combined_lower + 1 - } else { - combined_lower - }; - let upper = { - let extra_elem = match (curr_upper, next_upper) { - (_, None) => false, - (None, Some(_)) => true, - (Some(curr_max), Some(next_max)) => curr_max > next_max, - }; - if extra_elem { - combined_upper.and_then(|x| x.checked_add(1)) - } else { - combined_upper - } - }; - (lower, upper) - } -} - -impl<I, J> FusedIterator for InterleaveShortest<I, J> - where I: FusedIterator, - J: FusedIterator<Item = I::Item> -{} - -#[derive(Clone, Debug)] -/// An iterator adaptor that allows putting back a single -/// item to the front of the iterator. -/// -/// Iterator element type is `I::Item`. -pub struct PutBack<I> - where I: Iterator -{ - top: Option<I::Item>, - iter: I, -} - -/// Create an iterator where you can put back a single item -pub fn put_back<I>(iterable: I) -> PutBack<I::IntoIter> - where I: IntoIterator -{ - PutBack { - top: None, - iter: iterable.into_iter(), - } -} - -impl<I> PutBack<I> - where I: Iterator -{ - /// put back value `value` (builder method) - pub fn with_value(mut self, value: I::Item) -> Self { - self.put_back(value); - self - } - - /// Split the `PutBack` into its parts. - #[inline] - pub fn into_parts(self) -> (Option<I::Item>, I) { - let PutBack{top, iter} = self; - (top, iter) - } - - /// Put back a single value to the front of the iterator. - /// - /// If a value is already in the put back slot, it is overwritten. - #[inline] - pub fn put_back(&mut self, x: I::Item) { - self.top = Some(x); - } -} - -impl<I> Iterator for PutBack<I> - where I: Iterator -{ - type Item = I::Item; - #[inline] - fn next(&mut self) -> Option<Self::Item> { - match self.top { - None => self.iter.next(), - ref mut some => some.take(), - } - } - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - // Not ExactSizeIterator because size may be larger than usize - size_hint::add_scalar(self.iter.size_hint(), self.top.is_some() as usize) - } - - fn count(self) -> usize { - self.iter.count() + (self.top.is_some() as usize) - } - - fn last(self) -> Option<Self::Item> { - self.iter.last().or(self.top) - } - - fn nth(&mut self, n: usize) -> Option<Self::Item> { - match self.top { - None => self.iter.nth(n), - ref mut some => { - if n == 0 { - some.take() - } else { - *some = None; - self.iter.nth(n - 1) - } - } - } - } - - fn all<G>(&mut self, mut f: G) -> bool - where G: FnMut(Self::Item) -> bool - { - if let Some(elt) = self.top.take() { - if !f(elt) { - return false; - } - } - self.iter.all(f) - } - - fn fold<Acc, G>(mut self, init: Acc, mut f: G) -> Acc - where G: FnMut(Acc, Self::Item) -> Acc, - { - let mut accum = init; - if let Some(elt) = self.top.take() { - accum = f(accum, elt); - } - self.iter.fold(accum, f) - } -} - -#[derive(Debug, Clone)] -/// An iterator adaptor that iterates over the cartesian product of -/// the element sets of two iterators `I` and `J`. -/// -/// Iterator element type is `(I::Item, J::Item)`. -/// -/// See [`.cartesian_product()`](crate::Itertools::cartesian_product) for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Product<I, J> - where I: Iterator -{ - a: I, - a_cur: Option<I::Item>, - b: J, - b_orig: J, -} - -/// Create a new cartesian product iterator -/// -/// Iterator element type is `(I::Item, J::Item)`. -pub fn cartesian_product<I, J>(mut i: I, j: J) -> Product<I, J> - where I: Iterator, - J: Clone + Iterator, - I::Item: Clone -{ - Product { - a_cur: i.next(), - a: i, - b: j.clone(), - b_orig: j, - } -} - -impl<I, J> Iterator for Product<I, J> - where I: Iterator, - J: Clone + Iterator, - I::Item: Clone -{ - type Item = (I::Item, J::Item); - - fn next(&mut self) -> Option<Self::Item> { - let elt_b = match self.b.next() { - None => { - self.b = self.b_orig.clone(); - match self.b.next() { - None => return None, - Some(x) => { - self.a_cur = self.a.next(); - x - } - } - } - Some(x) => x - }; - self.a_cur.as_ref().map(|a| (a.clone(), elt_b)) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - let has_cur = self.a_cur.is_some() as usize; - // Not ExactSizeIterator because size may be larger than usize - let (b_min, b_max) = self.b.size_hint(); - - // Compute a * b_orig + b for both lower and upper bound - size_hint::add( - size_hint::mul(self.a.size_hint(), self.b_orig.size_hint()), - (b_min * has_cur, b_max.map(move |x| x * has_cur))) - } - - fn fold<Acc, G>(mut self, mut accum: Acc, mut f: G) -> Acc - where G: FnMut(Acc, Self::Item) -> Acc, - { - // use a split loop to handle the loose a_cur as well as avoiding to - // clone b_orig at the end. - if let Some(mut a) = self.a_cur.take() { - let mut b = self.b; - loop { - accum = b.fold(accum, |acc, elt| f(acc, (a.clone(), elt))); - - // we can only continue iterating a if we had a first element; - if let Some(next_a) = self.a.next() { - b = self.b_orig.clone(); - a = next_a; - } else { - break; - } - } - } - accum - } -} - -impl<I, J> FusedIterator for Product<I, J> - where I: FusedIterator, - J: Clone + FusedIterator, - I::Item: Clone -{} - -/// A “meta iterator adaptor”. Its closure receives a reference to the iterator -/// and may pick off as many elements as it likes, to produce the next iterator element. -/// -/// Iterator element type is *X*, if the return type of `F` is *Option\<X\>*. -/// -/// See [`.batching()`](crate::Itertools::batching) for more information. -#[derive(Clone)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Batching<I, F> { - f: F, - iter: I, -} - -impl<I, F> fmt::Debug for Batching<I, F> where I: fmt::Debug { - debug_fmt_fields!(Batching, iter); -} - -/// Create a new Batching iterator. -pub fn batching<I, F>(iter: I, f: F) -> Batching<I, F> { - Batching { f, iter } -} - -impl<B, F, I> Iterator for Batching<I, F> - where I: Iterator, - F: FnMut(&mut I) -> Option<B> -{ - type Item = B; - #[inline] - fn next(&mut self) -> Option<Self::Item> { - (self.f)(&mut self.iter) - } -} - -/// An iterator adaptor that steps a number elements in the base iterator -/// for each iteration. -/// -/// The iterator steps by yielding the next element from the base iterator, -/// then skipping forward *n-1* elements. -/// -/// See [`.step()`](crate::Itertools::step) for more information. -#[deprecated(note="Use std .step_by() instead", since="0.8.0")] -#[allow(deprecated)] -#[derive(Clone, Debug)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Step<I> { - iter: Fuse<I>, - skip: usize, -} - -/// Create a `Step` iterator. -/// -/// **Panics** if the step is 0. -#[allow(deprecated)] -pub fn step<I>(iter: I, step: usize) -> Step<I> - where I: Iterator -{ - assert!(step != 0); - Step { - iter: iter.fuse(), - skip: step - 1, - } -} - -#[allow(deprecated)] -impl<I> Iterator for Step<I> - where I: Iterator -{ - type Item = I::Item; - #[inline] - fn next(&mut self) -> Option<Self::Item> { - let elt = self.iter.next(); - if self.skip > 0 { - self.iter.nth(self.skip - 1); - } - elt - } - - fn size_hint(&self) -> (usize, Option<usize>) { - let (low, high) = self.iter.size_hint(); - let div = |x: usize| { - if x == 0 { - 0 - } else { - 1 + (x - 1) / (self.skip + 1) - } - }; - (div(low), high.map(div)) - } -} - -// known size -#[allow(deprecated)] -impl<I> ExactSizeIterator for Step<I> - where I: ExactSizeIterator -{} - -pub trait MergePredicate<T> { - fn merge_pred(&mut self, a: &T, b: &T) -> bool; -} - -#[derive(Clone, Debug)] -pub struct MergeLte; - -impl<T: PartialOrd> MergePredicate<T> for MergeLte { - fn merge_pred(&mut self, a: &T, b: &T) -> bool { - a <= b - } -} - -/// An iterator adaptor that merges the two base iterators in ascending order. -/// If both base iterators are sorted (ascending), the result is sorted. -/// -/// Iterator element type is `I::Item`. -/// -/// See [`.merge()`](crate::Itertools::merge_by) for more information. -pub type Merge<I, J> = MergeBy<I, J, MergeLte>; - -/// Create an iterator that merges elements in `i` and `j`. -/// -/// [`IntoIterator`] enabled version of [`Itertools::merge`](crate::Itertools::merge). -/// -/// ``` -/// use itertools::merge; -/// -/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) { -/// /* loop body */ -/// } -/// ``` -pub fn merge<I, J>(i: I, j: J) -> Merge<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter> - where I: IntoIterator, - J: IntoIterator<Item = I::Item>, - I::Item: PartialOrd -{ - merge_by_new(i, j, MergeLte) -} - -/// An iterator adaptor that merges the two base iterators in ascending order. -/// If both base iterators are sorted (ascending), the result is sorted. -/// -/// Iterator element type is `I::Item`. -/// -/// See [`.merge_by()`](crate::Itertools::merge_by) for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct MergeBy<I, J, F> - where I: Iterator, - J: Iterator<Item = I::Item> -{ - a: Peekable<I>, - b: Peekable<J>, - fused: Option<bool>, - cmp: F, -} - -impl<I, J, F> fmt::Debug for MergeBy<I, J, F> - where I: Iterator + fmt::Debug, J: Iterator<Item = I::Item> + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(MergeBy, a, b); -} - -impl<T, F: FnMut(&T, &T)->bool> MergePredicate<T> for F { - fn merge_pred(&mut self, a: &T, b: &T) -> bool { - self(a, b) - } -} - -/// Create a `MergeBy` iterator. -pub fn merge_by_new<I, J, F>(a: I, b: J, cmp: F) -> MergeBy<I::IntoIter, J::IntoIter, F> - where I: IntoIterator, - J: IntoIterator<Item = I::Item>, - F: MergePredicate<I::Item>, -{ - MergeBy { - a: a.into_iter().peekable(), - b: b.into_iter().peekable(), - fused: None, - cmp, - } -} - -impl<I, J, F> Clone for MergeBy<I, J, F> - where I: Iterator, - J: Iterator<Item = I::Item>, - Peekable<I>: Clone, - Peekable<J>: Clone, - F: Clone -{ - clone_fields!(a, b, fused, cmp); -} - -impl<I, J, F> Iterator for MergeBy<I, J, F> - where I: Iterator, - J: Iterator<Item = I::Item>, - F: MergePredicate<I::Item> -{ - type Item = I::Item; - - fn next(&mut self) -> Option<Self::Item> { - let less_than = match self.fused { - Some(lt) => lt, - None => match (self.a.peek(), self.b.peek()) { - (Some(a), Some(b)) => self.cmp.merge_pred(a, b), - (Some(_), None) => { - self.fused = Some(true); - true - } - (None, Some(_)) => { - self.fused = Some(false); - false - } - (None, None) => return None, - } - }; - if less_than { - self.a.next() - } else { - self.b.next() - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - // Not ExactSizeIterator because size may be larger than usize - size_hint::add(self.a.size_hint(), self.b.size_hint()) - } -} - -impl<I, J, F> FusedIterator for MergeBy<I, J, F> - where I: FusedIterator, - J: FusedIterator<Item = I::Item>, - F: MergePredicate<I::Item> -{} - -/// An iterator adaptor that borrows from a `Clone`-able iterator -/// to only pick off elements while the predicate returns `true`. -/// -/// See [`.take_while_ref()`](crate::Itertools::take_while_ref) for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct TakeWhileRef<'a, I: 'a, F> { - iter: &'a mut I, - f: F, -} - -impl<'a, I, F> fmt::Debug for TakeWhileRef<'a, I, F> - where I: Iterator + fmt::Debug, -{ - debug_fmt_fields!(TakeWhileRef, iter); -} - -/// Create a new `TakeWhileRef` from a reference to clonable iterator. -pub fn take_while_ref<I, F>(iter: &mut I, f: F) -> TakeWhileRef<I, F> - where I: Iterator + Clone -{ - TakeWhileRef { iter, f } -} - -impl<'a, I, F> Iterator for TakeWhileRef<'a, I, F> - where I: Iterator + Clone, - F: FnMut(&I::Item) -> bool -{ - type Item = I::Item; - - fn next(&mut self) -> Option<Self::Item> { - let old = self.iter.clone(); - match self.iter.next() { - None => None, - Some(elt) => { - if (self.f)(&elt) { - Some(elt) - } else { - *self.iter = old; - None - } - } - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - (0, self.iter.size_hint().1) - } -} - -/// An iterator adaptor that filters `Option<A>` iterator elements -/// and produces `A`. Stops on the first `None` encountered. -/// -/// See [`.while_some()`](crate::Itertools::while_some) for more information. -#[derive(Clone, Debug)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct WhileSome<I> { - iter: I, -} - -/// Create a new `WhileSome<I>`. -pub fn while_some<I>(iter: I) -> WhileSome<I> { - WhileSome { iter } -} - -impl<I, A> Iterator for WhileSome<I> - where I: Iterator<Item = Option<A>> -{ - type Item = A; - - fn next(&mut self) -> Option<Self::Item> { - match self.iter.next() { - None | Some(None) => None, - Some(elt) => elt, - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - (0, self.iter.size_hint().1) - } -} - -/// An iterator to iterate through all combinations in a `Clone`-able iterator that produces tuples -/// of a specific size. -/// -/// See [`.tuple_combinations()`](crate::Itertools::tuple_combinations) for more -/// information. -#[derive(Clone, Debug)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct TupleCombinations<I, T> - where I: Iterator, - T: HasCombination<I> -{ - iter: T::Combination, - _mi: PhantomData<I>, -} - -pub trait HasCombination<I>: Sized { - type Combination: From<I> + Iterator<Item = Self>; -} - -/// Create a new `TupleCombinations` from a clonable iterator. -pub fn tuple_combinations<T, I>(iter: I) -> TupleCombinations<I, T> - where I: Iterator + Clone, - I::Item: Clone, - T: HasCombination<I>, -{ - TupleCombinations { - iter: T::Combination::from(iter), - _mi: PhantomData, - } -} - -impl<I, T> Iterator for TupleCombinations<I, T> - where I: Iterator, - T: HasCombination<I>, -{ - type Item = T; - - fn next(&mut self) -> Option<Self::Item> { - self.iter.next() - } -} - -impl<I, T> FusedIterator for TupleCombinations<I, T> - where I: FusedIterator, - T: HasCombination<I>, -{} - -#[derive(Clone, Debug)] -pub struct Tuple1Combination<I> { - iter: I, -} - -impl<I> From<I> for Tuple1Combination<I> { - fn from(iter: I) -> Self { - Tuple1Combination { iter } - } -} - -impl<I: Iterator> Iterator for Tuple1Combination<I> { - type Item = (I::Item,); - - fn next(&mut self) -> Option<Self::Item> { - self.iter.next().map(|x| (x,)) - } -} - -impl<I: Iterator> HasCombination<I> for (I::Item,) { - type Combination = Tuple1Combination<I>; -} - -macro_rules! impl_tuple_combination { - ($C:ident $P:ident ; $($X:ident)*) => ( - #[derive(Clone, Debug)] - pub struct $C<I: Iterator> { - item: Option<I::Item>, - iter: I, - c: $P<I>, - } - - impl<I: Iterator + Clone> From<I> for $C<I> { - fn from(mut iter: I) -> Self { - Self { - item: iter.next(), - iter: iter.clone(), - c: iter.into(), - } - } - } - - impl<I: Iterator + Clone> From<I> for $C<Fuse<I>> { - fn from(iter: I) -> Self { - Self::from(iter.fuse()) - } - } - - impl<I, A> Iterator for $C<I> - where I: Iterator<Item = A> + Clone, - I::Item: Clone - { - type Item = (A, $(ignore_ident!($X, A)),*); - - fn next(&mut self) -> Option<Self::Item> { - if let Some(($($X),*,)) = self.c.next() { - let z = self.item.clone().unwrap(); - Some((z, $($X),*)) - } else { - self.item = self.iter.next(); - self.item.clone().and_then(|z| { - self.c = self.iter.clone().into(); - self.c.next().map(|($($X),*,)| (z, $($X),*)) - }) - } - } - } - - impl<I, A> HasCombination<I> for (A, $(ignore_ident!($X, A)),*) - where I: Iterator<Item = A> + Clone, - I::Item: Clone - { - type Combination = $C<Fuse<I>>; - } - ) -} - -// This snippet generates the twelve `impl_tuple_combination!` invocations: -// use core::iter; -// use itertools::Itertools; -// -// for i in 2..=12 { -// println!("impl_tuple_combination!(Tuple{arity}Combination Tuple{prev}Combination; {idents});", -// arity = i, -// prev = i - 1, -// idents = ('a'..'z').take(i - 1).join(" "), -// ); -// } -// It could probably be replaced by a bit more macro cleverness. -impl_tuple_combination!(Tuple2Combination Tuple1Combination; a); -impl_tuple_combination!(Tuple3Combination Tuple2Combination; a b); -impl_tuple_combination!(Tuple4Combination Tuple3Combination; a b c); -impl_tuple_combination!(Tuple5Combination Tuple4Combination; a b c d); -impl_tuple_combination!(Tuple6Combination Tuple5Combination; a b c d e); -impl_tuple_combination!(Tuple7Combination Tuple6Combination; a b c d e f); -impl_tuple_combination!(Tuple8Combination Tuple7Combination; a b c d e f g); -impl_tuple_combination!(Tuple9Combination Tuple8Combination; a b c d e f g h); -impl_tuple_combination!(Tuple10Combination Tuple9Combination; a b c d e f g h i); -impl_tuple_combination!(Tuple11Combination Tuple10Combination; a b c d e f g h i j); -impl_tuple_combination!(Tuple12Combination Tuple11Combination; a b c d e f g h i j k); - -/// An iterator adapter to filter values within a nested `Result::Ok`. -/// -/// See [`.filter_ok()`](crate::Itertools::filter_ok) for more information. -#[derive(Clone)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct FilterOk<I, F> { - iter: I, - f: F -} - -impl<I, F> fmt::Debug for FilterOk<I, F> -where - I: fmt::Debug, -{ - debug_fmt_fields!(FilterOk, iter); -} - -/// Create a new `FilterOk` iterator. -pub fn filter_ok<I, F, T, E>(iter: I, f: F) -> FilterOk<I, F> - where I: Iterator<Item = Result<T, E>>, - F: FnMut(&T) -> bool, -{ - FilterOk { - iter, - f, - } -} - -impl<I, F, T, E> Iterator for FilterOk<I, F> - where I: Iterator<Item = Result<T, E>>, - F: FnMut(&T) -> bool, -{ - type Item = Result<T, E>; - - fn next(&mut self) -> Option<Self::Item> { - loop { - match self.iter.next() { - Some(Ok(v)) => { - if (self.f)(&v) { - return Some(Ok(v)); - } - }, - Some(Err(e)) => return Some(Err(e)), - None => return None, - } - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - (0, self.iter.size_hint().1) - } - - fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc - where Fold: FnMut(Acc, Self::Item) -> Acc, - { - let mut f = self.f; - self.iter.filter(|v| { - v.as_ref().map(&mut f).unwrap_or(true) - }).fold(init, fold_f) - } - - fn collect<C>(self) -> C - where C: FromIterator<Self::Item> - { - let mut f = self.f; - self.iter.filter(|v| { - v.as_ref().map(&mut f).unwrap_or(true) - }).collect() - } -} - -impl<I, F, T, E> FusedIterator for FilterOk<I, F> - where I: FusedIterator<Item = Result<T, E>>, - F: FnMut(&T) -> bool, -{} - -/// An iterator adapter to filter and apply a transformation on values within a nested `Result::Ok`. -/// -/// See [`.filter_map_ok()`](crate::Itertools::filter_map_ok) for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct FilterMapOk<I, F> { - iter: I, - f: F -} - -impl<I, F> fmt::Debug for FilterMapOk<I, F> -where - I: fmt::Debug, -{ - debug_fmt_fields!(FilterMapOk, iter); -} - -fn transpose_result<T, E>(result: Result<Option<T>, E>) -> Option<Result<T, E>> { - match result { - Ok(Some(v)) => Some(Ok(v)), - Ok(None) => None, - Err(e) => Some(Err(e)), - } -} - -/// Create a new `FilterOk` iterator. -pub fn filter_map_ok<I, F, T, U, E>(iter: I, f: F) -> FilterMapOk<I, F> - where I: Iterator<Item = Result<T, E>>, - F: FnMut(T) -> Option<U>, -{ - FilterMapOk { - iter, - f, - } -} - -impl<I, F, T, U, E> Iterator for FilterMapOk<I, F> - where I: Iterator<Item = Result<T, E>>, - F: FnMut(T) -> Option<U>, -{ - type Item = Result<U, E>; - - fn next(&mut self) -> Option<Self::Item> { - loop { - match self.iter.next() { - Some(Ok(v)) => { - if let Some(v) = (self.f)(v) { - return Some(Ok(v)); - } - }, - Some(Err(e)) => return Some(Err(e)), - None => return None, - } - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - (0, self.iter.size_hint().1) - } - - fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc - where Fold: FnMut(Acc, Self::Item) -> Acc, - { - let mut f = self.f; - self.iter.filter_map(|v| { - transpose_result(v.map(&mut f)) - }).fold(init, fold_f) - } - - fn collect<C>(self) -> C - where C: FromIterator<Self::Item> - { - let mut f = self.f; - self.iter.filter_map(|v| { - transpose_result(v.map(&mut f)) - }).collect() - } -} - -impl<I, F, T, U, E> FusedIterator for FilterMapOk<I, F> - where I: FusedIterator<Item = Result<T, E>>, - F: FnMut(T) -> Option<U>, -{} - -/// An iterator adapter to get the positions of each element that matches a predicate. -/// -/// See [`.positions()`](crate::Itertools::positions) for more information. -#[derive(Clone)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Positions<I, F> { - iter: I, - f: F, - count: usize, -} - -impl<I, F> fmt::Debug for Positions<I, F> -where - I: fmt::Debug, -{ - debug_fmt_fields!(Positions, iter, count); -} - -/// Create a new `Positions` iterator. -pub fn positions<I, F>(iter: I, f: F) -> Positions<I, F> - where I: Iterator, - F: FnMut(I::Item) -> bool, -{ - Positions { - iter, - f, - count: 0 - } -} - -impl<I, F> Iterator for Positions<I, F> - where I: Iterator, - F: FnMut(I::Item) -> bool, -{ - type Item = usize; - - fn next(&mut self) -> Option<Self::Item> { - while let Some(v) = self.iter.next() { - let i = self.count; - self.count = i + 1; - if (self.f)(v) { - return Some(i); - } - } - None - } - - fn size_hint(&self) -> (usize, Option<usize>) { - (0, self.iter.size_hint().1) - } -} - -impl<I, F> DoubleEndedIterator for Positions<I, F> - where I: DoubleEndedIterator + ExactSizeIterator, - F: FnMut(I::Item) -> bool, -{ - fn next_back(&mut self) -> Option<Self::Item> { - while let Some(v) = self.iter.next_back() { - if (self.f)(v) { - return Some(self.count + self.iter.len()) - } - } - None - } -} - -impl<I, F> FusedIterator for Positions<I, F> - where I: FusedIterator, - F: FnMut(I::Item) -> bool, -{} - -/// An iterator adapter to apply a mutating function to each element before yielding it. -/// -/// See [`.update()`](crate::Itertools::update) for more information. -#[derive(Clone)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Update<I, F> { - iter: I, - f: F, -} - -impl<I, F> fmt::Debug for Update<I, F> -where - I: fmt::Debug, -{ - debug_fmt_fields!(Update, iter); -} - -/// Create a new `Update` iterator. -pub fn update<I, F>(iter: I, f: F) -> Update<I, F> -where - I: Iterator, - F: FnMut(&mut I::Item), -{ - Update { iter, f } -} - -impl<I, F> Iterator for Update<I, F> -where - I: Iterator, - F: FnMut(&mut I::Item), -{ - type Item = I::Item; - - fn next(&mut self) -> Option<Self::Item> { - if let Some(mut v) = self.iter.next() { - (self.f)(&mut v); - Some(v) - } else { - None - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } - - fn fold<Acc, G>(self, init: Acc, mut g: G) -> Acc - where G: FnMut(Acc, Self::Item) -> Acc, - { - let mut f = self.f; - self.iter.fold(init, move |acc, mut v| { f(&mut v); g(acc, v) }) - } - - // if possible, re-use inner iterator specializations in collect - fn collect<C>(self) -> C - where C: FromIterator<Self::Item> - { - let mut f = self.f; - self.iter.map(move |mut v| { f(&mut v); v }).collect() - } -} - -impl<I, F> ExactSizeIterator for Update<I, F> -where - I: ExactSizeIterator, - F: FnMut(&mut I::Item), -{} - -impl<I, F> DoubleEndedIterator for Update<I, F> -where - I: DoubleEndedIterator, - F: FnMut(&mut I::Item), -{ - fn next_back(&mut self) -> Option<Self::Item> { - if let Some(mut v) = self.iter.next_back() { - (self.f)(&mut v); - Some(v) - } else { - None - } - } -} - -impl<I, F> FusedIterator for Update<I, F> -where - I: FusedIterator, - F: FnMut(&mut I::Item), -{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/multi_product.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/multi_product.rs deleted file mode 100644 index 0b38406..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/multi_product.rs +++ /dev/null
@@ -1,230 +0,0 @@ -#![cfg(feature = "use_alloc")] - -use crate::size_hint; -use crate::Itertools; - -use alloc::vec::Vec; - -#[derive(Clone)] -/// An iterator adaptor that iterates over the cartesian product of -/// multiple iterators of type `I`. -/// -/// An iterator element type is `Vec<I>`. -/// -/// See [`.multi_cartesian_product()`](crate::Itertools::multi_cartesian_product) -/// for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct MultiProduct<I>(Vec<MultiProductIter<I>>) - where I: Iterator + Clone, - I::Item: Clone; - -impl<I> std::fmt::Debug for MultiProduct<I> -where - I: Iterator + Clone + std::fmt::Debug, - I::Item: Clone + std::fmt::Debug, -{ - debug_fmt_fields!(CoalesceBy, 0); -} - -/// Create a new cartesian product iterator over an arbitrary number -/// of iterators of the same type. -/// -/// Iterator element is of type `Vec<H::Item::Item>`. -pub fn multi_cartesian_product<H>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter> - where H: Iterator, - H::Item: IntoIterator, - <H::Item as IntoIterator>::IntoIter: Clone, - <H::Item as IntoIterator>::Item: Clone -{ - MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect()) -} - -#[derive(Clone, Debug)] -/// Holds the state of a single iterator within a `MultiProduct`. -struct MultiProductIter<I> - where I: Iterator + Clone, - I::Item: Clone -{ - cur: Option<I::Item>, - iter: I, - iter_orig: I, -} - -/// Holds the current state during an iteration of a `MultiProduct`. -#[derive(Debug)] -enum MultiProductIterState { - StartOfIter, - MidIter { on_first_iter: bool }, -} - -impl<I> MultiProduct<I> - where I: Iterator + Clone, - I::Item: Clone -{ - /// Iterates the rightmost iterator, then recursively iterates iterators - /// to the left if necessary. - /// - /// Returns true if the iteration succeeded, else false. - fn iterate_last( - multi_iters: &mut [MultiProductIter<I>], - mut state: MultiProductIterState - ) -> bool { - use self::MultiProductIterState::*; - - if let Some((last, rest)) = multi_iters.split_last_mut() { - let on_first_iter = match state { - StartOfIter => { - let on_first_iter = !last.in_progress(); - state = MidIter { on_first_iter }; - on_first_iter - }, - MidIter { on_first_iter } => on_first_iter - }; - - if !on_first_iter { - last.iterate(); - } - - if last.in_progress() { - true - } else if MultiProduct::iterate_last(rest, state) { - last.reset(); - last.iterate(); - // If iterator is None twice consecutively, then iterator is - // empty; whole product is empty. - last.in_progress() - } else { - false - } - } else { - // Reached end of iterator list. On initialisation, return true. - // At end of iteration (final iterator finishes), finish. - match state { - StartOfIter => false, - MidIter { on_first_iter } => on_first_iter - } - } - } - - /// Returns the unwrapped value of the next iteration. - fn curr_iterator(&self) -> Vec<I::Item> { - self.0.iter().map(|multi_iter| { - multi_iter.cur.clone().unwrap() - }).collect() - } - - /// Returns true if iteration has started and has not yet finished; false - /// otherwise. - fn in_progress(&self) -> bool { - if let Some(last) = self.0.last() { - last.in_progress() - } else { - false - } - } -} - -impl<I> MultiProductIter<I> - where I: Iterator + Clone, - I::Item: Clone -{ - fn new(iter: I) -> Self { - MultiProductIter { - cur: None, - iter: iter.clone(), - iter_orig: iter - } - } - - /// Iterate the managed iterator. - fn iterate(&mut self) { - self.cur = self.iter.next(); - } - - /// Reset the managed iterator. - fn reset(&mut self) { - self.iter = self.iter_orig.clone(); - } - - /// Returns true if the current iterator has been started and has not yet - /// finished; false otherwise. - fn in_progress(&self) -> bool { - self.cur.is_some() - } -} - -impl<I> Iterator for MultiProduct<I> - where I: Iterator + Clone, - I::Item: Clone -{ - type Item = Vec<I::Item>; - - fn next(&mut self) -> Option<Self::Item> { - if MultiProduct::iterate_last( - &mut self.0, - MultiProductIterState::StartOfIter - ) { - Some(self.curr_iterator()) - } else { - None - } - } - - fn count(self) -> usize { - if self.0.is_empty() { - return 0; - } - - if !self.in_progress() { - return self.0.into_iter().fold(1, |acc, multi_iter| { - acc * multi_iter.iter.count() - }); - } - - self.0.into_iter().fold( - 0, - |acc, MultiProductIter { iter, iter_orig, cur: _ }| { - let total_count = iter_orig.count(); - let cur_count = iter.count(); - acc * total_count + cur_count - } - ) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - // Not ExactSizeIterator because size may be larger than usize - if self.0.is_empty() { - return (0, Some(0)); - } - - if !self.in_progress() { - return self.0.iter().fold((1, Some(1)), |acc, multi_iter| { - size_hint::mul(acc, multi_iter.iter.size_hint()) - }); - } - - self.0.iter().fold( - (0, Some(0)), - |acc, &MultiProductIter { ref iter, ref iter_orig, cur: _ }| { - let cur_size = iter.size_hint(); - let total_size = iter_orig.size_hint(); - size_hint::add(size_hint::mul(acc, total_size), cur_size) - } - ) - } - - fn last(self) -> Option<Self::Item> { - let iter_count = self.0.len(); - - let lasts: Self::Item = self.0.into_iter() - .map(|multi_iter| multi_iter.iter.last()) - .while_some() - .collect(); - - if lasts.len() == iter_count { - Some(lasts) - } else { - None - } - } -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations.rs deleted file mode 100644 index 68a59c5..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations.rs +++ /dev/null
@@ -1,128 +0,0 @@ -use std::fmt; -use std::iter::FusedIterator; - -use super::lazy_buffer::LazyBuffer; -use alloc::vec::Vec; - -/// An iterator to iterate through all the `k`-length combinations in an iterator. -/// -/// See [`.combinations()`](crate::Itertools::combinations) for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Combinations<I: Iterator> { - indices: Vec<usize>, - pool: LazyBuffer<I>, - first: bool, -} - -impl<I> Clone for Combinations<I> - where I: Clone + Iterator, - I::Item: Clone, -{ - clone_fields!(indices, pool, first); -} - -impl<I> fmt::Debug for Combinations<I> - where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(Combinations, indices, pool, first); -} - -/// Create a new `Combinations` from a clonable iterator. -pub fn combinations<I>(iter: I, k: usize) -> Combinations<I> - where I: Iterator -{ - let mut pool = LazyBuffer::new(iter); - pool.prefill(k); - - Combinations { - indices: (0..k).collect(), - pool, - first: true, - } -} - -impl<I: Iterator> Combinations<I> { - /// Returns the length of a combination produced by this iterator. - #[inline] - pub fn k(&self) -> usize { self.indices.len() } - - /// Returns the (current) length of the pool from which combination elements are - /// selected. This value can change between invocations of [`next`](Combinations::next). - #[inline] - pub fn n(&self) -> usize { self.pool.len() } - - /// Returns a reference to the source iterator. - #[inline] - pub(crate) fn src(&self) -> &I { &self.pool.it } - - /// Resets this `Combinations` back to an initial state for combinations of length - /// `k` over the same pool data source. If `k` is larger than the current length - /// of the data pool an attempt is made to prefill the pool so that it holds `k` - /// elements. - pub(crate) fn reset(&mut self, k: usize) { - self.first = true; - - if k < self.indices.len() { - self.indices.truncate(k); - for i in 0..k { - self.indices[i] = i; - } - - } else { - for i in 0..self.indices.len() { - self.indices[i] = i; - } - self.indices.extend(self.indices.len()..k); - self.pool.prefill(k); - } - } -} - -impl<I> Iterator for Combinations<I> - where I: Iterator, - I::Item: Clone -{ - type Item = Vec<I::Item>; - fn next(&mut self) -> Option<Self::Item> { - if self.first { - if self.k() > self.n() { - return None; - } - self.first = false; - } else if self.indices.is_empty() { - return None; - } else { - // Scan from the end, looking for an index to increment - let mut i: usize = self.indices.len() - 1; - - // Check if we need to consume more from the iterator - if self.indices[i] == self.pool.len() - 1 { - self.pool.get_next(); // may change pool size - } - - while self.indices[i] == i + self.pool.len() - self.indices.len() { - if i > 0 { - i -= 1; - } else { - // Reached the last combination - return None; - } - } - - // Increment index, and reset the ones to its right - self.indices[i] += 1; - for j in i+1..self.indices.len() { - self.indices[j] = self.indices[j - 1] + 1; - } - } - - // Create result vector based on the indices - Some(self.indices.iter().map(|i| self.pool[*i].clone()).collect()) - } -} - -impl<I> FusedIterator for Combinations<I> - where I: Iterator, - I::Item: Clone -{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations_with_replacement.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations_with_replacement.rs deleted file mode 100644 index 0fec967..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations_with_replacement.rs +++ /dev/null
@@ -1,109 +0,0 @@ -use alloc::vec::Vec; -use std::fmt; -use std::iter::FusedIterator; - -use super::lazy_buffer::LazyBuffer; - -/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement. -/// -/// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement) -/// for more information. -#[derive(Clone)] -pub struct CombinationsWithReplacement<I> -where - I: Iterator, - I::Item: Clone, -{ - indices: Vec<usize>, - pool: LazyBuffer<I>, - first: bool, -} - -impl<I> fmt::Debug for CombinationsWithReplacement<I> -where - I: Iterator + fmt::Debug, - I::Item: fmt::Debug + Clone, -{ - debug_fmt_fields!(Combinations, indices, pool, first); -} - -impl<I> CombinationsWithReplacement<I> -where - I: Iterator, - I::Item: Clone, -{ - /// Map the current mask over the pool to get an output combination - fn current(&self) -> Vec<I::Item> { - self.indices.iter().map(|i| self.pool[*i].clone()).collect() - } -} - -/// Create a new `CombinationsWithReplacement` from a clonable iterator. -pub fn combinations_with_replacement<I>(iter: I, k: usize) -> CombinationsWithReplacement<I> -where - I: Iterator, - I::Item: Clone, -{ - let indices: Vec<usize> = alloc::vec![0; k]; - let pool: LazyBuffer<I> = LazyBuffer::new(iter); - - CombinationsWithReplacement { - indices, - pool, - first: true, - } -} - -impl<I> Iterator for CombinationsWithReplacement<I> -where - I: Iterator, - I::Item: Clone, -{ - type Item = Vec<I::Item>; - fn next(&mut self) -> Option<Self::Item> { - // If this is the first iteration, return early - if self.first { - // In empty edge cases, stop iterating immediately - return if !(self.indices.is_empty() || self.pool.get_next()) { - None - // Otherwise, yield the initial state - } else { - self.first = false; - Some(self.current()) - }; - } - - // Check if we need to consume more from the iterator - // This will run while we increment our first index digit - self.pool.get_next(); - - // Work out where we need to update our indices - let mut increment: Option<(usize, usize)> = None; - for (i, indices_int) in self.indices.iter().enumerate().rev() { - if *indices_int < self.pool.len()-1 { - increment = Some((i, indices_int + 1)); - break; - } - } - - match increment { - // If we can update the indices further - Some((increment_from, increment_value)) => { - // We need to update the rightmost non-max value - // and all those to the right - for indices_index in increment_from..self.indices.len() { - self.indices[indices_index] = increment_value; - } - Some(self.current()) - } - // Otherwise, we're done - None => None, - } - } -} - -impl<I> FusedIterator for CombinationsWithReplacement<I> -where - I: Iterator, - I::Item: Clone, -{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/concat_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/concat_impl.rs deleted file mode 100644 index f022ec9..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/concat_impl.rs +++ /dev/null
@@ -1,23 +0,0 @@ -use crate::Itertools; - -/// Combine all an iterator's elements into one element by using [`Extend`]. -/// -/// [`IntoIterator`]-enabled version of [`Itertools::concat`]. -/// -/// This combinator will extend the first item with each of the rest of the -/// items of the iterator. If the iterator is empty, the default value of -/// `I::Item` is returned. -/// -/// ```rust -/// use itertools::concat; -/// -/// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]]; -/// assert_eq!(concat(input), vec![1, 2, 3, 4, 5, 6]); -/// ``` -pub fn concat<I>(iterable: I) -> I::Item - where I: IntoIterator, - I::Item: Extend<<<I as IntoIterator>::Item as IntoIterator>::Item> + IntoIterator + Default -{ - #[allow(deprecated)] //TODO: once msrv hits 1.51. replace `fold1` with `reduce` - iterable.into_iter().fold1(|mut a, b| { a.extend(b); a }).unwrap_or_default() -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/cons_tuples_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/cons_tuples_impl.rs deleted file mode 100644 index ae0f48f..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/cons_tuples_impl.rs +++ /dev/null
@@ -1,64 +0,0 @@ - -macro_rules! impl_cons_iter( - ($_A:ident, $_B:ident, ) => (); // stop - - ($A:ident, $($B:ident,)*) => ( - impl_cons_iter!($($B,)*); - #[allow(non_snake_case)] - impl<X, Iter, $($B),*> Iterator for ConsTuples<Iter, (($($B,)*), X)> - where Iter: Iterator<Item = (($($B,)*), X)>, - { - type Item = ($($B,)* X, ); - fn next(&mut self) -> Option<Self::Item> { - self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, )) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } - fn fold<Acc, Fold>(self, accum: Acc, mut f: Fold) -> Acc - where Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.fold(accum, move |acc, (($($B,)*), x)| f(acc, ($($B,)* x, ))) - } - } - - #[allow(non_snake_case)] - impl<X, Iter, $($B),*> DoubleEndedIterator for ConsTuples<Iter, (($($B,)*), X)> - where Iter: DoubleEndedIterator<Item = (($($B,)*), X)>, - { - fn next_back(&mut self) -> Option<Self::Item> { - self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, )) - } - } - - ); -); - -impl_cons_iter!(A, B, C, D, E, F, G, H, I, J, K, L,); - -/// An iterator that maps an iterator of tuples like -/// `((A, B), C)` to an iterator of `(A, B, C)`. -/// -/// Used by the `iproduct!()` macro. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[derive(Debug)] -pub struct ConsTuples<I, J> - where I: Iterator<Item=J>, -{ - iter: I, -} - -impl<I, J> Clone for ConsTuples<I, J> - where I: Clone + Iterator<Item=J>, -{ - clone_fields!(iter); -} - -/// Create an iterator that maps for example iterators of -/// `((A, B), C)` to `(A, B, C)`. -pub fn cons_tuples<I, J>(iterable: I) -> ConsTuples<I::IntoIter, J> - where I: IntoIterator<Item=J> -{ - ConsTuples { iter: iterable.into_iter() } -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/diff.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/diff.rs deleted file mode 100644 index 1731f06..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/diff.rs +++ /dev/null
@@ -1,61 +0,0 @@ -//! "Diff"ing iterators for caching elements to sequential collections without requiring the new -//! elements' iterator to be `Clone`. -//! -//! - [`Diff`] (produced by the [`diff_with`] function) -//! describes the difference between two non-`Clone` iterators `I` and `J` after breaking ASAP from -//! a lock-step comparison. - -use crate::free::put_back; -use crate::structs::PutBack; - -/// A type returned by the [`diff_with`] function. -/// -/// `Diff` represents the way in which the elements yielded by the iterator `I` differ to some -/// iterator `J`. -pub enum Diff<I, J> - where I: Iterator, - J: Iterator -{ - /// The index of the first non-matching element along with both iterator's remaining elements - /// starting with the first mis-match. - FirstMismatch(usize, PutBack<I>, PutBack<J>), - /// The total number of elements that were in `J` along with the remaining elements of `I`. - Shorter(usize, PutBack<I>), - /// The total number of elements that were in `I` along with the remaining elements of `J`. - Longer(usize, PutBack<J>), -} - -/// Compares every element yielded by both `i` and `j` with the given function in lock-step and -/// returns a [`Diff`] which describes how `j` differs from `i`. -/// -/// If the number of elements yielded by `j` is less than the number of elements yielded by `i`, -/// the number of `j` elements yielded will be returned along with `i`'s remaining elements as -/// `Diff::Shorter`. -/// -/// If the two elements of a step differ, the index of those elements along with the remaining -/// elements of both `i` and `j` are returned as `Diff::FirstMismatch`. -/// -/// If `i` becomes exhausted before `j` becomes exhausted, the number of elements in `i` along with -/// the remaining `j` elements will be returned as `Diff::Longer`. -pub fn diff_with<I, J, F>(i: I, j: J, is_equal: F) - -> Option<Diff<I::IntoIter, J::IntoIter>> - where I: IntoIterator, - J: IntoIterator, - F: Fn(&I::Item, &J::Item) -> bool -{ - let mut i = i.into_iter(); - let mut j = j.into_iter(); - let mut idx = 0; - while let Some(i_elem) = i.next() { - match j.next() { - None => return Some(Diff::Shorter(idx, put_back(i).with_value(i_elem))), - Some(j_elem) => if !is_equal(&i_elem, &j_elem) { - let remaining_i = put_back(i).with_value(i_elem); - let remaining_j = put_back(j).with_value(j_elem); - return Some(Diff::FirstMismatch(idx, remaining_i, remaining_j)); - }, - } - idx += 1; - } - j.next().map(|j_elem| Diff::Longer(idx, put_back(j).with_value(j_elem))) -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/intersperse.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/intersperse.rs deleted file mode 100644 index 10a3a538..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/intersperse.rs +++ /dev/null
@@ -1,118 +0,0 @@ -use std::iter::{Fuse, FusedIterator}; -use super::size_hint; - -pub trait IntersperseElement<Item> { - fn generate(&mut self) -> Item; -} - -#[derive(Debug, Clone)] -pub struct IntersperseElementSimple<Item>(Item); - -impl<Item: Clone> IntersperseElement<Item> for IntersperseElementSimple<Item> { - fn generate(&mut self) -> Item { - self.0.clone() - } -} - -/// An iterator adaptor to insert a particular value -/// between each element of the adapted iterator. -/// -/// Iterator element type is `I::Item` -/// -/// This iterator is *fused*. -/// -/// See [`.intersperse()`](crate::Itertools::intersperse) for more information. -pub type Intersperse<I> = IntersperseWith<I, IntersperseElementSimple<<I as Iterator>::Item>>; - -/// Create a new Intersperse iterator -pub fn intersperse<I>(iter: I, elt: I::Item) -> Intersperse<I> - where I: Iterator, -{ - intersperse_with(iter, IntersperseElementSimple(elt)) -} - -impl<Item, F: FnMut()->Item> IntersperseElement<Item> for F { - fn generate(&mut self) -> Item { - self() - } -} - -/// An iterator adaptor to insert a particular value created by a function -/// between each element of the adapted iterator. -/// -/// Iterator element type is `I::Item` -/// -/// This iterator is *fused*. -/// -/// See [`.intersperse_with()`](crate::Itertools::intersperse_with) for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[derive(Clone, Debug)] -pub struct IntersperseWith<I, ElemF> - where I: Iterator, -{ - element: ElemF, - iter: Fuse<I>, - peek: Option<I::Item>, -} - -/// Create a new `IntersperseWith` iterator -pub fn intersperse_with<I, ElemF>(iter: I, elt: ElemF) -> IntersperseWith<I, ElemF> - where I: Iterator, -{ - let mut iter = iter.fuse(); - IntersperseWith { - peek: iter.next(), - iter, - element: elt, - } -} - -impl<I, ElemF> Iterator for IntersperseWith<I, ElemF> - where I: Iterator, - ElemF: IntersperseElement<I::Item> -{ - type Item = I::Item; - #[inline] - fn next(&mut self) -> Option<Self::Item> { - if self.peek.is_some() { - self.peek.take() - } else { - self.peek = self.iter.next(); - if self.peek.is_some() { - Some(self.element.generate()) - } else { - None - } - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - // 2 * SH + { 1 or 0 } - let has_peek = self.peek.is_some() as usize; - let sh = self.iter.size_hint(); - size_hint::add_scalar(size_hint::add(sh, sh), has_peek) - } - - fn fold<B, F>(mut self, init: B, mut f: F) -> B where - Self: Sized, F: FnMut(B, Self::Item) -> B, - { - let mut accum = init; - - if let Some(x) = self.peek.take() { - accum = f(accum, x); - } - - let element = &mut self.element; - - self.iter.fold(accum, - |accum, x| { - let accum = f(accum, element.generate()); - f(accum, x) - }) - } -} - -impl<I, ElemF> FusedIterator for IntersperseWith<I, ElemF> - where I: Iterator, - ElemF: IntersperseElement<I::Item> -{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/k_smallest.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/k_smallest.rs deleted file mode 100644 index acaea59..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/k_smallest.rs +++ /dev/null
@@ -1,20 +0,0 @@ -use alloc::collections::BinaryHeap; -use core::cmp::Ord; - -pub(crate) fn k_smallest<T: Ord, I: Iterator<Item = T>>(mut iter: I, k: usize) -> BinaryHeap<T> { - if k == 0 { return BinaryHeap::new(); } - - let mut heap = iter.by_ref().take(k).collect::<BinaryHeap<_>>(); - - iter.for_each(|i| { - debug_assert_eq!(heap.len(), k); - // Equivalent to heap.push(min(i, heap.pop())) but more efficient. - // This should be done with a single `.peek_mut().unwrap()` but - // `PeekMut` sifts-down unconditionally on Rust 1.46.0 and prior. - if *heap.peek().unwrap() > i { - *heap.peek_mut().unwrap() = i; - } - }); - - heap -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lazy_buffer.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lazy_buffer.rs deleted file mode 100644 index ca24062..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lazy_buffer.rs +++ /dev/null
@@ -1,63 +0,0 @@ -use std::ops::Index; -use alloc::vec::Vec; - -#[derive(Debug, Clone)] -pub struct LazyBuffer<I: Iterator> { - pub it: I, - done: bool, - buffer: Vec<I::Item>, -} - -impl<I> LazyBuffer<I> -where - I: Iterator, -{ - pub fn new(it: I) -> LazyBuffer<I> { - LazyBuffer { - it, - done: false, - buffer: Vec::new(), - } - } - - pub fn len(&self) -> usize { - self.buffer.len() - } - - pub fn get_next(&mut self) -> bool { - if self.done { - return false; - } - if let Some(x) = self.it.next() { - self.buffer.push(x); - true - } else { - self.done = true; - false - } - } - - pub fn prefill(&mut self, len: usize) { - let buffer_len = self.buffer.len(); - - if !self.done && len > buffer_len { - let delta = len - buffer_len; - - self.buffer.extend(self.it.by_ref().take(delta)); - self.done = self.buffer.len() < len; - } - } -} - -impl<I, J> Index<J> for LazyBuffer<I> -where - I: Iterator, - I::Item: Sized, - Vec<I::Item>: Index<J> -{ - type Output = <Vec<I::Item> as Index<J>>::Output; - - fn index(&self, index: J) -> &Self::Output { - self.buffer.index(index) - } -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/merge_join.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/merge_join.rs deleted file mode 100644 index 84f7d033..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/merge_join.rs +++ /dev/null
@@ -1,220 +0,0 @@ -use std::cmp::Ordering; -use std::iter::Fuse; -use std::fmt; - -use either::Either; - -use super::adaptors::{PutBack, put_back}; -use crate::either_or_both::EitherOrBoth; -use crate::size_hint::{self, SizeHint}; -#[cfg(doc)] -use crate::Itertools; - -/// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order. -/// -/// [`IntoIterator`] enabled version of [`Itertools::merge_join_by`]. -pub fn merge_join_by<I, J, F, T>(left: I, right: J, cmp_fn: F) - -> MergeJoinBy<I::IntoIter, J::IntoIter, F> - where I: IntoIterator, - J: IntoIterator, - F: FnMut(&I::Item, &J::Item) -> T, - T: OrderingOrBool<I::Item, J::Item>, -{ - MergeJoinBy { - left: put_back(left.into_iter().fuse()), - right: put_back(right.into_iter().fuse()), - cmp_fn, - } -} - -/// An iterator adaptor that merge-joins items from the two base iterators in ascending order. -/// -/// See [`.merge_join_by()`](crate::Itertools::merge_join_by) for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct MergeJoinBy<I: Iterator, J: Iterator, F> { - left: PutBack<Fuse<I>>, - right: PutBack<Fuse<J>>, - cmp_fn: F, -} - -pub trait OrderingOrBool<L, R> { - type MergeResult; - fn left(left: L) -> Self::MergeResult; - fn right(right: R) -> Self::MergeResult; - // "merge" never returns (Some(...), Some(...), ...) so Option<Either<I::Item, J::Item>> - // is appealing but it is always followed by two put_backs, so we think the compiler is - // smart enough to optimize it. Or we could move put_backs into "merge". - fn merge(self, left: L, right: R) -> (Option<L>, Option<R>, Self::MergeResult); - fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint; -} - -impl<L, R> OrderingOrBool<L, R> for Ordering { - type MergeResult = EitherOrBoth<L, R>; - fn left(left: L) -> Self::MergeResult { - EitherOrBoth::Left(left) - } - fn right(right: R) -> Self::MergeResult { - EitherOrBoth::Right(right) - } - fn merge(self, left: L, right: R) -> (Option<L>, Option<R>, Self::MergeResult) { - match self { - Ordering::Equal => (None, None, EitherOrBoth::Both(left, right)), - Ordering::Less => (None, Some(right), EitherOrBoth::Left(left)), - Ordering::Greater => (Some(left), None, EitherOrBoth::Right(right)), - } - } - fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { - let (a_lower, a_upper) = left; - let (b_lower, b_upper) = right; - let lower = ::std::cmp::max(a_lower, b_lower); - let upper = match (a_upper, b_upper) { - (Some(x), Some(y)) => x.checked_add(y), - _ => None, - }; - (lower, upper) - } -} - -impl<L, R> OrderingOrBool<L, R> for bool { - type MergeResult = Either<L, R>; - fn left(left: L) -> Self::MergeResult { - Either::Left(left) - } - fn right(right: R) -> Self::MergeResult { - Either::Right(right) - } - fn merge(self, left: L, right: R) -> (Option<L>, Option<R>, Self::MergeResult) { - if self { - (None, Some(right), Either::Left(left)) - } else { - (Some(left), None, Either::Right(right)) - } - } - fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { - // Not ExactSizeIterator because size may be larger than usize - size_hint::add(left, right) - } -} - -impl<I, J, F> Clone for MergeJoinBy<I, J, F> - where I: Iterator, - J: Iterator, - PutBack<Fuse<I>>: Clone, - PutBack<Fuse<J>>: Clone, - F: Clone, -{ - clone_fields!(left, right, cmp_fn); -} - -impl<I, J, F> fmt::Debug for MergeJoinBy<I, J, F> - where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, - J: Iterator + fmt::Debug, - J::Item: fmt::Debug, -{ - debug_fmt_fields!(MergeJoinBy, left, right); -} - -impl<I, J, F, T> Iterator for MergeJoinBy<I, J, F> - where I: Iterator, - J: Iterator, - F: FnMut(&I::Item, &J::Item) -> T, - T: OrderingOrBool<I::Item, J::Item>, -{ - type Item = T::MergeResult; - - fn next(&mut self) -> Option<Self::Item> { - match (self.left.next(), self.right.next()) { - (None, None) => None, - (Some(left), None) => Some(T::left(left)), - (None, Some(right)) => Some(T::right(right)), - (Some(left), Some(right)) => { - let (left, right, next) = (self.cmp_fn)(&left, &right).merge(left, right); - if let Some(left) = left { - self.left.put_back(left); - } - if let Some(right) = right { - self.right.put_back(right); - } - Some(next) - } - } - } - - fn size_hint(&self) -> SizeHint { - T::size_hint(self.left.size_hint(), self.right.size_hint()) - } - - fn count(mut self) -> usize { - let mut count = 0; - loop { - match (self.left.next(), self.right.next()) { - (None, None) => break count, - (Some(_left), None) => break count + 1 + self.left.into_parts().1.count(), - (None, Some(_right)) => break count + 1 + self.right.into_parts().1.count(), - (Some(left), Some(right)) => { - count += 1; - let (left, right, _) = (self.cmp_fn)(&left, &right).merge(left, right); - if let Some(left) = left { - self.left.put_back(left); - } - if let Some(right) = right { - self.right.put_back(right); - } - } - } - } - } - - fn last(mut self) -> Option<Self::Item> { - let mut previous_element = None; - loop { - match (self.left.next(), self.right.next()) { - (None, None) => break previous_element, - (Some(left), None) => { - break Some(T::left( - self.left.into_parts().1.last().unwrap_or(left), - )) - } - (None, Some(right)) => { - break Some(T::right( - self.right.into_parts().1.last().unwrap_or(right), - )) - } - (Some(left), Some(right)) => { - let (left, right, elem) = (self.cmp_fn)(&left, &right).merge(left, right); - if let Some(left) = left { - self.left.put_back(left); - } - if let Some(right) = right { - self.right.put_back(right); - } - previous_element = Some(elem); - } - } - } - } - - fn nth(&mut self, mut n: usize) -> Option<Self::Item> { - loop { - if n == 0 { - break self.next(); - } - n -= 1; - match (self.left.next(), self.right.next()) { - (None, None) => break None, - (Some(_left), None) => break self.left.nth(n).map(T::left), - (None, Some(_right)) => break self.right.nth(n).map(T::right), - (Some(left), Some(right)) => { - let (left, right, _) = (self.cmp_fn)(&left, &right).merge(left, right); - if let Some(left) = left { - self.left.put_back(left); - } - if let Some(right) = right { - self.right.put_back(right); - } - } - } - } - } -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peek_nth.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peek_nth.rs deleted file mode 100644 index bcca4583..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peek_nth.rs +++ /dev/null
@@ -1,102 +0,0 @@ -use crate::size_hint; -use crate::PeekingNext; -use alloc::collections::VecDeque; -use std::iter::Fuse; - -/// See [`peek_nth()`] for more information. -#[derive(Clone, Debug)] -pub struct PeekNth<I> -where - I: Iterator, -{ - iter: Fuse<I>, - buf: VecDeque<I::Item>, -} - -/// A drop-in replacement for [`std::iter::Peekable`] which adds a `peek_nth` -/// method allowing the user to `peek` at a value several iterations forward -/// without advancing the base iterator. -/// -/// This differs from `multipeek` in that subsequent calls to `peek` or -/// `peek_nth` will always return the same value until `next` is called -/// (making `reset_peek` unnecessary). -pub fn peek_nth<I>(iterable: I) -> PeekNth<I::IntoIter> -where - I: IntoIterator, -{ - PeekNth { - iter: iterable.into_iter().fuse(), - buf: VecDeque::new(), - } -} - -impl<I> PeekNth<I> -where - I: Iterator, -{ - /// Works exactly like the `peek` method in `std::iter::Peekable` - pub fn peek(&mut self) -> Option<&I::Item> { - self.peek_nth(0) - } - - /// Returns a reference to the `nth` value without advancing the iterator. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```rust - /// use itertools::peek_nth; - /// - /// let xs = vec![1,2,3]; - /// let mut iter = peek_nth(xs.iter()); - /// - /// assert_eq!(iter.peek_nth(0), Some(&&1)); - /// assert_eq!(iter.next(), Some(&1)); - /// - /// // The iterator does not advance even if we call `peek_nth` multiple times - /// assert_eq!(iter.peek_nth(0), Some(&&2)); - /// assert_eq!(iter.peek_nth(1), Some(&&3)); - /// assert_eq!(iter.next(), Some(&2)); - /// - /// // Calling `peek_nth` past the end of the iterator will return `None` - /// assert_eq!(iter.peek_nth(1), None); - /// ``` - pub fn peek_nth(&mut self, n: usize) -> Option<&I::Item> { - let unbuffered_items = (n + 1).saturating_sub(self.buf.len()); - - self.buf.extend(self.iter.by_ref().take(unbuffered_items)); - - self.buf.get(n) - } -} - -impl<I> Iterator for PeekNth<I> -where - I: Iterator, -{ - type Item = I::Item; - - fn next(&mut self) -> Option<Self::Item> { - self.buf.pop_front().or_else(|| self.iter.next()) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) - } -} - -impl<I> ExactSizeIterator for PeekNth<I> where I: ExactSizeIterator {} - -impl<I> PeekingNext for PeekNth<I> -where - I: Iterator, -{ - fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item> - where - F: FnOnce(&Self::Item) -> bool, - { - self.peek().filter(|item| accept(item))?; - self.next() - } -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/permutations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/permutations.rs deleted file mode 100644 index d03b852..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/permutations.rs +++ /dev/null
@@ -1,277 +0,0 @@ -use alloc::vec::Vec; -use std::fmt; -use std::iter::once; - -use super::lazy_buffer::LazyBuffer; - -/// An iterator adaptor that iterates through all the `k`-permutations of the -/// elements from an iterator. -/// -/// See [`.permutations()`](crate::Itertools::permutations) for -/// more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Permutations<I: Iterator> { - vals: LazyBuffer<I>, - state: PermutationState, -} - -impl<I> Clone for Permutations<I> - where I: Clone + Iterator, - I::Item: Clone, -{ - clone_fields!(vals, state); -} - -#[derive(Clone, Debug)] -enum PermutationState { - StartUnknownLen { - k: usize, - }, - OngoingUnknownLen { - k: usize, - min_n: usize, - }, - Complete(CompleteState), - Empty, -} - -#[derive(Clone, Debug)] -enum CompleteState { - Start { - n: usize, - k: usize, - }, - Ongoing { - indices: Vec<usize>, - cycles: Vec<usize>, - } -} - -enum CompleteStateRemaining { - Known(usize), - Overflow, -} - -impl<I> fmt::Debug for Permutations<I> - where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(Permutations, vals, state); -} - -pub fn permutations<I: Iterator>(iter: I, k: usize) -> Permutations<I> { - let mut vals = LazyBuffer::new(iter); - - if k == 0 { - // Special case, yields single empty vec; `n` is irrelevant - let state = PermutationState::Complete(CompleteState::Start { n: 0, k: 0 }); - - return Permutations { - vals, - state - }; - } - - let mut enough_vals = true; - - while vals.len() < k { - if !vals.get_next() { - enough_vals = false; - break; - } - } - - let state = if enough_vals { - PermutationState::StartUnknownLen { k } - } else { - PermutationState::Empty - }; - - Permutations { - vals, - state - } -} - -impl<I> Iterator for Permutations<I> -where - I: Iterator, - I::Item: Clone -{ - type Item = Vec<I::Item>; - - fn next(&mut self) -> Option<Self::Item> { - self.advance(); - - let &mut Permutations { ref vals, ref state } = self; - - match *state { - PermutationState::StartUnknownLen { .. } => panic!("unexpected iterator state"), - PermutationState::OngoingUnknownLen { k, min_n } => { - let latest_idx = min_n - 1; - let indices = (0..(k - 1)).chain(once(latest_idx)); - - Some(indices.map(|i| vals[i].clone()).collect()) - } - PermutationState::Complete(CompleteState::Ongoing { ref indices, ref cycles }) => { - let k = cycles.len(); - Some(indices[0..k].iter().map(|&i| vals[i].clone()).collect()) - }, - PermutationState::Complete(CompleteState::Start { .. }) | PermutationState::Empty => None - } - } - - fn count(self) -> usize { - fn from_complete(complete_state: CompleteState) -> usize { - match complete_state.remaining() { - CompleteStateRemaining::Known(count) => count, - CompleteStateRemaining::Overflow => { - panic!("Iterator count greater than usize::MAX"); - } - } - } - - let Permutations { vals, state } = self; - match state { - PermutationState::StartUnknownLen { k } => { - let n = vals.len() + vals.it.count(); - let complete_state = CompleteState::Start { n, k }; - - from_complete(complete_state) - } - PermutationState::OngoingUnknownLen { k, min_n } => { - let prev_iteration_count = min_n - k + 1; - let n = vals.len() + vals.it.count(); - let complete_state = CompleteState::Start { n, k }; - - from_complete(complete_state) - prev_iteration_count - }, - PermutationState::Complete(state) => from_complete(state), - PermutationState::Empty => 0 - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - match self.state { - PermutationState::StartUnknownLen { .. } | - PermutationState::OngoingUnknownLen { .. } => (0, None), // TODO can we improve this lower bound? - PermutationState::Complete(ref state) => match state.remaining() { - CompleteStateRemaining::Known(count) => (count, Some(count)), - CompleteStateRemaining::Overflow => (::std::usize::MAX, None) - } - PermutationState::Empty => (0, Some(0)) - } - } -} - -impl<I> Permutations<I> -where - I: Iterator, - I::Item: Clone -{ - fn advance(&mut self) { - let &mut Permutations { ref mut vals, ref mut state } = self; - - *state = match *state { - PermutationState::StartUnknownLen { k } => { - PermutationState::OngoingUnknownLen { k, min_n: k } - } - PermutationState::OngoingUnknownLen { k, min_n } => { - if vals.get_next() { - PermutationState::OngoingUnknownLen { k, min_n: min_n + 1 } - } else { - let n = min_n; - let prev_iteration_count = n - k + 1; - let mut complete_state = CompleteState::Start { n, k }; - - // Advance the complete-state iterator to the correct point - for _ in 0..(prev_iteration_count + 1) { - complete_state.advance(); - } - - PermutationState::Complete(complete_state) - } - } - PermutationState::Complete(ref mut state) => { - state.advance(); - - return; - } - PermutationState::Empty => { return; } - }; - } -} - -impl CompleteState { - fn advance(&mut self) { - *self = match *self { - CompleteState::Start { n, k } => { - let indices = (0..n).collect(); - let cycles = ((n - k)..n).rev().collect(); - - CompleteState::Ongoing { - cycles, - indices - } - }, - CompleteState::Ongoing { ref mut indices, ref mut cycles } => { - let n = indices.len(); - let k = cycles.len(); - - for i in (0..k).rev() { - if cycles[i] == 0 { - cycles[i] = n - i - 1; - - let to_push = indices.remove(i); - indices.push(to_push); - } else { - let swap_index = n - cycles[i]; - indices.swap(i, swap_index); - - cycles[i] -= 1; - return; - } - } - - CompleteState::Start { n, k } - } - } - } - - fn remaining(&self) -> CompleteStateRemaining { - use self::CompleteStateRemaining::{Known, Overflow}; - - match *self { - CompleteState::Start { n, k } => { - if n < k { - return Known(0); - } - - let count: Option<usize> = (n - k + 1..n + 1).fold(Some(1), |acc, i| { - acc.and_then(|acc| acc.checked_mul(i)) - }); - - match count { - Some(count) => Known(count), - None => Overflow - } - } - CompleteState::Ongoing { ref indices, ref cycles } => { - let mut count: usize = 0; - - for (i, &c) in cycles.iter().enumerate() { - let radix = indices.len() - i; - let next_count = count.checked_mul(radix) - .and_then(|count| count.checked_add(c)); - - count = match next_count { - Some(count) => count, - None => { return Overflow; } - }; - } - - Known(count) - } - } - } -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/powerset.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/powerset.rs deleted file mode 100644 index 4d7685b1..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/powerset.rs +++ /dev/null
@@ -1,90 +0,0 @@ -use std::fmt; -use std::iter::FusedIterator; -use std::usize; -use alloc::vec::Vec; - -use super::combinations::{Combinations, combinations}; -use super::size_hint; - -/// An iterator to iterate through the powerset of the elements from an iterator. -/// -/// See [`.powerset()`](crate::Itertools::powerset) for more -/// information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Powerset<I: Iterator> { - combs: Combinations<I>, - // Iterator `position` (equal to count of yielded elements). - pos: usize, -} - -impl<I> Clone for Powerset<I> - where I: Clone + Iterator, - I::Item: Clone, -{ - clone_fields!(combs, pos); -} - -impl<I> fmt::Debug for Powerset<I> - where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(Powerset, combs, pos); -} - -/// Create a new `Powerset` from a clonable iterator. -pub fn powerset<I>(src: I) -> Powerset<I> - where I: Iterator, - I::Item: Clone, -{ - Powerset { - combs: combinations(src, 0), - pos: 0, - } -} - -impl<I> Iterator for Powerset<I> - where - I: Iterator, - I::Item: Clone, -{ - type Item = Vec<I::Item>; - - fn next(&mut self) -> Option<Self::Item> { - if let Some(elt) = self.combs.next() { - self.pos = self.pos.saturating_add(1); - Some(elt) - } else if self.combs.k() < self.combs.n() - || self.combs.k() == 0 - { - self.combs.reset(self.combs.k() + 1); - self.combs.next().map(|elt| { - self.pos = self.pos.saturating_add(1); - elt - }) - } else { - None - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - // Total bounds for source iterator. - let src_total = size_hint::add_scalar(self.combs.src().size_hint(), self.combs.n()); - - // Total bounds for self ( length(powerset(set) == 2 ^ length(set) ) - let self_total = size_hint::pow_scalar_base(2, src_total); - - if self.pos < usize::MAX { - // Subtract count of elements already yielded from total. - size_hint::sub_scalar(self_total, self.pos) - } else { - // Fallback: self.pos is saturated and no longer reliable. - (0, self_total.1) - } - } -} - -impl<I> FusedIterator for Powerset<I> - where - I: Iterator, - I::Item: Clone, -{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/process_results_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/process_results_impl.rs deleted file mode 100644 index 713db45..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/process_results_impl.rs +++ /dev/null
@@ -1,68 +0,0 @@ -#[cfg(doc)] -use crate::Itertools; - -/// An iterator that produces only the `T` values as long as the -/// inner iterator produces `Ok(T)`. -/// -/// Used by [`process_results`](crate::process_results), see its docs -/// for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[derive(Debug)] -pub struct ProcessResults<'a, I, E: 'a> { - error: &'a mut Result<(), E>, - iter: I, -} - -impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E> - where I: Iterator<Item = Result<T, E>> -{ - type Item = T; - - fn next(&mut self) -> Option<Self::Item> { - match self.iter.next() { - Some(Ok(x)) => Some(x), - Some(Err(e)) => { - *self.error = Err(e); - None - } - None => None, - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - (0, self.iter.size_hint().1) - } - - fn fold<B, F>(mut self, init: B, mut f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - let error = self.error; - self.iter - .try_fold(init, |acc, opt| match opt { - Ok(x) => Ok(f(acc, x)), - Err(e) => { - *error = Err(e); - Err(acc) - } - }) - .unwrap_or_else(|e| e) - } -} - -/// “Lift” a function of the values of an iterator so that it can process -/// an iterator of `Result` values instead. -/// -/// [`IntoIterator`] enabled version of [`Itertools::process_results`]. -pub fn process_results<I, F, T, E, R>(iterable: I, processor: F) -> Result<R, E> - where I: IntoIterator<Item = Result<T, E>>, - F: FnOnce(ProcessResults<I::IntoIter, E>) -> R -{ - let iter = iterable.into_iter(); - let mut error = Ok(()); - - let result = processor(ProcessResults { error: &mut error, iter }); - - error.map(|_| result) -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/repeatn.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/repeatn.rs deleted file mode 100644 index e025f6f..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/repeatn.rs +++ /dev/null
@@ -1,59 +0,0 @@ -use std::iter::FusedIterator; - -/// An iterator that produces *n* repetitions of an element. -/// -/// See [`repeat_n()`](crate::repeat_n) for more information. -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[derive(Clone, Debug)] -pub struct RepeatN<A> { - elt: Option<A>, - n: usize, -} - -/// Create an iterator that produces `n` repetitions of `element`. -pub fn repeat_n<A>(element: A, n: usize) -> RepeatN<A> - where A: Clone, -{ - if n == 0 { - RepeatN { elt: None, n, } - } else { - RepeatN { elt: Some(element), n, } - } -} - -impl<A> Iterator for RepeatN<A> - where A: Clone -{ - type Item = A; - - fn next(&mut self) -> Option<Self::Item> { - if self.n > 1 { - self.n -= 1; - self.elt.as_ref().cloned() - } else { - self.n = 0; - self.elt.take() - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - (self.n, Some(self.n)) - } -} - -impl<A> DoubleEndedIterator for RepeatN<A> - where A: Clone -{ - #[inline] - fn next_back(&mut self) -> Option<Self::Item> { - self.next() - } -} - -impl<A> ExactSizeIterator for RepeatN<A> - where A: Clone -{} - -impl<A> FusedIterator for RepeatN<A> - where A: Clone -{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/take_while_inclusive.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/take_while_inclusive.rs deleted file mode 100644 index e2a7479..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/take_while_inclusive.rs +++ /dev/null
@@ -1,68 +0,0 @@ -use core::iter::FusedIterator; -use std::fmt; - -/// An iterator adaptor that consumes elements while the given predicate is -/// `true`, including the element for which the predicate first returned -/// `false`. -/// -/// See [`.take_while_inclusive()`](crate::Itertools::take_while_inclusive) -/// for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct TakeWhileInclusive<'a, I: 'a, F> { - iter: &'a mut I, - predicate: F, - done: bool, -} - -impl<'a, I, F> TakeWhileInclusive<'a, I, F> -where - I: Iterator, - F: FnMut(&I::Item) -> bool, -{ - /// Create a new [`TakeWhileInclusive`] from an iterator and a predicate. - pub fn new(iter: &'a mut I, predicate: F) -> Self { - Self { iter, predicate, done: false} - } -} - -impl<'a, I, F> fmt::Debug for TakeWhileInclusive<'a, I, F> - where I: Iterator + fmt::Debug, -{ - debug_fmt_fields!(TakeWhileInclusive, iter); -} - -impl<'a, I, F> Iterator for TakeWhileInclusive<'a, I, F> -where - I: Iterator, - F: FnMut(&I::Item) -> bool -{ - type Item = I::Item; - - fn next(&mut self) -> Option<Self::Item> { - if self.done { - None - } else { - self.iter.next().map(|item| { - if !(self.predicate)(&item) { - self.done = true; - } - item - }) - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - if self.done { - (0, Some(0)) - } else { - (0, self.iter.size_hint().1) - } - } -} - -impl<I, F> FusedIterator for TakeWhileInclusive<'_, I, F> -where - I: Iterator, - F: FnMut(&I::Item) -> bool -{ -} \ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tuple_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tuple_impl.rs deleted file mode 100644 index fdf0865..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tuple_impl.rs +++ /dev/null
@@ -1,331 +0,0 @@ -//! Some iterator that produces tuples - -use std::iter::Fuse; -use std::iter::FusedIterator; -use std::iter::Take; -use std::iter::Cycle; -use std::marker::PhantomData; - -// `HomogeneousTuple` is a public facade for `TupleCollect`, allowing -// tuple-related methods to be used by clients in generic contexts, while -// hiding the implementation details of `TupleCollect`. -// See https://github.com/rust-itertools/itertools/issues/387 - -/// Implemented for homogeneous tuples of size up to 12. -pub trait HomogeneousTuple - : TupleCollect -{} - -impl<T: TupleCollect> HomogeneousTuple for T {} - -/// An iterator over a incomplete tuple. -/// -/// See [`.tuples()`](crate::Itertools::tuples) and -/// [`Tuples::into_buffer()`]. -#[derive(Clone, Debug)] -pub struct TupleBuffer<T> - where T: HomogeneousTuple -{ - cur: usize, - buf: T::Buffer, -} - -impl<T> TupleBuffer<T> - where T: HomogeneousTuple -{ - fn new(buf: T::Buffer) -> Self { - TupleBuffer { - cur: 0, - buf, - } - } -} - -impl<T> Iterator for TupleBuffer<T> - where T: HomogeneousTuple -{ - type Item = T::Item; - - fn next(&mut self) -> Option<Self::Item> { - let s = self.buf.as_mut(); - if let Some(ref mut item) = s.get_mut(self.cur) { - self.cur += 1; - item.take() - } else { - None - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - let buffer = &self.buf.as_ref()[self.cur..]; - let len = if buffer.is_empty() { - 0 - } else { - buffer.iter() - .position(|x| x.is_none()) - .unwrap_or_else(|| buffer.len()) - }; - (len, Some(len)) - } -} - -impl<T> ExactSizeIterator for TupleBuffer<T> - where T: HomogeneousTuple -{ -} - -/// An iterator that groups the items in tuples of a specific size. -/// -/// See [`.tuples()`](crate::Itertools::tuples) for more information. -#[derive(Clone, Debug)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Tuples<I, T> - where I: Iterator<Item = T::Item>, - T: HomogeneousTuple -{ - iter: Fuse<I>, - buf: T::Buffer, -} - -/// Create a new tuples iterator. -pub fn tuples<I, T>(iter: I) -> Tuples<I, T> - where I: Iterator<Item = T::Item>, - T: HomogeneousTuple -{ - Tuples { - iter: iter.fuse(), - buf: Default::default(), - } -} - -impl<I, T> Iterator for Tuples<I, T> - where I: Iterator<Item = T::Item>, - T: HomogeneousTuple -{ - type Item = T; - - fn next(&mut self) -> Option<Self::Item> { - T::collect_from_iter(&mut self.iter, &mut self.buf) - } -} - -impl<I, T> Tuples<I, T> - where I: Iterator<Item = T::Item>, - T: HomogeneousTuple -{ - /// Return a buffer with the produced items that was not enough to be grouped in a tuple. - /// - /// ``` - /// use itertools::Itertools; - /// - /// let mut iter = (0..5).tuples(); - /// assert_eq!(Some((0, 1, 2)), iter.next()); - /// assert_eq!(None, iter.next()); - /// itertools::assert_equal(vec![3, 4], iter.into_buffer()); - /// ``` - pub fn into_buffer(self) -> TupleBuffer<T> { - TupleBuffer::new(self.buf) - } -} - - -/// An iterator over all contiguous windows that produces tuples of a specific size. -/// -/// See [`.tuple_windows()`](crate::Itertools::tuple_windows) for more -/// information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[derive(Clone, Debug)] -pub struct TupleWindows<I, T> - where I: Iterator<Item = T::Item>, - T: HomogeneousTuple -{ - iter: I, - last: Option<T>, -} - -/// Create a new tuple windows iterator. -pub fn tuple_windows<I, T>(mut iter: I) -> TupleWindows<I, T> - where I: Iterator<Item = T::Item>, - T: HomogeneousTuple, - T::Item: Clone -{ - use std::iter::once; - - let mut last = None; - if T::num_items() != 1 { - // put in a duplicate item in front of the tuple; this simplifies - // .next() function. - if let Some(item) = iter.next() { - let iter = once(item.clone()).chain(once(item)).chain(&mut iter); - last = T::collect_from_iter_no_buf(iter); - } - } - - TupleWindows { - iter, - last, - } -} - -impl<I, T> Iterator for TupleWindows<I, T> - where I: Iterator<Item = T::Item>, - T: HomogeneousTuple + Clone, - T::Item: Clone -{ - type Item = T; - - fn next(&mut self) -> Option<Self::Item> { - if T::num_items() == 1 { - return T::collect_from_iter_no_buf(&mut self.iter) - } - if let Some(ref mut last) = self.last { - if let Some(new) = self.iter.next() { - last.left_shift_push(new); - return Some(last.clone()); - } - } - None - } -} - -impl<I, T> FusedIterator for TupleWindows<I, T> - where I: FusedIterator<Item = T::Item>, - T: HomogeneousTuple + Clone, - T::Item: Clone -{} - -/// An iterator over all windows, wrapping back to the first elements when the -/// window would otherwise exceed the length of the iterator, producing tuples -/// of a specific size. -/// -/// See [`.circular_tuple_windows()`](crate::Itertools::circular_tuple_windows) for more -/// information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[derive(Debug, Clone)] -pub struct CircularTupleWindows<I, T: Clone> - where I: Iterator<Item = T::Item> + Clone, - T: TupleCollect + Clone -{ - iter: Take<TupleWindows<Cycle<I>, T>>, - phantom_data: PhantomData<T> -} - -pub fn circular_tuple_windows<I, T>(iter: I) -> CircularTupleWindows<I, T> - where I: Iterator<Item = T::Item> + Clone + ExactSizeIterator, - T: TupleCollect + Clone, - T::Item: Clone -{ - let len = iter.len(); - let iter = tuple_windows(iter.cycle()).take(len); - - CircularTupleWindows { - iter, - phantom_data: PhantomData{} - } -} - -impl<I, T> Iterator for CircularTupleWindows<I, T> - where I: Iterator<Item = T::Item> + Clone, - T: TupleCollect + Clone, - T::Item: Clone -{ - type Item = T; - - fn next(&mut self) -> Option<Self::Item> { - self.iter.next() - } -} - -pub trait TupleCollect: Sized { - type Item; - type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>; - - fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self> - where I: IntoIterator<Item = Self::Item>; - - fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self> - where I: IntoIterator<Item = Self::Item>; - - fn num_items() -> usize; - - fn left_shift_push(&mut self, item: Self::Item); -} - -macro_rules! count_ident{ - () => {0}; - ($i0:ident, $($i:ident,)*) => {1 + count_ident!($($i,)*)}; -} -macro_rules! rev_for_each_ident{ - ($m:ident, ) => {}; - ($m:ident, $i0:ident, $($i:ident,)*) => { - rev_for_each_ident!($m, $($i,)*); - $m!($i0); - }; -} - -macro_rules! impl_tuple_collect { - ($dummy:ident,) => {}; // stop - ($dummy:ident, $($Y:ident,)*) => ( - impl_tuple_collect!($($Y,)*); - impl<A> TupleCollect for ($(ignore_ident!($Y, A),)*) { - type Item = A; - type Buffer = [Option<A>; count_ident!($($Y,)*) - 1]; - - #[allow(unused_assignments, unused_mut)] - fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self> - where I: IntoIterator<Item = A> - { - let mut iter = iter.into_iter(); - $( - let mut $Y = None; - )* - - loop { - $( - $Y = iter.next(); - if $Y.is_none() { - break - } - )* - return Some(($($Y.unwrap()),*,)) - } - - let mut i = 0; - let mut s = buf.as_mut(); - $( - if i < s.len() { - s[i] = $Y; - i += 1; - } - )* - return None; - } - - fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self> - where I: IntoIterator<Item = A> - { - let mut iter = iter.into_iter(); - - Some(($( - { let $Y = iter.next()?; $Y }, - )*)) - } - - fn num_items() -> usize { - count_ident!($($Y,)*) - } - - fn left_shift_push(&mut self, mut item: A) { - use std::mem::replace; - - let &mut ($(ref mut $Y),*,) = self; - macro_rules! replace_item{($i:ident) => { - item = replace($i, item); - }} - rev_for_each_ident!(replace_item, $($Y,)*); - drop(item); - } - } - ) -} -impl_tuple_collect!(dummy, a, b, c, d, e, f, g, h, i, j, k, l,);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/with_position.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/with_position.rs deleted file mode 100644 index dda9b25..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/with_position.rs +++ /dev/null
@@ -1,88 +0,0 @@ -use std::iter::{Fuse,Peekable, FusedIterator}; - -/// An iterator adaptor that wraps each element in an [`Position`]. -/// -/// Iterator element type is `(Position, I::Item)`. -/// -/// See [`.with_position()`](crate::Itertools::with_position) for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct WithPosition<I> - where I: Iterator, -{ - handled_first: bool, - peekable: Peekable<Fuse<I>>, -} - -impl<I> Clone for WithPosition<I> - where I: Clone + Iterator, - I::Item: Clone, -{ - clone_fields!(handled_first, peekable); -} - -/// Create a new `WithPosition` iterator. -pub fn with_position<I>(iter: I) -> WithPosition<I> - where I: Iterator, -{ - WithPosition { - handled_first: false, - peekable: iter.fuse().peekable(), - } -} - -/// The first component of the value yielded by `WithPosition`. -/// Indicates the position of this element in the iterator results. -/// -/// See [`.with_position()`](crate::Itertools::with_position) for more information. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Position { - /// This is the first element. - First, - /// This is neither the first nor the last element. - Middle, - /// This is the last element. - Last, - /// This is the only element. - Only, -} - -impl<I: Iterator> Iterator for WithPosition<I> { - type Item = (Position, I::Item); - - fn next(&mut self) -> Option<Self::Item> { - match self.peekable.next() { - Some(item) => { - if !self.handled_first { - // Haven't seen the first item yet, and there is one to give. - self.handled_first = true; - // Peek to see if this is also the last item, - // in which case tag it as `Only`. - match self.peekable.peek() { - Some(_) => Some((Position::First, item)), - None => Some((Position::Only, item)), - } - } else { - // Have seen the first item, and there's something left. - // Peek to see if this is the last item. - match self.peekable.peek() { - Some(_) => Some((Position::Middle, item)), - None => Some((Position::Last, item)), - } - } - } - // Iterator is finished. - None => None, - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - self.peekable.size_hint() - } -} - -impl<I> ExactSizeIterator for WithPosition<I> - where I: ExactSizeIterator, -{ } - -impl<I: Iterator> FusedIterator for WithPosition<I> -{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_longest.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_longest.rs deleted file mode 100644 index cb9a7bac..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_longest.rs +++ /dev/null
@@ -1,83 +0,0 @@ -use std::cmp::Ordering::{Equal, Greater, Less}; -use super::size_hint; -use std::iter::{Fuse, FusedIterator}; - -use crate::either_or_both::EitherOrBoth; - -// ZipLongest originally written by SimonSapin, -// and dedicated to itertools https://github.com/rust-lang/rust/pull/19283 - -/// An iterator which iterates two other iterators simultaneously -/// -/// This iterator is *fused*. -/// -/// See [`.zip_longest()`](crate::Itertools::zip_longest) for more information. -#[derive(Clone, Debug)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct ZipLongest<T, U> { - a: Fuse<T>, - b: Fuse<U>, -} - -/// Create a new `ZipLongest` iterator. -pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U> - where T: Iterator, - U: Iterator -{ - ZipLongest { - a: a.fuse(), - b: b.fuse(), - } -} - -impl<T, U> Iterator for ZipLongest<T, U> - where T: Iterator, - U: Iterator -{ - type Item = EitherOrBoth<T::Item, U::Item>; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - match (self.a.next(), self.b.next()) { - (None, None) => None, - (Some(a), None) => Some(EitherOrBoth::Left(a)), - (None, Some(b)) => Some(EitherOrBoth::Right(b)), - (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)), - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - size_hint::max(self.a.size_hint(), self.b.size_hint()) - } -} - -impl<T, U> DoubleEndedIterator for ZipLongest<T, U> - where T: DoubleEndedIterator + ExactSizeIterator, - U: DoubleEndedIterator + ExactSizeIterator -{ - #[inline] - fn next_back(&mut self) -> Option<Self::Item> { - match self.a.len().cmp(&self.b.len()) { - Equal => match (self.a.next_back(), self.b.next_back()) { - (None, None) => None, - (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)), - // These can only happen if .len() is inconsistent with .next_back() - (Some(a), None) => Some(EitherOrBoth::Left(a)), - (None, Some(b)) => Some(EitherOrBoth::Right(b)), - }, - Greater => self.a.next_back().map(EitherOrBoth::Left), - Less => self.b.next_back().map(EitherOrBoth::Right), - } - } -} - -impl<T, U> ExactSizeIterator for ZipLongest<T, U> - where T: ExactSizeIterator, - U: ExactSizeIterator -{} - -impl<T, U> FusedIterator for ZipLongest<T, U> - where T: Iterator, - U: Iterator -{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/flatten_ok.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/flatten_ok.rs deleted file mode 100644 index bf835b5..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/flatten_ok.rs +++ /dev/null
@@ -1,76 +0,0 @@ -use itertools::{assert_equal, Itertools}; -use std::{ops::Range, vec::IntoIter}; - -fn mix_data() -> IntoIter<Result<Range<i32>, bool>> { - vec![Ok(0..2), Err(false), Ok(2..4), Err(true), Ok(4..6)].into_iter() -} - -fn ok_data() -> IntoIter<Result<Range<i32>, bool>> { - vec![Ok(0..2), Ok(2..4), Ok(4..6)].into_iter() -} - -#[test] -fn flatten_ok_mixed_expected_forward() { - assert_equal( - mix_data().flatten_ok(), - vec![ - Ok(0), - Ok(1), - Err(false), - Ok(2), - Ok(3), - Err(true), - Ok(4), - Ok(5), - ], - ); -} - -#[test] -fn flatten_ok_mixed_expected_reverse() { - assert_equal( - mix_data().flatten_ok().rev(), - vec![ - Ok(5), - Ok(4), - Err(true), - Ok(3), - Ok(2), - Err(false), - Ok(1), - Ok(0), - ], - ); -} - -#[test] -fn flatten_ok_collect_mixed_forward() { - assert_eq!( - mix_data().flatten_ok().collect::<Result<Vec<_>, _>>(), - Err(false) - ); -} - -#[test] -fn flatten_ok_collect_mixed_reverse() { - assert_eq!( - mix_data().flatten_ok().rev().collect::<Result<Vec<_>, _>>(), - Err(true) - ); -} - -#[test] -fn flatten_ok_collect_ok_forward() { - assert_eq!( - ok_data().flatten_ok().collect::<Result<Vec<_>, _>>(), - Ok((0..6).collect()) - ); -} - -#[test] -fn flatten_ok_collect_ok_reverse() { - assert_eq!( - ok_data().flatten_ok().rev().collect::<Result<Vec<_>, _>>(), - Ok((0..6).rev().collect()) - ); -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/macros_hygiene.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/macros_hygiene.rs deleted file mode 100644 index d111124..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/macros_hygiene.rs +++ /dev/null
@@ -1,13 +0,0 @@ -#[test] -fn iproduct_hygiene() { - let _ = itertools::iproduct!(0..6); - let _ = itertools::iproduct!(0..6, 0..9); - let _ = itertools::iproduct!(0..6, 0..9, 0..12); -} - -#[test] -fn izip_hygiene() { - let _ = itertools::izip!(0..6); - let _ = itertools::izip!(0..6, 0..9); - let _ = itertools::izip!(0..6, 0..9, 0..12); -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/merge_join.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/merge_join.rs deleted file mode 100644 index 3280b7d..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/merge_join.rs +++ /dev/null
@@ -1,108 +0,0 @@ -use itertools::EitherOrBoth; -use itertools::free::merge_join_by; - -#[test] -fn empty() { - let left: Vec<u32> = vec![]; - let right: Vec<u32> = vec![]; - let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![]; - let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) - .collect::<Vec<_>>(); - assert_eq!(expected_result, actual_result); -} - -#[test] -fn left_only() { - let left: Vec<u32> = vec![1,2,3]; - let right: Vec<u32> = vec![]; - let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![ - EitherOrBoth::Left(1), - EitherOrBoth::Left(2), - EitherOrBoth::Left(3) - ]; - let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) - .collect::<Vec<_>>(); - assert_eq!(expected_result, actual_result); -} - -#[test] -fn right_only() { - let left: Vec<u32> = vec![]; - let right: Vec<u32> = vec![1,2,3]; - let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![ - EitherOrBoth::Right(1), - EitherOrBoth::Right(2), - EitherOrBoth::Right(3) - ]; - let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) - .collect::<Vec<_>>(); - assert_eq!(expected_result, actual_result); -} - -#[test] -fn first_left_then_right() { - let left: Vec<u32> = vec![1,2,3]; - let right: Vec<u32> = vec![4,5,6]; - let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![ - EitherOrBoth::Left(1), - EitherOrBoth::Left(2), - EitherOrBoth::Left(3), - EitherOrBoth::Right(4), - EitherOrBoth::Right(5), - EitherOrBoth::Right(6) - ]; - let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) - .collect::<Vec<_>>(); - assert_eq!(expected_result, actual_result); -} - -#[test] -fn first_right_then_left() { - let left: Vec<u32> = vec![4,5,6]; - let right: Vec<u32> = vec![1,2,3]; - let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![ - EitherOrBoth::Right(1), - EitherOrBoth::Right(2), - EitherOrBoth::Right(3), - EitherOrBoth::Left(4), - EitherOrBoth::Left(5), - EitherOrBoth::Left(6) - ]; - let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) - .collect::<Vec<_>>(); - assert_eq!(expected_result, actual_result); -} - -#[test] -fn interspersed_left_and_right() { - let left: Vec<u32> = vec![1,3,5]; - let right: Vec<u32> = vec![2,4,6]; - let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![ - EitherOrBoth::Left(1), - EitherOrBoth::Right(2), - EitherOrBoth::Left(3), - EitherOrBoth::Right(4), - EitherOrBoth::Left(5), - EitherOrBoth::Right(6) - ]; - let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) - .collect::<Vec<_>>(); - assert_eq!(expected_result, actual_result); -} - -#[test] -fn overlapping_left_and_right() { - let left: Vec<u32> = vec![1,3,4,6]; - let right: Vec<u32> = vec![2,3,4,5]; - let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![ - EitherOrBoth::Left(1), - EitherOrBoth::Right(2), - EitherOrBoth::Both(3, 3), - EitherOrBoth::Both(4, 4), - EitherOrBoth::Right(5), - EitherOrBoth::Left(6) - ]; - let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) - .collect::<Vec<_>>(); - assert_eq!(expected_result, actual_result); -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/specializations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/specializations.rs deleted file mode 100644 index 057e11c..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/specializations.rs +++ /dev/null
@@ -1,153 +0,0 @@ -use itertools::Itertools; -use std::fmt::Debug; -use quickcheck::quickcheck; - -struct Unspecialized<I>(I); -impl<I> Iterator for Unspecialized<I> -where - I: Iterator, -{ - type Item = I::Item; - - #[inline(always)] - fn next(&mut self) -> Option<Self::Item> { - self.0.next() - } -} - -macro_rules! check_specialized { - ($src:expr, |$it:pat| $closure:expr) => { - let $it = $src.clone(); - let v1 = $closure; - - let $it = Unspecialized($src.clone()); - let v2 = $closure; - - assert_eq!(v1, v2); - } -} - -fn test_specializations<IterItem, Iter>( - it: &Iter, -) where - IterItem: Eq + Debug + Clone, - Iter: Iterator<Item = IterItem> + Clone, -{ - check_specialized!(it, |i| i.count()); - check_specialized!(it, |i| i.last()); - check_specialized!(it, |i| i.collect::<Vec<_>>()); - check_specialized!(it, |i| { - let mut parameters_from_fold = vec![]; - let fold_result = i.fold(vec![], |mut acc, v: IterItem| { - parameters_from_fold.push((acc.clone(), v.clone())); - acc.push(v); - acc - }); - (parameters_from_fold, fold_result) - }); - check_specialized!(it, |mut i| { - let mut parameters_from_all = vec![]; - let first = i.next(); - let all_result = i.all(|x| { - parameters_from_all.push(x.clone()); - Some(x)==first - }); - (parameters_from_all, all_result) - }); - let size = it.clone().count(); - for n in 0..size + 2 { - check_specialized!(it, |mut i| i.nth(n)); - } - // size_hint is a bit harder to check - let mut it_sh = it.clone(); - for n in 0..size + 2 { - let len = it_sh.clone().count(); - let (min, max) = it_sh.size_hint(); - assert_eq!(size - n.min(size), len); - assert!(min <= len); - if let Some(max) = max { - assert!(len <= max); - } - it_sh.next(); - } -} - -quickcheck! { - fn intersperse(v: Vec<u8>) -> () { - test_specializations(&v.into_iter().intersperse(0)); - } -} - -quickcheck! { - fn put_back_qc(test_vec: Vec<i32>) -> () { - test_specializations(&itertools::put_back(test_vec.iter())); - let mut pb = itertools::put_back(test_vec.into_iter()); - pb.put_back(1); - test_specializations(&pb); - } -} - -quickcheck! { - fn merge_join_by_qc(i1: Vec<usize>, i2: Vec<usize>) -> () { - test_specializations(&i1.into_iter().merge_join_by(i2.into_iter(), std::cmp::Ord::cmp)); - } -} - -quickcheck! { - fn map_into(v: Vec<u8>) -> () { - test_specializations(&v.into_iter().map_into::<u32>()); - } -} - -quickcheck! { - fn map_ok(v: Vec<Result<u8, char>>) -> () { - test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1))); - } -} - -quickcheck! { - fn process_results(v: Vec<Result<u8, u8>>) -> () { - helper(v.iter().copied()); - helper(v.iter().copied().filter(Result::is_ok)); - - fn helper(it: impl Iterator<Item = Result<u8, u8>> + Clone) { - macro_rules! check_results_specialized { - ($src:expr, |$it:pat| $closure:expr) => { - assert_eq!( - itertools::process_results($src.clone(), |$it| $closure), - itertools::process_results($src.clone(), |i| { - let $it = Unspecialized(i); - $closure - }), - ) - } - } - - check_results_specialized!(it, |i| i.count()); - check_results_specialized!(it, |i| i.last()); - check_results_specialized!(it, |i| i.collect::<Vec<_>>()); - check_results_specialized!(it, |i| { - let mut parameters_from_fold = vec![]; - let fold_result = i.fold(vec![], |mut acc, v| { - parameters_from_fold.push((acc.clone(), v)); - acc.push(v); - acc - }); - (parameters_from_fold, fold_result) - }); - check_results_specialized!(it, |mut i| { - let mut parameters_from_all = vec![]; - let first = i.next(); - let all_result = i.all(|x| { - parameters_from_all.push(x); - Some(x)==first - }); - (parameters_from_all, all_result) - }); - let size = it.clone().count(); - for n in 0..size + 2 { - check_results_specialized!(it, |mut i| i.nth(n)); - } - } - } -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_std.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_std.rs deleted file mode 100644 index 77207d87..0000000 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_std.rs +++ /dev/null
@@ -1,1184 +0,0 @@ -use quickcheck as qc; -use rand::{distributions::{Distribution, Standard}, Rng, SeedableRng, rngs::StdRng}; -use rand::{seq::SliceRandom, thread_rng}; -use std::{cmp::min, fmt::Debug, marker::PhantomData}; -use itertools as it; -use crate::it::Itertools; -use crate::it::ExactlyOneError; -use crate::it::multizip; -use crate::it::multipeek; -use crate::it::peek_nth; -use crate::it::free::rciter; -use crate::it::free::put_back_n; -use crate::it::FoldWhile; -use crate::it::cloned; -use crate::it::iproduct; -use crate::it::izip; - -#[test] -fn product3() { - let prod = iproduct!(0..3, 0..2, 0..2); - assert_eq!(prod.size_hint(), (12, Some(12))); - let v = prod.collect_vec(); - for i in 0..3 { - for j in 0..2 { - for k in 0..2 { - assert!((i, j, k) == v[(i * 2 * 2 + j * 2 + k) as usize]); - } - } - } - for (_, _, _, _) in iproduct!(0..3, 0..2, 0..2, 0..3) { - /* test compiles */ - } -} - -#[test] -fn interleave_shortest() { - let v0: Vec<i32> = vec![0, 2, 4]; - let v1: Vec<i32> = vec![1, 3, 5, 7]; - let it = v0.into_iter().interleave_shortest(v1.into_iter()); - assert_eq!(it.size_hint(), (6, Some(6))); - assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5]); - - let v0: Vec<i32> = vec![0, 2, 4, 6, 8]; - let v1: Vec<i32> = vec![1, 3, 5]; - let it = v0.into_iter().interleave_shortest(v1.into_iter()); - assert_eq!(it.size_hint(), (7, Some(7))); - assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5, 6]); - - let i0 = ::std::iter::repeat(0); - let v1: Vec<_> = vec![1, 3, 5]; - let it = i0.interleave_shortest(v1.into_iter()); - assert_eq!(it.size_hint(), (7, Some(7))); - - let v0: Vec<_> = vec![0, 2, 4]; - let i1 = ::std::iter::repeat(1); - let it = v0.into_iter().interleave_shortest(i1); - assert_eq!(it.size_hint(), (6, Some(6))); -} - -#[test] -fn duplicates_by() { - let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"]; - let ys = ["aa", "bbbb", "cccc"]; - it::assert_equal(ys.iter(), xs.iter().duplicates_by(|x| x[..2].to_string())); - it::assert_equal(ys.iter(), xs.iter().rev().duplicates_by(|x| x[..2].to_string()).rev()); - let ys_rev = ["ccc", "aa", "bbbbb"]; - it::assert_equal(ys_rev.iter(), xs.iter().duplicates_by(|x| x[..2].to_string()).rev()); -} - -#[test] -fn duplicates() { - let xs = [0, 1, 2, 3, 2, 1, 3]; - let ys = [2, 1, 3]; - it::assert_equal(ys.iter(), xs.iter().duplicates()); - it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev()); - let ys_rev = [3, 2, 1]; - it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev()); - - let xs = [0, 1, 0, 1]; - let ys = [0, 1]; - it::assert_equal(ys.iter(), xs.iter().duplicates()); - it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev()); - let ys_rev = [1, 0]; - it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev()); - - let xs = vec![0, 1, 2, 1, 2]; - let ys = vec![1, 2]; - assert_eq!(ys, xs.iter().duplicates().cloned().collect_vec()); - assert_eq!(ys, xs.iter().rev().duplicates().rev().cloned().collect_vec()); - let ys_rev = vec![2, 1]; - assert_eq!(ys_rev, xs.iter().duplicates().rev().cloned().collect_vec()); -} - -#[test] -fn unique_by() { - let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"]; - let ys = ["aaa", "bbbbb", "ccc"]; - it::assert_equal(ys.iter(), xs.iter().unique_by(|x| x[..2].to_string())); - it::assert_equal(ys.iter(), xs.iter().rev().unique_by(|x| x[..2].to_string()).rev()); - let ys_rev = ["cccc", "aaaaa", "bbbb"]; - it::assert_equal(ys_rev.iter(), xs.iter().unique_by(|x| x[..2].to_string()).rev()); -} - -#[test] -fn unique() { - let xs = [0, 1, 2, 3, 2, 1, 3]; - let ys = [0, 1, 2, 3]; - it::assert_equal(ys.iter(), xs.iter().unique()); - it::assert_equal(ys.iter(), xs.iter().rev().unique().rev()); - let ys_rev = [3, 1, 2, 0]; - it::assert_equal(ys_rev.iter(), xs.iter().unique().rev()); - - let xs = [0, 1]; - let ys = [0, 1]; - it::assert_equal(ys.iter(), xs.iter().unique()); - it::assert_equal(ys.iter(), xs.iter().rev().unique().rev()); - let ys_rev = [1, 0]; - it::assert_equal(ys_rev.iter(), xs.iter().unique().rev()); -} - -#[test] -fn intersperse() { - let xs = ["a", "", "b", "c"]; - let v: Vec<&str> = xs.iter().cloned().intersperse(", ").collect(); - let text: String = v.concat(); - assert_eq!(text, "a, , b, c".to_string()); - - let ys = [0, 1, 2, 3]; - let mut it = ys[..0].iter().copied().intersperse(1); - assert!(it.next() == None); -} - -#[test] -fn dedup() { - let xs = [0, 1, 1, 1, 2, 1, 3, 3]; - let ys = [0, 1, 2, 1, 3]; - it::assert_equal(ys.iter(), xs.iter().dedup()); - let xs = [0, 0, 0, 0, 0]; - let ys = [0]; - it::assert_equal(ys.iter(), xs.iter().dedup()); - - let xs = [0, 1, 1, 1, 2, 1, 3, 3]; - let ys = [0, 1, 2, 1, 3]; - let mut xs_d = Vec::new(); - xs.iter().dedup().fold((), |(), &elt| xs_d.push(elt)); - assert_eq!(&xs_d, &ys); -} - -#[test] -fn coalesce() { - let data = vec![-1., -2., -3., 3., 1., 0., -1.]; - let it = data.iter().cloned().coalesce(|x, y| - if (x >= 0.) == (y >= 0.) { - Ok(x + y) - } else { - Err((x, y)) - } - ); - itertools::assert_equal(it.clone(), vec![-6., 4., -1.]); - assert_eq!( - it.fold(vec![], |mut v, n| { - v.push(n); - v - }), - vec![-6., 4., -1.] - ); -} - -#[test] -fn dedup_by() { - let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)]; - let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)]; - it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.1==y.1)); - let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]; - let ys = [(0, 1)]; - it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.0==y.0)); - - let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)]; - let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)]; - let mut xs_d = Vec::new(); - xs.iter().dedup_by(|x, y| x.1==y.1).fold((), |(), &elt| xs_d.push(elt)); - assert_eq!(&xs_d, &ys); -} - -#[test] -fn dedup_with_count() { - let xs: [i32; 8] = [0, 1, 1, 1, 2, 1, 3, 3]; - let ys: [(usize, &i32); 5] = [(1, &0), (3, &1), (1, &2), (1, &1), (2, &3)]; - - it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count()); - - let xs: [i32; 5] = [0, 0, 0, 0, 0]; - let ys: [(usize, &i32); 1] = [(5, &0)]; - - it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count()); -} - - -#[test] -fn dedup_by_with_count() { - let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)]; - let ys = [(1, &(0, 0)), (3, &(0, 1)), (1, &(0, 2)), (1, &(3, 1)), (2, &(0, 3))]; - - it::assert_equal(ys.iter().cloned(), xs.iter().dedup_by_with_count(|x, y| x.1==y.1)); - - let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]; - let ys = [( 5, &(0, 1))]; - - it::assert_equal(ys.iter().cloned(), xs.iter().dedup_by_with_count(|x, y| x.0==y.0)); -} - -#[test] -fn all_equal() { - assert!("".chars().all_equal()); - assert!("A".chars().all_equal()); - assert!(!"AABBCCC".chars().all_equal()); - assert!("AAAAAAA".chars().all_equal()); - for (_key, mut sub) in &"AABBCCC".chars().group_by(|&x| x) { - assert!(sub.all_equal()); - } -} - -#[test] -fn all_equal_value() { - assert_eq!("".chars().all_equal_value(), Err(None)); - assert_eq!("A".chars().all_equal_value(), Ok('A')); - assert_eq!("AABBCCC".chars().all_equal_value(), Err(Some(('A', 'B')))); - assert_eq!("AAAAAAA".chars().all_equal_value(), Ok('A')); - { - let mut it = [1,2,3].iter().copied(); - let result = it.all_equal_value(); - assert_eq!(result, Err(Some((1, 2)))); - let remaining = it.next(); - assert_eq!(remaining, Some(3)); - assert!(it.next().is_none()); - } -} - -#[test] -fn all_unique() { - assert!("ABCDEFGH".chars().all_unique()); - assert!(!"ABCDEFGA".chars().all_unique()); - assert!(::std::iter::empty::<usize>().all_unique()); -} - -#[test] -fn test_put_back_n() { - let xs = [0, 1, 1, 1, 2, 1, 3, 3]; - let mut pb = put_back_n(xs.iter().cloned()); - pb.next(); - pb.next(); - pb.put_back(1); - pb.put_back(0); - it::assert_equal(pb, xs.iter().cloned()); -} - -#[test] -fn tee() { - let xs = [0, 1, 2, 3]; - let (mut t1, mut t2) = xs.iter().cloned().tee(); - assert_eq!(t1.next(), Some(0)); - assert_eq!(t2.next(), Some(0)); - assert_eq!(t1.next(), Some(1)); - assert_eq!(t1.next(), Some(2)); - assert_eq!(t1.next(), Some(3)); - assert_eq!(t1.next(), None); - assert_eq!(t2.next(), Some(1)); - assert_eq!(t2.next(), Some(2)); - assert_eq!(t1.next(), None); - assert_eq!(t2.next(), Some(3)); - assert_eq!(t2.next(), None); - assert_eq!(t1.next(), None); - assert_eq!(t2.next(), None); - - let (t1, t2) = xs.iter().cloned().tee(); - it::assert_equal(t1, xs.iter().cloned()); - it::assert_equal(t2, xs.iter().cloned()); - - let (t1, t2) = xs.iter().cloned().tee(); - it::assert_equal(t1.zip(t2), xs.iter().cloned().zip(xs.iter().cloned())); -} - - -#[test] -fn test_rciter() { - let xs = [0, 1, 1, 1, 2, 1, 3, 5, 6]; - - let mut r1 = rciter(xs.iter().cloned()); - let mut r2 = r1.clone(); - assert_eq!(r1.next(), Some(0)); - assert_eq!(r2.next(), Some(1)); - let mut z = r1.zip(r2); - assert_eq!(z.next(), Some((1, 1))); - assert_eq!(z.next(), Some((2, 1))); - assert_eq!(z.next(), Some((3, 5))); - assert_eq!(z.next(), None); - - // test intoiterator - let r1 = rciter(0..5); - let mut z = izip!(&r1, r1); - assert_eq!(z.next(), Some((0, 1))); -} - -#[allow(deprecated)] -#[test] -fn trait_pointers() { - struct ByRef<'r, I: ?Sized>(&'r mut I) ; - - impl<'r, X, I: ?Sized> Iterator for ByRef<'r, I> where - I: 'r + Iterator<Item=X> - { - type Item = X; - fn next(&mut self) -> Option<Self::Item> - { - self.0.next() - } - } - - let mut it = Box::new(0..10) as Box<dyn Iterator<Item=i32>>; - assert_eq!(it.next(), Some(0)); - - { - /* make sure foreach works on non-Sized */ - let jt: &mut dyn Iterator<Item = i32> = &mut *it; - assert_eq!(jt.next(), Some(1)); - - { - let mut r = ByRef(jt); - assert_eq!(r.next(), Some(2)); - } - - assert_eq!(jt.find_position(|x| *x == 4), Some((1, 4))); - jt.foreach(|_| ()); - } -} - -#[test] -fn merge_by() { - let odd : Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")]; - let even = vec![(2, "foo"), (4, "bar"), (6, "baz")]; - let expected = vec![(1, "hello"), (2, "foo"), (3, "world"), (4, "bar"), (5, "!"), (6, "baz")]; - let results = odd.iter().merge_by(even.iter(), |a, b| a.0 <= b.0); - it::assert_equal(results, expected.iter()); -} - -#[test] -fn merge_by_btree() { - use std::collections::BTreeMap; - let mut bt1 = BTreeMap::new(); - bt1.insert("hello", 1); - bt1.insert("world", 3); - let mut bt2 = BTreeMap::new(); - bt2.insert("foo", 2); - bt2.insert("bar", 4); - let results = bt1.into_iter().merge_by(bt2.into_iter(), |a, b| a.0 <= b.0 ); - let expected = vec![("bar", 4), ("foo", 2), ("hello", 1), ("world", 3)]; - it::assert_equal(results, expected.into_iter()); -} - -#[allow(deprecated)] -#[test] -fn kmerge() { - let its = (0..4).map(|s| (s..10).step(4)); - - it::assert_equal(its.kmerge(), 0..10); -} - -#[allow(deprecated)] -#[test] -fn kmerge_2() { - let its = vec![3, 2, 1, 0].into_iter().map(|s| (s..10).step(4)); - - it::assert_equal(its.kmerge(), 0..10); -} - -#[test] -fn kmerge_empty() { - let its = (0..4).map(|_| 0..0); - assert_eq!(its.kmerge().next(), None); -} - -#[test] -fn kmerge_size_hint() { - let its = (0..5).map(|_| (0..10)); - assert_eq!(its.kmerge().size_hint(), (50, Some(50))); -} - -#[test] -fn kmerge_empty_size_hint() { - let its = (0..5).map(|_| (0..0)); - assert_eq!(its.kmerge().size_hint(), (0, Some(0))); -} - -#[test] -fn join() { - let many = [1, 2, 3]; - let one = [1]; - let none: Vec<i32> = vec![]; - - assert_eq!(many.iter().join(", "), "1, 2, 3"); - assert_eq!( one.iter().join(", "), "1"); - assert_eq!(none.iter().join(", "), ""); -} - -#[test] -fn sorted_unstable_by() { - let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| { - a.cmp(&b) - }); - it::assert_equal(sc, vec![1, 2, 3, 4]); - - let v = (0..5).sorted_unstable_by(|&a, &b| a.cmp(&b).reverse()); - it::assert_equal(v, vec![4, 3, 2, 1, 0]); -} - -#[test] -fn sorted_unstable_by_key() { - let sc = [3, 4, 1, 2].iter().cloned().sorted_unstable_by_key(|&x| x); - it::assert_equal(sc, vec![1, 2, 3, 4]); - - let v = (0..5).sorted_unstable_by_key(|&x| -x); - it::assert_equal(v, vec![4, 3, 2, 1, 0]); -} - -#[test] -fn sorted_by() { - let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| { - a.cmp(&b) - }); - it::assert_equal(sc, vec![1, 2, 3, 4]); - - let v = (0..5).sorted_by(|&a, &b| a.cmp(&b).reverse()); - it::assert_equal(v, vec![4, 3, 2, 1, 0]); -} - -qc::quickcheck! { - fn k_smallest_range(n: u64, m: u16, k: u16) -> () { - // u16 is used to constrain k and m to 0..2¹⁶, - // otherwise the test could use too much memory. - let (k, m) = (k as u64, m as u64); - - // Generate a random permutation of n..n+m - let i = { - let mut v: Vec<u64> = (n..n.saturating_add(m)).collect(); - v.shuffle(&mut thread_rng()); - v.into_iter() - }; - - // Check that taking the k smallest elements yields n..n+min(k, m) - it::assert_equal( - i.k_smallest(k as usize), - n..n.saturating_add(min(k, m)) - ); - } -} - -#[derive(Clone, Debug)] -struct RandIter<T: 'static + Clone + Send, R: 'static + Clone + Rng + SeedableRng + Send = StdRng> { - idx: usize, - len: usize, - rng: R, - _t: PhantomData<T> -} - -impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> Iterator for RandIter<T, R> -where Standard: Distribution<T> { - type Item = T; - fn next(&mut self) -> Option<T> { - if self.idx == self.len { - None - } else { - self.idx += 1; - Some(self.rng.gen()) - } - } -} - -impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> qc::Arbitrary for RandIter<T, R> { - fn arbitrary<G: qc::Gen>(g: &mut G) -> Self { - RandIter { - idx: 0, - len: g.size(), - rng: R::seed_from_u64(g.next_u64()), - _t : PhantomData{}, - } - } -} - -// Check that taking the k smallest is the same as -// sorting then taking the k first elements -fn k_smallest_sort<I>(i: I, k: u16) -where - I: Iterator + Clone, - I::Item: Ord + Debug, -{ - let j = i.clone(); - let k = k as usize; - it::assert_equal( - i.k_smallest(k), - j.sorted().take(k) - ) -} - -macro_rules! generic_test { - ($f:ident, $($t:ty),+) => { - $(paste::item! { - qc::quickcheck! { - fn [< $f _ $t >](i: RandIter<$t>, k: u16) -> () { - $f(i, k) - } - } - })+ - }; -} - -generic_test!(k_smallest_sort, u8, u16, u32, u64, i8, i16, i32, i64); - -#[test] -fn sorted_by_key() { - let sc = [3, 4, 1, 2].iter().cloned().sorted_by_key(|&x| x); - it::assert_equal(sc, vec![1, 2, 3, 4]); - - let v = (0..5).sorted_by_key(|&x| -x); - it::assert_equal(v, vec![4, 3, 2, 1, 0]); -} - -#[test] -fn sorted_by_cached_key() { - // Track calls to key function - let mut ncalls = 0; - - let sorted = [3, 4, 1, 2].iter().cloned().sorted_by_cached_key(|&x| { - ncalls += 1; - x.to_string() - }); - it::assert_equal(sorted, vec![1, 2, 3, 4]); - // Check key function called once per element - assert_eq!(ncalls, 4); - - let mut ncalls = 0; - - let sorted = (0..5).sorted_by_cached_key(|&x| { - ncalls += 1; - -x - }); - it::assert_equal(sorted, vec![4, 3, 2, 1, 0]); - // Check key function called once per element - assert_eq!(ncalls, 5); -} - -#[test] -fn test_multipeek() { - let nums = vec![1u8,2,3,4,5]; - - let mp = multipeek(nums.iter().copied()); - assert_eq!(nums, mp.collect::<Vec<_>>()); - - let mut mp = multipeek(nums.iter().copied()); - assert_eq!(mp.peek(), Some(&1)); - assert_eq!(mp.next(), Some(1)); - assert_eq!(mp.peek(), Some(&2)); - assert_eq!(mp.peek(), Some(&3)); - assert_eq!(mp.next(), Some(2)); - assert_eq!(mp.peek(), Some(&3)); - assert_eq!(mp.peek(), Some(&4)); - assert_eq!(mp.peek(), Some(&5)); - assert_eq!(mp.peek(), None); - assert_eq!(mp.next(), Some(3)); - assert_eq!(mp.next(), Some(4)); - assert_eq!(mp.peek(), Some(&5)); - assert_eq!(mp.peek(), None); - assert_eq!(mp.next(), Some(5)); - assert_eq!(mp.next(), None); - assert_eq!(mp.peek(), None); -} - -#[test] -fn test_multipeek_reset() { - let data = [1, 2, 3, 4]; - - let mut mp = multipeek(cloned(&data)); - assert_eq!(mp.peek(), Some(&1)); - assert_eq!(mp.next(), Some(1)); - assert_eq!(mp.peek(), Some(&2)); - assert_eq!(mp.peek(), Some(&3)); - mp.reset_peek(); - assert_eq!(mp.peek(), Some(&2)); - assert_eq!(mp.next(), Some(2)); -} - -#[test] -fn test_multipeek_peeking_next() { - use crate::it::PeekingNext; - let nums = vec![1u8,2,3,4,5,6,7]; - - let mut mp = multipeek(nums.iter().copied()); - assert_eq!(mp.peeking_next(|&x| x != 0), Some(1)); - assert_eq!(mp.next(), Some(2)); - assert_eq!(mp.peek(), Some(&3)); - assert_eq!(mp.peek(), Some(&4)); - assert_eq!(mp.peeking_next(|&x| x == 3), Some(3)); - assert_eq!(mp.peek(), Some(&4)); - assert_eq!(mp.peeking_next(|&x| x != 4), None); - assert_eq!(mp.peeking_next(|&x| x == 4), Some(4)); - assert_eq!(mp.peek(), Some(&5)); - assert_eq!(mp.peek(), Some(&6)); - assert_eq!(mp.peeking_next(|&x| x != 5), None); - assert_eq!(mp.peek(), Some(&7)); - assert_eq!(mp.peeking_next(|&x| x == 5), Some(5)); - assert_eq!(mp.peeking_next(|&x| x == 6), Some(6)); - assert_eq!(mp.peek(), Some(&7)); - assert_eq!(mp.peek(), None); - assert_eq!(mp.next(), Some(7)); - assert_eq!(mp.peek(), None); -} - -#[test] -fn test_peek_nth() { - let nums = vec![1u8,2,3,4,5]; - - let iter = peek_nth(nums.iter().copied()); - assert_eq!(nums, iter.collect::<Vec<_>>()); - - let mut iter = peek_nth(nums.iter().copied()); - - assert_eq!(iter.peek_nth(0), Some(&1)); - assert_eq!(iter.peek_nth(0), Some(&1)); - assert_eq!(iter.next(), Some(1)); - - assert_eq!(iter.peek_nth(0), Some(&2)); - assert_eq!(iter.peek_nth(1), Some(&3)); - assert_eq!(iter.next(), Some(2)); - - assert_eq!(iter.peek_nth(0), Some(&3)); - assert_eq!(iter.peek_nth(1), Some(&4)); - assert_eq!(iter.peek_nth(2), Some(&5)); - assert_eq!(iter.peek_nth(3), None); - - assert_eq!(iter.next(), Some(3)); - assert_eq!(iter.next(), Some(4)); - - assert_eq!(iter.peek_nth(0), Some(&5)); - assert_eq!(iter.peek_nth(1), None); - assert_eq!(iter.next(), Some(5)); - assert_eq!(iter.next(), None); - - assert_eq!(iter.peek_nth(0), None); - assert_eq!(iter.peek_nth(1), None); -} - -#[test] -fn test_peek_nth_peeking_next() { - use it::PeekingNext; - let nums = vec![1u8,2,3,4,5,6,7]; - let mut iter = peek_nth(nums.iter().copied()); - - assert_eq!(iter.peeking_next(|&x| x != 0), Some(1)); - assert_eq!(iter.next(), Some(2)); - - assert_eq!(iter.peek_nth(0), Some(&3)); - assert_eq!(iter.peek_nth(1), Some(&4)); - assert_eq!(iter.peeking_next(|&x| x == 3), Some(3)); - assert_eq!(iter.peek(), Some(&4)); - - assert_eq!(iter.peeking_next(|&x| x != 4), None); - assert_eq!(iter.peeking_next(|&x| x == 4), Some(4)); - assert_eq!(iter.peek_nth(0), Some(&5)); - assert_eq!(iter.peek_nth(1), Some(&6)); - - assert_eq!(iter.peeking_next(|&x| x != 5), None); - assert_eq!(iter.peek(), Some(&5)); - - assert_eq!(iter.peeking_next(|&x| x == 5), Some(5)); - assert_eq!(iter.peeking_next(|&x| x == 6), Some(6)); - assert_eq!(iter.peek_nth(0), Some(&7)); - assert_eq!(iter.peek_nth(1), None); - assert_eq!(iter.next(), Some(7)); - assert_eq!(iter.peek(), None); -} - -#[test] -fn pad_using() { - it::assert_equal((0..0).pad_using(1, |_| 1), 1..2); - - let v: Vec<usize> = vec![0, 1, 2]; - let r = v.into_iter().pad_using(5, |n| n); - it::assert_equal(r, vec![0, 1, 2, 3, 4]); - - let v: Vec<usize> = vec![0, 1, 2]; - let r = v.into_iter().pad_using(1, |_| panic!()); - it::assert_equal(r, vec![0, 1, 2]); -} - -#[test] -fn group_by() { - for (ch1, sub) in &"AABBCCC".chars().group_by(|&x| x) { - for ch2 in sub { - assert_eq!(ch1, ch2); - } - } - - for (ch1, sub) in &"AAABBBCCCCDDDD".chars().group_by(|&x| x) { - for ch2 in sub { - assert_eq!(ch1, ch2); - if ch1 == 'C' { - break; - } - } - } - - let toupper = |ch: &char| ch.to_uppercase().next().unwrap(); - - // try all possible orderings - for indices in permutohedron::Heap::new(&mut [0, 1, 2, 3]) { - let groups = "AaaBbbccCcDDDD".chars().group_by(&toupper); - let mut subs = groups.into_iter().collect_vec(); - - for &idx in &indices[..] { - let (key, text) = match idx { - 0 => ('A', "Aaa".chars()), - 1 => ('B', "Bbb".chars()), - 2 => ('C', "ccCc".chars()), - 3 => ('D', "DDDD".chars()), - _ => unreachable!(), - }; - assert_eq!(key, subs[idx].0); - it::assert_equal(&mut subs[idx].1, text); - } - } - - let groups = "AAABBBCCCCDDDD".chars().group_by(|&x| x); - let mut subs = groups.into_iter().map(|(_, g)| g).collect_vec(); - - let sd = subs.pop().unwrap(); - let sc = subs.pop().unwrap(); - let sb = subs.pop().unwrap(); - let sa = subs.pop().unwrap(); - for (a, b, c, d) in multizip((sa, sb, sc, sd)) { - assert_eq!(a, 'A'); - assert_eq!(b, 'B'); - assert_eq!(c, 'C'); - assert_eq!(d, 'D'); - } - - // check that the key closure is called exactly n times - { - let mut ntimes = 0; - let text = "AABCCC"; - for (_, sub) in &text.chars().group_by(|&x| { ntimes += 1; x}) { - for _ in sub { - } - } - assert_eq!(ntimes, text.len()); - } - - { - let mut ntimes = 0; - let text = "AABCCC"; - for _ in &text.chars().group_by(|&x| { ntimes += 1; x}) { - } - assert_eq!(ntimes, text.len()); - } - - { - let text = "ABCCCDEEFGHIJJKK"; - let gr = text.chars().group_by(|&x| x); - it::assert_equal(gr.into_iter().flat_map(|(_, sub)| sub), text.chars()); - } -} - -#[test] -fn group_by_lazy_2() { - let data = vec![0, 1]; - let groups = data.iter().group_by(|k| *k); - let gs = groups.into_iter().collect_vec(); - it::assert_equal(data.iter(), gs.into_iter().flat_map(|(_k, g)| g)); - - let data = vec![0, 1, 1, 0, 0]; - let groups = data.iter().group_by(|k| *k); - let mut gs = groups.into_iter().collect_vec(); - gs[1..].reverse(); - it::assert_equal(&[0, 0, 0, 1, 1], gs.into_iter().flat_map(|(_, g)| g)); - - let grouper = data.iter().group_by(|k| *k); - let mut groups = Vec::new(); - for (k, group) in &grouper { - if *k == 1 { - groups.push(group); - } - } - it::assert_equal(&mut groups[0], &[1, 1]); - - let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; - let grouper = data.iter().group_by(|k| *k); - let mut groups = Vec::new(); - for (i, (_, group)) in grouper.into_iter().enumerate() { - if i < 2 { - groups.push(group); - } else if i < 4 { - for _ in group { - } - } else { - groups.push(group); - } - } - it::assert_equal(&mut groups[0], &[0, 0, 0]); - it::assert_equal(&mut groups[1], &[1, 1]); - it::assert_equal(&mut groups[2], &[3, 3]); - - // use groups as chunks - let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; - let mut i = 0; - let grouper = data.iter().group_by(move |_| { let k = i / 3; i += 1; k }); - for (i, group) in &grouper { - match i { - 0 => it::assert_equal(group, &[0, 0, 0]), - 1 => it::assert_equal(group, &[1, 1, 0]), - 2 => it::assert_equal(group, &[0, 2, 2]), - 3 => it::assert_equal(group, &[3, 3]), - _ => unreachable!(), - } - } -} - -#[test] -fn group_by_lazy_3() { - // test consuming each group on the lap after it was produced - let data = vec![0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2]; - let grouper = data.iter().group_by(|elt| *elt); - let mut last = None; - for (key, group) in &grouper { - if let Some(gr) = last.take() { - for elt in gr { - assert!(elt != key && i32::abs(elt - key) == 1); - } - } - last = Some(group); - } -} - -#[test] -fn chunks() { - let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; - let grouper = data.iter().chunks(3); - for (i, chunk) in grouper.into_iter().enumerate() { - match i { - 0 => it::assert_equal(chunk, &[0, 0, 0]), - 1 => it::assert_equal(chunk, &[1, 1, 0]), - 2 => it::assert_equal(chunk, &[0, 2, 2]), - 3 => it::assert_equal(chunk, &[3, 3]), - _ => unreachable!(), - } - } -} - -#[test] -fn concat_empty() { - let data: Vec<Vec<()>> = Vec::new(); - assert_eq!(data.into_iter().concat(), Vec::new()) -} - -#[test] -fn concat_non_empty() { - let data = vec![vec![1,2,3], vec![4,5,6], vec![7,8,9]]; - assert_eq!(data.into_iter().concat(), vec![1,2,3,4,5,6,7,8,9]) -} - -#[test] -fn combinations() { - assert!((1..3).combinations(5).next().is_none()); - - let it = (1..3).combinations(2); - it::assert_equal(it, vec![ - vec![1, 2], - ]); - - let it = (1..5).combinations(2); - it::assert_equal(it, vec![ - vec![1, 2], - vec![1, 3], - vec![1, 4], - vec![2, 3], - vec![2, 4], - vec![3, 4], - ]); - - it::assert_equal((0..0).tuple_combinations::<(_, _)>(), <Vec<_>>::new()); - it::assert_equal((0..1).tuple_combinations::<(_, _)>(), <Vec<_>>::new()); - it::assert_equal((0..2).tuple_combinations::<(_, _)>(), vec![(0, 1)]); - - it::assert_equal((0..0).combinations(2), <Vec<Vec<_>>>::new()); - it::assert_equal((0..1).combinations(1), vec![vec![0]]); - it::assert_equal((0..2).combinations(1), vec![vec![0], vec![1]]); - it::assert_equal((0..2).combinations(2), vec![vec![0, 1]]); -} - -#[test] -fn combinations_of_too_short() { - for i in 1..10 { - assert!((0..0).combinations(i).next().is_none()); - assert!((0..i - 1).combinations(i).next().is_none()); - } -} - - -#[test] -fn combinations_zero() { - it::assert_equal((1..3).combinations(0), vec![vec![]]); - it::assert_equal((0..0).combinations(0), vec![vec![]]); -} - -#[test] -fn permutations_zero() { - it::assert_equal((1..3).permutations(0), vec![vec![]]); - it::assert_equal((0..0).permutations(0), vec![vec![]]); -} - -#[test] -fn combinations_with_replacement() { - // Pool smaller than n - it::assert_equal((0..1).combinations_with_replacement(2), vec![vec![0, 0]]); - // Pool larger than n - it::assert_equal( - (0..3).combinations_with_replacement(2), - vec![ - vec![0, 0], - vec![0, 1], - vec![0, 2], - vec![1, 1], - vec![1, 2], - vec![2, 2], - ], - ); - // Zero size - it::assert_equal( - (0..3).combinations_with_replacement(0), - vec![vec![]], - ); - // Zero size on empty pool - it::assert_equal( - (0..0).combinations_with_replacement(0), - vec![vec![]], - ); - // Empty pool - it::assert_equal( - (0..0).combinations_with_replacement(2), - <Vec<Vec<_>>>::new(), - ); -} - -#[test] -fn powerset() { - it::assert_equal((0..0).powerset(), vec![vec![]]); - it::assert_equal((0..1).powerset(), vec![vec![], vec![0]]); - it::assert_equal((0..2).powerset(), vec![vec![], vec![0], vec![1], vec![0, 1]]); - it::assert_equal((0..3).powerset(), vec![ - vec![], - vec![0], vec![1], vec![2], - vec![0, 1], vec![0, 2], vec![1, 2], - vec![0, 1, 2] - ]); - - assert_eq!((0..4).powerset().count(), 1 << 4); - assert_eq!((0..8).powerset().count(), 1 << 8); - assert_eq!((0..16).powerset().count(), 1 << 16); -} - -#[test] -fn diff_mismatch() { - let a = vec![1, 2, 3, 4]; - let b = vec![1.0, 5.0, 3.0, 4.0]; - let b_map = b.into_iter().map(|f| f as i32); - let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); - - assert!(match diff { - Some(it::Diff::FirstMismatch(1, _, from_diff)) => - from_diff.collect::<Vec<_>>() == vec![5, 3, 4], - _ => false, - }); -} - -#[test] -fn diff_longer() { - let a = vec![1, 2, 3, 4]; - let b = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; - let b_map = b.into_iter().map(|f| f as i32); - let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); - - assert!(match diff { - Some(it::Diff::Longer(_, remaining)) => - remaining.collect::<Vec<_>>() == vec![5, 6], - _ => false, - }); -} - -#[test] -fn diff_shorter() { - let a = vec![1, 2, 3, 4]; - let b = vec![1.0, 2.0]; - let b_map = b.into_iter().map(|f| f as i32); - let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); - - assert!(match diff { - Some(it::Diff::Shorter(len, _)) => len == 2, - _ => false, - }); -} - -#[test] -fn extrema_set() { - use std::cmp::Ordering; - - // A peculiar type: Equality compares both tuple items, but ordering only the - // first item. Used to distinguish equal elements. - #[derive(Clone, Debug, PartialEq, Eq)] - struct Val(u32, u32); - - impl PartialOrd<Val> for Val { - fn partial_cmp(&self, other: &Val) -> Option<Ordering> { - self.0.partial_cmp(&other.0) - } - } - - impl Ord for Val { - fn cmp(&self, other: &Val) -> Ordering { - self.0.cmp(&other.0) - } - } - - assert_eq!(None::<u32>.iter().min_set(), Vec::<&u32>::new()); - assert_eq!(None::<u32>.iter().max_set(), Vec::<&u32>::new()); - - assert_eq!(Some(1u32).iter().min_set(), vec![&1]); - assert_eq!(Some(1u32).iter().max_set(), vec![&1]); - - let data = vec![Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)]; - - let min_set = data.iter().min_set(); - assert_eq!(min_set, vec![&Val(0, 1), &Val(0, 2)]); - - let min_set_by_key = data.iter().min_set_by_key(|v| v.1); - assert_eq!(min_set_by_key, vec![&Val(2, 0), &Val(1, 0)]); - - let min_set_by = data.iter().min_set_by(|x, y| x.1.cmp(&y.1)); - assert_eq!(min_set_by, vec![&Val(2, 0), &Val(1, 0)]); - - let max_set = data.iter().max_set(); - assert_eq!(max_set, vec![&Val(2, 0), &Val(2, 1)]); - - let max_set_by_key = data.iter().max_set_by_key(|v| v.1); - assert_eq!(max_set_by_key, vec![&Val(0, 2)]); - - let max_set_by = data.iter().max_set_by(|x, y| x.1.cmp(&y.1)); - assert_eq!(max_set_by, vec![&Val(0, 2)]); -} - -#[test] -fn minmax() { - use std::cmp::Ordering; - use crate::it::MinMaxResult; - - // A peculiar type: Equality compares both tuple items, but ordering only the - // first item. This is so we can check the stability property easily. - #[derive(Clone, Debug, PartialEq, Eq)] - struct Val(u32, u32); - - impl PartialOrd<Val> for Val { - fn partial_cmp(&self, other: &Val) -> Option<Ordering> { - self.0.partial_cmp(&other.0) - } - } - - impl Ord for Val { - fn cmp(&self, other: &Val) -> Ordering { - self.0.cmp(&other.0) - } - } - - assert_eq!(None::<Option<u32>>.iter().minmax(), MinMaxResult::NoElements); - - assert_eq!(Some(1u32).iter().minmax(), MinMaxResult::OneElement(&1)); - - let data = vec![Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)]; - - let minmax = data.iter().minmax(); - assert_eq!(minmax, MinMaxResult::MinMax(&Val(0, 1), &Val(2, 1))); - - let (min, max) = data.iter().minmax_by_key(|v| v.1).into_option().unwrap(); - assert_eq!(min, &Val(2, 0)); - assert_eq!(max, &Val(0, 2)); - - let (min, max) = data.iter().minmax_by(|x, y| x.1.cmp(&y.1)).into_option().unwrap(); - assert_eq!(min, &Val(2, 0)); - assert_eq!(max, &Val(0, 2)); -} - -#[test] -fn format() { - let data = [0, 1, 2, 3]; - let ans1 = "0, 1, 2, 3"; - let ans2 = "0--1--2--3"; - - let t1 = format!("{}", data.iter().format(", ")); - assert_eq!(t1, ans1); - let t2 = format!("{:?}", data.iter().format("--")); - assert_eq!(t2, ans2); - - let dataf = [1.1, 5.71828, -22.]; - let t3 = format!("{:.2e}", dataf.iter().format(", ")); - assert_eq!(t3, "1.10e0, 5.72e0, -2.20e1"); -} - -#[test] -fn while_some() { - let ns = (1..10).map(|x| if x % 5 != 0 { Some(x) } else { None }) - .while_some(); - it::assert_equal(ns, vec![1, 2, 3, 4]); -} - -#[allow(deprecated)] -#[test] -fn fold_while() { - let mut iterations = 0; - let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - let sum = vec.into_iter().fold_while(0, |acc, item| { - iterations += 1; - let new_sum = acc + item; - if new_sum <= 20 { - FoldWhile::Continue(new_sum) - } else { - FoldWhile::Done(acc) - } - }).into_inner(); - assert_eq!(iterations, 6); - assert_eq!(sum, 15); -} - -#[test] -fn tree_fold1() { - let x = [ - "", - "0", - "0 1 x", - "0 1 x 2 x", - "0 1 x 2 3 x x", - "0 1 x 2 3 x x 4 x", - "0 1 x 2 3 x x 4 5 x x", - "0 1 x 2 3 x x 4 5 x 6 x x", - "0 1 x 2 3 x x 4 5 x 6 7 x x x", - "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 x", - "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x x", - "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 x x", - "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x x", - "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 x x", - "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x x x", - "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 x x x", - "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 15 x x x x", - ]; - for (i, &s) in x.iter().enumerate() { - let expected = if s.is_empty() { None } else { Some(s.to_string()) }; - let num_strings = (0..i).map(|x| x.to_string()); - let actual = num_strings.tree_fold1(|a, b| format!("{} {} x", a, b)); - assert_eq!(actual, expected); - } -} - -#[test] -fn exactly_one_question_mark_syntax_works() { - exactly_one_question_mark_return().unwrap_err(); -} - -fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError<std::slice::Iter<'static, ()>>> { - [].iter().exactly_one()?; - Ok(()) -} - -#[test] -fn multiunzip() { - let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2), (3, 4, 5), (6, 7, 8)].iter().cloned().multiunzip(); - assert_eq!((a, b, c), (vec![0, 3, 6], vec![1, 4, 7], vec![2, 5, 8])); - let (): () = [(), (), ()].iter().cloned().multiunzip(); - let t: (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)].iter().cloned().multiunzip(); - assert_eq!(t, (vec![0], vec![1], vec![2], vec![3], vec![4], vec![5], vec![6], vec![7], vec![8], vec![9], vec![10], vec![11])); -}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.cargo-checksum.json b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.cargo-checksum.json similarity index 100% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.cargo-checksum.json rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.cargo-checksum.json
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.cargo_vcs_info.json new file mode 100644 index 0000000..a6fc921 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.cargo_vcs_info.json
@@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "a015a6831525ee1637df747d3f530a627d9741bf" + }, + "path_in_vcs": "" +} \ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.codecov.yml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.codecov.yml new file mode 100644 index 0000000..d06394a --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.codecov.yml
@@ -0,0 +1,7 @@ +coverage: + status: + project: + default: + target: auto + # Allow a tiny drop of overall project coverage in PR to reduce spurious failures. + threshold: 0.25%
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/dependabot.yml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/dependabot.yml similarity index 100% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/dependabot.yml rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/dependabot.yml
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/ci.yml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/ci.yml new file mode 100644 index 0000000..8e2a9d6 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/ci.yml
@@ -0,0 +1,101 @@ +name: CI + +on: + pull_request: + paths-ignore: + - "**.md" + merge_group: + paths-ignore: + - "**.md" + +jobs: + check: + runs-on: ubuntu-latest + strategy: + matrix: + features: + [ + "", + "--no-default-features", + "--no-default-features --features use_alloc", + "--all-targets --all-features", + ] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - run: RUSTFLAGS="--deny warnings" cargo clippy ${{ matrix.features }} + + doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: RUSTDOCFLAGS="-Dwarnings" cargo doc --all-features + + msrv: + runs-on: ubuntu-latest + env: + CARGO_NET_GIT_FETCH_WITH_CLI: true + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/install-action@cargo-no-dev-deps + - uses: dtolnay/rust-toolchain@master + with: + # Here, it does not trigger a PR from dependabot. + toolchain: 1.63.0 + - run: cargo no-dev-deps check + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo test --all-features + + miri: + runs-on: ubuntu-latest + env: + CARGO_TERM_COLOR: always + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + components: miri + - uses: taiki-e/install-action@nextest + - run: | + cargo miri nextest run --all-features + cargo miri test --doc + + check-format: + name: check format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: rustfmt + - run: cargo fmt --check + + semver-checks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: obi1kenobi/cargo-semver-checks-action@v2.4 + with: + rust-toolchain: manual + feature-group: all-features + + # Used to signal to branch protections that all other jobs have succeeded. + all-jobs-succeed: + name: All checks succeeded + if: success() + runs-on: ubuntu-latest + needs: [check, msrv, test, check-format, doc] + steps: + - name: Mark the job as successful + run: exit 0
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/coverage.yml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/coverage.yml new file mode 100644 index 0000000..5c084565 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/coverage.yml
@@ -0,0 +1,34 @@ +on: + push: + branches: [master] + paths-ignore: + - "**.md" + pull_request: + paths-ignore: + - "**.md" + +name: Code Coverage + +jobs: + coverage: + name: coverage + runs-on: ubuntu-latest + steps: + - name: checkout source + uses: actions/checkout@v4 + + - name: Install nightly toolchain + uses: dtolnay/rust-toolchain@nightly + with: + components: llvm-tools-preview + + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + + - name: Run llvm-cov + run: cargo llvm-cov --all-features --doctests --workspace --lcov --output-path lcov.info + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + files: lcov.info
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.gitignore b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.gitignore similarity index 100% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.gitignore rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.gitignore
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/CHANGELOG.md b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CHANGELOG.md similarity index 76% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/CHANGELOG.md rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CHANGELOG.md index 8d7404e..6b08f68 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/CHANGELOG.md +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CHANGELOG.md
@@ -1,5 +1,166 @@ # Changelog +## 0.14.0 + +### Breaking +- Increased MSRV to 1.63.0 (#960) +- Removed generic parameter from `cons_tuples` (#988) + +### Added +- Added `array_combinations` (#991) +- Added `k_smallest_relaxed` and variants (#925) +- Added `next_array` and `collect_array` (#560) +- Implemented `DoubleEndedIterator` for `FilterOk` (#948) +- Implemented `DoubleEndedIterator` for `FilterMapOk` (#950) + +### Changed +- Allow `Q: ?Sized` in `Itertools::contains` (#971) +- Improved hygiene of `chain!` (#943) +- Improved `into_group_map_by` documentation (#1000) +- Improved `tree_reduce` documentation (#955) +- Improved discoverability of `merge_join_by` (#966) +- Improved discoverability of `take_while_inclusive` (#972) +- Improved documentation of `find_or_last` and `find_or_first` (#984) +- Prevented exponentially large type sizes in `tuple_combinations` (#945) +- Added `track_caller` attr for `asser_equal` (#976) + +### Notable Internal Changes +- Fixed clippy lints (#956, #987, #1008) +- Addressed warnings within doctests (#964) +- CI: Run most tests with miri (#961) +- CI: Speed up "cargo-semver-checks" action (#938) +- Changed an instance of `default_features` in `Cargo.toml` to `default-features` (#985) + +## 0.13.0 + +### Breaking +- Removed implementation of `DoubleEndedIterator` for `ConsTuples` (#853) +- Made `MultiProduct` fused and fixed on an empty iterator (#835, #834) +- Changed `iproduct!` to return tuples for maxi one iterator too (#870) +- Changed `PutBack::put_back` to return the old value (#880) +- Removed deprecated `repeat_call, Itertools::{foreach, step, map_results, fold_results}` (#878) +- Removed `TakeWhileInclusive::new` (#912) + +### Added +- Added `Itertools::{smallest_by, smallest_by_key, largest, largest_by, largest_by_key}` (#654, #885) +- Added `Itertools::tail` (#899) +- Implemented `DoubleEndedIterator` for `ProcessResults` (#910) +- Implemented `Debug` for `FormatWith` (#931) +- Added `Itertools::get` (#891) + +### Changed +- Deprecated `Itertools::group_by` (renamed `chunk_by`) (#866, #879) +- Deprecated `unfold` (use `std::iter::from_fn` instead) (#871) +- Optimized `GroupingMapBy` (#873, #876) +- Relaxed `Fn` bounds to `FnMut` in `diff_with, Itertools::into_group_map_by` (#886) +- Relaxed `Debug/Clone` bounds for `MapInto` (#889) +- Documented the `use_alloc` feature (#887) +- Optimized `Itertools::set_from` (#888) +- Removed badges in `README.md` (#890) +- Added "no-std" categories in `Cargo.toml` (#894) +- Fixed `Itertools::k_smallest` on short unfused iterators (#900) +- Deprecated `Itertools::tree_fold1` (renamed `tree_reduce`) (#895) +- Deprecated `GroupingMap::fold_first` (renamed `reduce`) (#902) +- Fixed `Itertools::k_smallest(0)` to consume the iterator, optimized `Itertools::k_smallest(1)` (#909) +- Specialized `Combinations::nth` (#914) +- Specialized `MergeBy::fold` (#920) +- Specialized `CombinationsWithReplacement::nth` (#923) +- Specialized `FlattenOk::{fold, rfold}` (#927) +- Specialized `Powerset::nth` (#924) +- Documentation fixes (#882, #936) +- Fixed `assert_equal` for iterators longer than `i32::MAX` (#932) +- Updated the `must_use` message of non-lazy `KMergeBy` and `TupleCombinations` (#939) + +### Notable Internal Changes +- Tested iterator laziness (#792) +- Created `CONTRIBUTING.md` (#767) + +## 0.12.1 + +### Added +- Documented iteration order guarantee for `Itertools::[tuple_]combinations` (#822) +- Documented possible panic in `iterate` (#842) +- Implemented `Clone` and `Debug` for `Diff` (#845) +- Implemented `Debug` for `WithPosition` (#859) +- Implemented `Eq` for `MinMaxResult` (#838) +- Implemented `From<EitherOrBoth<A, B>>` for `Option<Either<A, B>>` (#843) +- Implemented `PeekingNext` for `RepeatN` (#855) + +### Changed +- Made `CoalesceBy` lazy (#801) +- Optimized `Filter[Map]Ok::next`, `Itertools::partition`, `Unique[By]::next[_back]` (#818) +- Optimized `Itertools::find_position` (#837) +- Optimized `Positions::next[_back]` (#816) +- Optimized `ZipLongest::fold` (#854) +- Relaxed `Debug` bounds for `GroupingMapBy` (#860) +- Specialized `ExactlyOneError::fold` (#826) +- Specialized `Interleave[Shortest]::fold` (#849) +- Specialized `MultiPeek::fold` (#820) +- Specialized `PadUsing::[r]fold` (#825) +- Specialized `PeekNth::fold` (#824) +- Specialized `Positions::[r]fold` (#813) +- Specialized `PutBackN::fold` (#823) +- Specialized `RepeatN::[r]fold` (#821) +- Specialized `TakeWhileInclusive::fold` (#851) +- Specialized `ZipLongest::rfold` (#848) + +### Notable Internal Changes +- Added test coverage in CI (#847, #856) +- Added semver check in CI (#784) +- Enforced `clippy` in CI (#740) +- Enforced `rustdoc` in CI (#840) +- Improved specialization tests (#807) +- More specialization benchmarks (#806) + +## 0.12.0 + +### Breaking +- Made `take_while_inclusive` consume iterator by value (#709) +- Added `Clone` bound to `Unique` (#777) + +### Added +- Added `Itertools::try_len` (#723) +- Added free function `sort_unstable` (#796) +- Added `GroupMap::fold_with` (#778, #785) +- Added `PeekNth::{peek_mut, peek_nth_mut}` (#716) +- Added `PeekNth::{next_if, next_if_eq}` (#734) +- Added conversion into `(Option<A>,Option<B>)` to `EitherOrBoth` (#713) +- Added conversion from `Either<A, B>` to `EitherOrBoth<A, B>` (#715) +- Implemented `ExactSizeIterator` for `Tuples` (#761) +- Implemented `ExactSizeIterator` for `(Circular)TupleWindows` (#752) +- Made `EitherOrBoth<T>` a shorthand for `EitherOrBoth<T, T>` (#719) + +### Changed +- Added missing `#[must_use]` annotations on iterator adaptors (#794) +- Made `Combinations` lazy (#795) +- Made `Intersperse(With)` lazy (#797) +- Made `Permutations` lazy (#793) +- Made `Product` lazy (#800) +- Made `TupleWindows` lazy (#602) +- Specialized `Combinations::{count, size_hint}` (#729) +- Specialized `CombinationsWithReplacement::{count, size_hint}` (#737) +- Specialized `Powerset::fold` (#765) +- Specialized `Powerset::count` (#735) +- Specialized `TupleCombinations::{count, size_hint}` (#763) +- Specialized `TupleCombinations::fold` (#775) +- Specialized `WhileSome::fold` (#780) +- Specialized `WithPosition::fold` (#772) +- Specialized `ZipLongest::fold` (#774) +- Changed `{min, max}_set*` operations require `alloc` feature, instead of `std` (#760) +- Improved documentation of `tree_fold1` (#787) +- Improved documentation of `permutations` (#724) +- Fixed typo in documentation of `multiunzip` (#770) + +### Notable Internal Changes +- Improved specialization tests (#799, #786, #782) +- Simplified implementation of `Permutations` (#739, #748, #790) +- Combined `Merge`/`MergeBy`/`MergeJoinBy` implementations (#736) +- Simplified `Permutations::size_hint` (#739) +- Fix wrapping arithmetic in benchmarks (#770) +- Enforced `rustfmt` in CI (#751) +- Disallowed compile warnings in CI (#720) +- Used `cargo hack` to check MSRV (#754) + ## 0.11.0 ### Breaking
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CONTRIBUTING.md b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CONTRIBUTING.md new file mode 100644 index 0000000..1dbf6f5 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CONTRIBUTING.md
@@ -0,0 +1,189 @@ +# Contributing to itertools + +We use stable Rust only. +Please check the minimum version of Rust we use in `Cargo.toml`. + +_If you are proposing a major change to CI or a new iterator adaptor for this crate, +then **please first file an issue** describing your proposal._ +[Usual concerns about new methods](https://github.com/rust-itertools/itertools/issues/413#issuecomment-657670781). + +To pass CI tests successfully, your code must be free of "compiler warnings" and "clippy warnings" and be "rustfmt" formatted. + +Note that small PRs are easier to review and therefore are more easily merged. + +## Write a new method/adaptor for `Itertools` trait +In general, the code logic should be tested with [quickcheck](https://crates.io/crates/quickcheck) tests in `tests/quick.rs` +which allow us to test properties about the code with randomly generated inputs. + +### Behind `use_std`/`use_alloc` feature? +If it needs the "std" (such as using hashes) then it should be behind the `use_std` feature, +or if it requires heap allocation (such as using vectors) then it should be behind the `use_alloc` feature. +Otherwise it should be able to run in `no_std` context. + +This mostly applies to your new module, each import from it, and to your new `Itertools` method. + +### Pick the right receiver +`self`, `&mut self` or `&self`? From [#710](https://github.com/rust-itertools/itertools/pull/710): + +- Take by value when: + - It transfers ownership to another iterator type, such as `filter`, `map`... + - It consumes the iterator completely, such as `count`, `last`, `max`... +- Mutably borrow when it consumes only part of the iterator, such as `find`, `all`, `try_collect`... +- Immutably borrow when there is no change, such as `size_hint`. + +### Laziness +Iterators are [lazy](https://doc.rust-lang.org/std/iter/index.html#laziness): + +- structs of iterator adaptors should have `#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]` ; +- structs of iterators should have `#[must_use = "iterators are lazy and do nothing unless consumed"]`. + +Those behaviors are **tested** in `tests/laziness.rs`. + +## Specialize `Iterator` methods +It might be more performant to specialize some methods. +However, each specialization should be thoroughly tested. + +Correctly specializing methods can be difficult, and _we do not require that you do it on your initial PR_. + +Most of the time, we want specializations of: + +- [`size_hint`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.size_hint): + It mostly allows allocation optimizations. + When always exact, it also enables to implement `ExactSizeIterator`. + See our private module `src/size_hint.rs` for helpers. +- [`fold`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold) + might make iteration faster than calling `next` repeatedly. +- [`count`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.count), + [`last`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.last), + [`nth`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.nth) + as we might be able to avoid iterating on every item with `next`. + +Additionally, + +- `for_each`, `reduce`, `max/min[_by[_key]]` and `partition` all rely on `fold` so you should specialize it instead. +- `all`, `any`, `find`, `find_map`, `cmp`, `partial_cmp`, `eq`, `ne`, `lt`, `le`, `gt`, `ge` and `position` all rely (by default) on `try_fold` + which we can not specialize on stable rust, so you might want to wait it stabilizes + or specialize each of them. +- `DoubleEndedIterator::{nth_back, rfold, rfind}`: similar reasoning. + +An adaptor might use the inner iterator specializations for its own specializations. + +They are **tested** in `tests/specializations.rs` and **benchmarked** in `benches/specializations.rs` +(build those benchmarks is slow so you might want to temporarily remove the ones you do not want to measure). + +## Additional implementations +### The [`Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) implementation +All our iterators should implement `Debug`. + +When one of the field is not debuggable (such as _functions_), you must not derive `Debug`. +Instead, manually implement it and _ignore this field_ in our helper macro `debug_fmt_fields`. + +<details> +<summary>4 examples (click to expand)</summary> + +```rust +use std::fmt; + +/* ===== Simple derive. ===== */ +#[derive(Debug)] +struct Name1<I> { + iter: I, +} + +/* ===== With an unclonable field. ===== */ +struct Name2<I, F> { + iter: I, + func: F, +} + +// No `F: Debug` bound and the field `func` is ignored. +impl<I: fmt::Debug, F> fmt::Debug for Name2<I, F> { + // it defines the `fmt` function from a struct name and the fields you want to debug. + debug_fmt_fields!(Name2, iter); +} + +/* ===== With an unclonable field, but another bound to add. ===== */ +struct Name3<I: Iterator, F> { + iter: I, + item: Option<I::Item>, + func: F, +} + +// Same about `F` and `func`, similar about `I` but we must add the `I::Item: Debug` bound. +impl<I: Iterator + fmt::Debug, F> fmt::Debug for Name3<I, F> +where + I::Item: fmt::Debug, +{ + debug_fmt_fields!(Name3, iter, item); +} + +/* ===== With an unclonable field for which we can provide some information. ===== */ +struct Name4<I, F> { + iter: I, + func: Option<F>, +} + +// If ignore a field is not good enough, implement Debug fully manually. +impl<I: fmt::Debug, F> fmt::Debug for Name4<I, F> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let func = if self.func.is_some() { "Some(_)" } else { "None" }; + f.debug_struct("Name4") + .field("iter", &self.iter) + .field("func", &func) + .finish() + } +} +``` +</details> + +### When/How to implement [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html) +All our iterators should implement `Clone` when possible. + +Note that a mutable reference is never clonable so `struct Name<'a, I: 'a> { iter: &'a mut I }` can not implement `Clone`. + +Derive `Clone` on a generic struct adds the bound `Clone` on each generic parameter. +It might be an issue in which case you should manually implement it with our helper macro `clone_fields` (it defines the `clone` function calling `clone` on each field) and be careful about the bounds. + +### When to implement [`std::iter::FusedIterator`](https://doc.rust-lang.org/std/iter/trait.FusedIterator.html) +This trait should be implemented _by all iterators that always return `None` after returning `None` once_, because it allows to optimize `Iterator::fuse()`. + +The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, eventually refined to ensure it behaves in a fused way. + +### When to implement [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html) +_When we are always able to return an exact non-overflowing length._ + +Therefore, we do not implement it on adaptors that makes the iterator longer as the resulting length could overflow. + +One should not override `ExactSizeIterator::len` method but rely on an exact `Iterator::size_hint` implementation, meaning it returns `(length, Some(length))` (unless you could make `len` more performant than the default). + +The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure the size hint is exact. + +### When to implement [`DoubleEndedIterator`](https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html) +When the iterator structure allows to handle _iterating on both fronts simultaneously_. +The iteration might stop in the middle when both fronts meet. + +The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure we can iterate on both fronts simultaneously. + +### When to implement [`itertools::PeekingNext`](https://docs.rs/itertools/latest/itertools/trait.PeekingNext.html) +TODO + +This is currently **tested** in `tests/test_std.rs`. + +## About lending iterators +TODO + + +## Other notes +No guideline about using `#[inline]` yet. + +### `.fold` / `.for_each` / `.try_fold` / `.try_for_each` +In the Rust standard library, it's quite common for `fold` to be implemented in terms of `try_fold`. But it's not something we do yet because we can not specialize `try_fold` methods yet (it uses the unstable `Try`). + +From [#781](https://github.com/rust-itertools/itertools/pull/781), the general rule to follow is something like this: + +- If you need to completely consume an iterator: + - Use `fold` if you need an _owned_ access to an accumulator. + - Use `for_each` otherwise. +- If you need to partly consume an iterator, the same applies with `try_` versions: + - Use `try_fold` if you need an _owned_ access to an accumulator. + - Use `try_for_each` otherwise.
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.lock similarity index 62% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.lock rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.lock index 76936c9e..fbf369b 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.lock +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.lock
@@ -3,6 +3,15 @@ version = 3 [[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -21,9 +30,9 @@ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" @@ -33,9 +42,9 @@ [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cast" @@ -51,9 +60,9 @@ [[package]] name = "ciborium" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -62,15 +71,15 @@ [[package]] name = "ciborium-io" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", @@ -78,9 +87,9 @@ [[package]] name = "clap" -version = "3.2.22" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "bitflags", "clap_lex", @@ -109,7 +118,7 @@ "ciborium", "clap", "criterion-plot", - "itertools 0.10.4", + "itertools 0.10.5", "lazy_static", "num-traits", "oorandom", @@ -130,59 +139,45 @@ checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools 0.10.4", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if", - "crossbeam-utils", + "itertools 0.10.5", ] [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.10" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset", - "once_cell", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" -dependencies = [ - "cfg-if", - "once_cell", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "either" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "getrandom" @@ -197,9 +192,13 @@ [[package]] name = "half" -version = "1.8.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] [[package]] name = "hashbrown" @@ -218,9 +217,9 @@ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -228,16 +227,16 @@ [[package]] name = "itertools" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itertools" -version = "0.11.0" +version = "0.14.0" dependencies = [ "criterion", "either", @@ -249,15 +248,15 @@ [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -270,52 +269,36 @@ [[package]] name = "libc" -version = "0.2.133" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "log" -version = "0.4.17" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] -name = "memoffset" -version = "0.6.5" +name = "memchr" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] name = "once_cell" -version = "1.14.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -325,15 +308,15 @@ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "paste" -version = "1.0.9" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "permutohedron" @@ -343,9 +326,9 @@ [[package]] name = "plotters" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", @@ -356,30 +339,30 @@ [[package]] name = "plotters-backend" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -396,9 +379,9 @@ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -446,48 +429,58 @@ [[package]] name = "rayon" -version = "1.5.3" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -499,25 +492,19 @@ ] [[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] name = "serde" -version = "1.0.144" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", @@ -526,9 +513,9 @@ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -537,9 +524,9 @@ [[package]] name = "syn" -version = "1.0.100" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -548,9 +535,9 @@ [[package]] name = "textwrap" -version = "0.15.1" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "tinytemplate" @@ -564,18 +551,17 @@ [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -587,9 +573,9 @@ [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -597,9 +583,9 @@ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -612,9 +598,9 @@ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -622,9 +608,9 @@ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -635,15 +621,15 @@ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -667,11 +653,11 @@ [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys", ] [[package]] @@ -679,3 +665,76 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml new file mode 100644 index 0000000..96f959771 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml
@@ -0,0 +1,180 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.63.0" +name = "itertools" +version = "0.14.0" +authors = ["bluss"] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Extra iterator adaptors, iterator methods, free functions, and macros." +documentation = "https://docs.rs/itertools/" +readme = "README.md" +keywords = [ + "iterator", + "data-structure", + "zip", + "product", +] +categories = [ + "algorithms", + "rust-patterns", + "no-std", + "no-std::no-alloc", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-itertools/itertools" + +[features] +default = ["use_std"] +use_alloc = [] +use_std = [ + "use_alloc", + "either/use_std", +] + +[lib] +name = "itertools" +path = "src/lib.rs" +test = false +bench = false + +[[example]] +name = "iris" +path = "examples/iris.rs" + +[[test]] +name = "adaptors_no_collect" +path = "tests/adaptors_no_collect.rs" + +[[test]] +name = "flatten_ok" +path = "tests/flatten_ok.rs" + +[[test]] +name = "laziness" +path = "tests/laziness.rs" + +[[test]] +name = "macros_hygiene" +path = "tests/macros_hygiene.rs" + +[[test]] +name = "merge_join" +path = "tests/merge_join.rs" + +[[test]] +name = "peeking_take_while" +path = "tests/peeking_take_while.rs" + +[[test]] +name = "quick" +path = "tests/quick.rs" + +[[test]] +name = "specializations" +path = "tests/specializations.rs" + +[[test]] +name = "test_core" +path = "tests/test_core.rs" + +[[test]] +name = "test_std" +path = "tests/test_std.rs" + +[[test]] +name = "tuples" +path = "tests/tuples.rs" + +[[test]] +name = "zip" +path = "tests/zip.rs" + +[[bench]] +name = "bench1" +path = "benches/bench1.rs" +harness = false + +[[bench]] +name = "combinations" +path = "benches/combinations.rs" +harness = false + +[[bench]] +name = "combinations_with_replacement" +path = "benches/combinations_with_replacement.rs" +harness = false + +[[bench]] +name = "fold_specialization" +path = "benches/fold_specialization.rs" +harness = false + +[[bench]] +name = "k_smallest" +path = "benches/k_smallest.rs" +harness = false + +[[bench]] +name = "powerset" +path = "benches/powerset.rs" +harness = false + +[[bench]] +name = "specializations" +path = "benches/specializations.rs" +harness = false + +[[bench]] +name = "tree_reduce" +path = "benches/tree_reduce.rs" +harness = false + +[[bench]] +name = "tuple_combinations" +path = "benches/tuple_combinations.rs" +harness = false + +[[bench]] +name = "tuples" +path = "benches/tuples.rs" +harness = false + +[dependencies.either] +version = "1.0" +default-features = false + +[dev-dependencies.criterion] +version = "0.4.0" +features = ["html_reports"] + +[dev-dependencies.paste] +version = "1.0.0" + +[dev-dependencies.permutohedron] +version = "0.2" + +[dev-dependencies.quickcheck] +version = "0.9" +default-features = false + +[dev-dependencies.rand] +version = "0.7" + +[profile.bench] +debug = 2
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml.orig similarity index 67% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml.orig rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml.orig index a6dfefb..1d8a68f 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml.orig +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml.orig
@@ -1,6 +1,6 @@ [package] name = "itertools" -version = "0.11.0" +version = "0.14.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-itertools/itertools" @@ -10,13 +10,13 @@ description = "Extra iterator adaptors, iterator methods, free functions, and macros." -keywords = ["iterator", "data-structure", "zip", "product", "group-by"] -categories = ["algorithms", "rust-patterns"] -exclude = ["/bors.toml"] +keywords = ["iterator", "data-structure", "zip", "product"] +categories = ["algorithms", "rust-patterns", "no-std", "no-std::no-alloc"] edition = "2018" -rust-version = "1.36.0" +# When bumping, please resolve all `#[allow(clippy::*)]` that are newly resolvable. +rust-version = "1.63.0" [lib] bench = false @@ -27,10 +27,10 @@ [dev-dependencies] rand = "0.7" -criterion = "0.4.0" +criterion = { version = "0.4.0", features = ["html_reports"] } paste = "1.0.0" # Used in test_std to instantiate generic tests permutohedron = "0.2" -quickcheck = { version = "0.9", default_features = false } +quickcheck = { version = "0.9", default-features = false } [features] default = ["use_std"] @@ -57,7 +57,7 @@ harness = false [[bench]] -name = "tree_fold1" +name = "tree_reduce" harness = false [[bench]] @@ -71,3 +71,11 @@ [[bench]] name = "powerset" harness = false + +[[bench]] +name = "specializations" +harness = false + +[[bench]] +name = "k_smallest" +harness = false
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/LICENSE-APACHE b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-APACHE similarity index 100% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/LICENSE-APACHE rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-APACHE
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/LICENSE-MIT b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-MIT similarity index 100% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/LICENSE-MIT rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-MIT
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/README.md b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/README.md new file mode 100644 index 0000000..46acc3f --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/README.md
@@ -0,0 +1,33 @@ +# Itertools + +Extra iterator adaptors, functions and macros. + +Please read the [API documentation here](https://docs.rs/itertools/). + +How to use with Cargo: + +```toml +[dependencies] +itertools = "0.14.0" +``` + +How to use in your crate: + +```rust +use itertools::Itertools; +``` + +## How to contribute +If you're not sure what to work on, try checking the [help wanted](https://github.com/rust-itertools/itertools/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label. + +See our [CONTRIBUTING.md](https://github.com/rust-itertools/itertools/blob/master/CONTRIBUTING.md) for a detailed guide. + +## License + +Dual-licensed to be compatible with the Rust project. + +Licensed under the Apache License, Version 2.0 +https://www.apache.org/licenses/LICENSE-2.0 or the MIT license +https://opensource.org/licenses/MIT, at your +option. This file may not be copied, modified, or distributed +except according to those terms.
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/bench1.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/bench1.rs similarity index 72% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/bench1.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/bench1.rs index 71278d1..a55fa7c9 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/bench1.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/bench1.rs
@@ -1,22 +1,20 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use itertools::Itertools; +use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; use itertools::free::cloned; use itertools::iproduct; +use itertools::Itertools; -use std::iter::repeat; use std::cmp; +use std::iter::repeat; use std::ops::{Add, Range}; -mod extra; - -use crate::extra::ZipSlices; - fn slice_iter(c: &mut Criterion) { let xs: Vec<_> = repeat(1i32).take(20).collect(); c.bench_function("slice iter", move |b| { - b.iter(|| for elt in xs.iter() { - black_box(elt); + b.iter(|| { + for elt in xs.iter() { + black_box(elt); + } }) }); } @@ -25,8 +23,10 @@ let xs: Vec<_> = repeat(1i32).take(20).collect(); c.bench_function("slice iter rev", move |b| { - b.iter(|| for elt in xs.iter().rev() { - black_box(elt); + b.iter(|| { + for elt in xs.iter().rev() { + black_box(elt); + } }) }); } @@ -116,72 +116,6 @@ }); } -fn zipslices(c: &mut Criterion) { - let xs = vec![0; 1024]; - let ys = vec![0; 768]; - let xs = black_box(xs); - let ys = black_box(ys); - - c.bench_function("zipslices", move |b| { - b.iter(|| { - for (&x, &y) in ZipSlices::new(&xs, &ys) { - black_box(x); - black_box(y); - } - }) - }); -} - -fn zipslices_mut(c: &mut Criterion) { - let xs = vec![0; 1024]; - let ys = vec![0; 768]; - let xs = black_box(xs); - let mut ys = black_box(ys); - - c.bench_function("zipslices mut", move |b| { - b.iter(|| { - for (&x, &mut y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) { - black_box(x); - black_box(y); - } - }) - }); -} - -fn zipdot_i32_zipslices(c: &mut Criterion) { - let xs = vec![2; 1024]; - let ys = vec![2; 768]; - let xs = black_box(xs); - let ys = black_box(ys); - - c.bench_function("zipdot i32 zipslices", move |b| { - b.iter(|| { - let mut s = 0i32; - for (&x, &y) in ZipSlices::new(&xs, &ys) { - s += x * y; - } - s - }) - }); -} - -fn zipdot_f32_zipslices(c: &mut Criterion) { - let xs = vec![2f32; 1024]; - let ys = vec![2f32; 768]; - let xs = black_box(xs); - let ys = black_box(ys); - - c.bench_function("zipdot f32 zipslices", move |b| { - b.iter(|| { - let mut s = 0.; - for (&x, &y) in ZipSlices::new(&xs, &ys) { - s += x * y; - } - s - }) - }); -} - fn zip_checked_counted_loop(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; @@ -307,10 +241,10 @@ let len = cmp::min(xs.len(), ys.len()); for i in 0..len { unsafe { - let x = *xs.get_unchecked(i); - let y = *ys.get_unchecked(i); - black_box(x); - black_box(y); + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + black_box(x); + black_box(y); } } }) @@ -329,9 +263,9 @@ let mut s = 0i32; for i in 0..len { unsafe { - let x = *xs.get_unchecked(i); - let y = *ys.get_unchecked(i); - s += x * y; + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + s += x * y; } } s @@ -351,9 +285,9 @@ let mut s = 0f32; for i in 0..len { unsafe { - let x = *xs.get_unchecked(i); - let y = *ys.get_unchecked(i); - s += x * y; + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + s += x * y; } } s @@ -374,19 +308,19 @@ let len = cmp::min(xs.len(), cmp::min(ys.len(), zs.len())); for i in 0..len { unsafe { - let x = *xs.get_unchecked(i); - let y = *ys.get_unchecked(i); - let z = *zs.get_unchecked(i); - black_box(x); - black_box(y); - black_box(z); + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + let z = *zs.get_unchecked(i); + black_box(x); + black_box(y); + black_box(z); } } }) }); } -fn group_by_lazy_1(c: &mut Criterion) { +fn chunk_by_lazy_1(c: &mut Criterion) { let mut data = vec![0; 1024]; for (index, elt) in data.iter_mut().enumerate() { *elt = index / 10; @@ -394,10 +328,10 @@ let data = black_box(data); - c.bench_function("group by lazy 1", move |b| { + c.bench_function("chunk by lazy 1", move |b| { b.iter(|| { - for (_key, group) in &data.iter().group_by(|elt| **elt) { - for elt in group { + for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) { + for elt in chunk { black_box(elt); } } @@ -405,7 +339,7 @@ }); } -fn group_by_lazy_2(c: &mut Criterion) { +fn chunk_by_lazy_2(c: &mut Criterion) { let mut data = vec![0; 1024]; for (index, elt) in data.iter_mut().enumerate() { *elt = index / 2; @@ -413,10 +347,10 @@ let data = black_box(data); - c.bench_function("group by lazy 2", move |b| { + c.bench_function("chunk by lazy 2", move |b| { b.iter(|| { - for (_key, group) in &data.iter().group_by(|elt| **elt) { - for elt in group { + for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) { + for elt in chunk { black_box(elt); } } @@ -432,8 +366,8 @@ c.bench_function("slice chunks", move |b| { b.iter(|| { - for group in data.chunks(sz) { - for elt in group { + for chunk in data.chunks(sz) { + for elt in chunk { black_box(elt); } } @@ -449,8 +383,8 @@ c.bench_function("chunks lazy 1", move |b| { b.iter(|| { - for group in &data.iter().chunks(sz) { - for elt in group { + for chunk in &data.iter().chunks(sz) { + for elt in chunk { black_box(elt); } } @@ -464,17 +398,15 @@ let alpha = black_box(&data[1..]); let beta = black_box(&data[..l - 1]); - c.bench_function("equal", move |b| { - b.iter(|| { - itertools::equal(alpha, beta) - }) - }); + c.bench_function("equal", move |b| b.iter(|| itertools::equal(alpha, beta))); } fn merge_default(c: &mut Criterion) { let mut data1 = vec![0; 1024]; let mut data2 = vec![0; 800]; let mut x = 0; + + #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)] for (_, elt) in data1.iter_mut().enumerate() { *elt = x; x += 1; @@ -492,17 +424,15 @@ let data1 = black_box(data1); let data2 = black_box(data2); - c.bench_function("merge default", move |b| { - b.iter(|| { - data1.iter().merge(&data2).count() - }) - }); + c.bench_function("merge default", move |b| b.iter(|| data1.iter().merge(&data2).count())); } fn merge_by_cmp(c: &mut Criterion) { let mut data1 = vec![0; 1024]; let mut data2 = vec![0; 800]; let mut x = 0; + + #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)] for (_, elt) in data1.iter_mut().enumerate() { *elt = x; x += 1; @@ -521,9 +451,7 @@ let data2 = black_box(data2); c.bench_function("merge by cmp", move |b| { - b.iter(|| { - data1.iter().merge_by(&data2, PartialOrd::le).count() - }) + b.iter(|| data1.iter().merge_by(&data2, PartialOrd::le).count()) }); } @@ -531,6 +459,8 @@ let mut data1 = vec![0; 1024]; let mut data2 = vec![0; 800]; let mut x = 0; + + #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)] for (_, elt) in data1.iter_mut().enumerate() { *elt = x; x += 1; @@ -549,9 +479,7 @@ let data2 = black_box(data2); c.bench_function("merge by lt", move |b| { - b.iter(|| { - data1.iter().merge_by(&data2, |a, b| a <= b).count() - }) + b.iter(|| data1.iter().merge_by(&data2, |a, b| a <= b).count()) }); } @@ -559,6 +487,8 @@ let mut data1 = vec![0; 1024]; let mut data2 = vec![0; 800]; let mut x = 0; + + #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)] for (_, elt) in data1.iter_mut().enumerate() { *elt = x; x += 1; @@ -577,11 +507,7 @@ let data2 = black_box(data2); let its = &[data1.iter(), data2.iter()]; - c.bench_function("kmerge default", move |b| { - b.iter(|| { - its.iter().cloned().kmerge().count() - }) - }); + c.bench_function("kmerge default", move |b| b.iter(|| its.iter().cloned().kmerge().count())); } fn kmerge_tenway(c: &mut Criterion) { @@ -589,7 +515,7 @@ let mut state = 1729u16; fn rng(state: &mut u16) -> u16 { - let new = state.wrapping_mul(31421) + 6927; + let new = state.wrapping_mul(31421).wrapping_add(6927); *state = new; new } @@ -600,27 +526,25 @@ let mut chunks = Vec::new(); let mut rest = &mut data[..]; - while rest.len() > 0 { + while !rest.is_empty() { let chunk_len = 1 + rng(&mut state) % 512; let chunk_len = cmp::min(rest.len(), chunk_len as usize); - let (fst, tail) = {rest}.split_at_mut(chunk_len); + let (fst, tail) = { rest }.split_at_mut(chunk_len); fst.sort(); chunks.push(fst.iter().cloned()); rest = tail; } - // println!("Chunk lengths: {}", chunks.iter().format_with(", ", |elt, f| f(&elt.len()))); + // println!("Chunk lengths: {}", chunks.iter().format_with(", ", |elt, f| + // f(&elt.len()))); - c.bench_function("kmerge tenway", move |b| { - b.iter(|| { - chunks.iter().cloned().kmerge().count() - }) - }); + c.bench_function("kmerge tenway", move |b| b.iter(|| chunks.iter().cloned().kmerge().count())); } fn fast_integer_sum<I>(iter: I) -> I::Item - where I: IntoIterator, - I::Item: Default + Add<Output=I::Item> +where + I: IntoIterator, + I::Item: Default + Add<Output = I::Item>, { iter.into_iter().fold(<_>::default(), |x, y| x + y) } @@ -629,9 +553,7 @@ let v = vec![0; 1024]; c.bench_function("step vec 2", move |b| { - b.iter(|| { - fast_integer_sum(cloned(v.iter().step_by(2))) - }) + b.iter(|| fast_integer_sum(cloned(v.iter().step_by(2)))) }); } @@ -639,29 +561,32 @@ let v = vec![0; 1024]; c.bench_function("step vec 10", move |b| { - b.iter(|| { - fast_integer_sum(cloned(v.iter().step_by(10))) - }) + b.iter(|| fast_integer_sum(cloned(v.iter().step_by(10)))) }); } fn step_range_2(c: &mut Criterion) { let v = black_box(0..1024); - c.bench_function("step range 2", move |b| { - b.iter(|| { - fast_integer_sum(v.clone().step_by(2)) - }) - }); + c.bench_function("step range 2", move |b| b.iter(|| fast_integer_sum(v.clone().step_by(2)))); } fn step_range_10(c: &mut Criterion) { let v = black_box(0..1024); - c.bench_function("step range 10", move |b| { - b.iter(|| { - fast_integer_sum(v.clone().step_by(10)) - }) + c.bench_function("step range 10", move |b| b.iter(|| fast_integer_sum(v.clone().step_by(10)))); +} + +fn vec_iter_mut_partition(c: &mut Criterion) { + let data = std::iter::repeat(-1024i32..1024).take(256).flatten().collect_vec(); + c.bench_function("vec iter mut partition", move |b| { + b.iter_batched( + || data.clone(), + |mut data| { + black_box(itertools::partition(black_box(&mut data), |n| *n >= 0)); + }, + BatchSize::LargeInput, + ) }); } @@ -681,22 +606,6 @@ }); } -fn cartesian_product_fold(c: &mut Criterion) { - let xs = vec![0; 16]; - - c.bench_function("cartesian product fold", move |b| { - b.iter(|| { - let mut sum = 0; - iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| { - sum += x; - sum += y; - sum += z; - }); - sum - }) - }); -} - fn multi_cartesian_product_iterator(c: &mut Criterion) { let xs = [vec![0; 16], vec![0; 16], vec![0; 16]]; @@ -713,22 +622,6 @@ }); } -fn multi_cartesian_product_fold(c: &mut Criterion) { - let xs = [vec![0; 16], vec![0; 16], vec![0; 16]]; - - c.bench_function("multi cartesian product fold", move |b| { - b.iter(|| { - let mut sum = 0; - xs.iter().multi_cartesian_product().fold((), |(), x| { - sum += x[0]; - sum += x[1]; - sum += x[2]; - }); - sum - }) - }); -} - fn cartesian_product_nested_for(c: &mut Criterion) { let xs = vec![0; 16]; @@ -753,9 +646,7 @@ let mut xs = vec![0; 5_000_000]; xs.extend(vec![1; 5_000_000]); - c.bench_function("all equal", move |b| { - b.iter(|| xs.iter().all_equal()) - }); + c.bench_function("all equal", move |b| b.iter(|| xs.iter().all_equal())); } fn all_equal_for(c: &mut Criterion) { @@ -778,9 +669,7 @@ let mut xs = vec![0; 5_000_000]; xs.extend(vec![1; 5_000_000]); - c.bench_function("all equal default", move |b| { - b.iter(|| xs.iter().dedup().nth(1).is_none()) - }); + c.bench_function("all equal default", move |b| b.iter(|| xs.iter().dedup().nth(1).is_none())); } const PERM_COUNT: usize = 6; @@ -797,21 +686,13 @@ } c.bench_function("permutations iter", move |b| { - b.iter(|| { - for _ in NewIterator(0..PERM_COUNT).permutations(PERM_COUNT) { - - } - }) + b.iter(|| for _ in NewIterator(0..PERM_COUNT).permutations(PERM_COUNT) {}) }); } fn permutations_range(c: &mut Criterion) { c.bench_function("permutations range", move |b| { - b.iter(|| { - for _ in (0..PERM_COUNT).permutations(PERM_COUNT) { - - } - }) + b.iter(|| for _ in (0..PERM_COUNT).permutations(PERM_COUNT) {}) }); } @@ -819,11 +700,7 @@ let v = (0..PERM_COUNT).collect_vec(); c.bench_function("permutations slice", move |b| { - b.iter(|| { - for _ in v.as_slice().iter().permutations(PERM_COUNT) { - - } - }) + b.iter(|| for _ in v.as_slice().iter().permutations(PERM_COUNT) {}) }); } @@ -836,10 +713,6 @@ zipdot_f32_default_zip, zip_default_zip3, zip_slices_ziptuple, - zipslices, - zipslices_mut, - zipdot_i32_zipslices, - zipdot_f32_zipslices, zip_checked_counted_loop, zipdot_i32_checked_counted_loop, zipdot_f32_checked_counted_loop, @@ -848,8 +721,8 @@ zipdot_i32_unchecked_counted_loop, zipdot_f32_unchecked_counted_loop, zip_unchecked_counted_loop3, - group_by_lazy_1, - group_by_lazy_2, + chunk_by_lazy_1, + chunk_by_lazy_2, slice_chunks, chunks_lazy_1, equal, @@ -862,10 +735,9 @@ step_vec_10, step_range_2, step_range_10, + vec_iter_mut_partition, cartesian_product_iterator, - cartesian_product_fold, multi_cartesian_product_iterator, - multi_cartesian_product_fold, cartesian_product_nested_for, all_equal, all_equal_for,
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/combinations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/combinations.rs similarity index 95% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/combinations.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/combinations.rs index e7433a4..42a45211 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/combinations.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/combinations.rs
@@ -111,15 +111,7 @@ } criterion_group!( - benches, - comb_for1, - comb_for2, - comb_for3, - comb_for4, - comb_c1, - comb_c2, - comb_c3, - comb_c4, + benches, comb_for1, comb_for2, comb_for3, comb_for4, comb_c1, comb_c2, comb_c3, comb_c4, comb_c14, ); criterion_main!(benches);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/combinations_with_replacement.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/combinations_with_replacement.rs similarity index 100% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/combinations_with_replacement.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/combinations_with_replacement.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/fold_specialization.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/fold_specialization.rs similarity index 82% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/fold_specialization.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/fold_specialization.rs index 5de4671..b44f347 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/fold_specialization.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/fold_specialization.rs
@@ -1,10 +1,13 @@ +#![allow(unstable_name_collisions)] + use criterion::{criterion_group, criterion_main, Criterion}; use itertools::Itertools; struct Unspecialized<I>(I); impl<I> Iterator for Unspecialized<I> -where I: Iterator +where + I: Iterator, { type Item = I::Item; @@ -25,8 +28,7 @@ pub mod intersperse { use super::*; - pub fn external(c: &mut Criterion) - { + pub fn external(c: &mut Criterion) { let arr = [1; 1024]; c.bench_function("external", move |b| { @@ -40,23 +42,23 @@ }); } - pub fn internal_specialized(c: &mut Criterion) - { + pub fn internal_specialized(c: &mut Criterion) { let arr = [1; 1024]; c.bench_function("internal specialized", move |b| { b.iter(|| { + #[allow(clippy::unnecessary_fold)] arr.iter().intersperse(&0).fold(0, |acc, x| acc + x) }) }); } - pub fn internal_unspecialized(c: &mut Criterion) - { + pub fn internal_unspecialized(c: &mut Criterion) { let arr = [1; 1024]; c.bench_function("internal unspecialized", move |b| { b.iter(|| { + #[allow(clippy::unnecessary_fold)] Unspecialized(arr.iter().intersperse(&0)).fold(0, |acc, x| acc + x) }) });
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/k_smallest.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/k_smallest.rs new file mode 100644 index 0000000..509ed7f --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/k_smallest.rs
@@ -0,0 +1,61 @@ +use criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion}; +use itertools::Itertools; +use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng}; + +fn strict(b: &mut Bencher, (k, vals): &(usize, &Vec<usize>)) { + b.iter(|| black_box(vals.iter()).k_smallest(*k)) +} + +fn relaxed(b: &mut Bencher, (k, vals): &(usize, &Vec<usize>)) { + b.iter(|| black_box(vals.iter()).k_smallest_relaxed(*k)) +} + +fn ascending(n: usize) -> Vec<usize> { + (0..n).collect() +} + +fn random(n: usize) -> Vec<usize> { + let mut vals = (0..n).collect_vec(); + vals.shuffle(&mut StdRng::seed_from_u64(42)); + vals +} + +fn descending(n: usize) -> Vec<usize> { + (0..n).rev().collect() +} + +fn k_smallest(c: &mut Criterion, order: &str, vals: fn(usize) -> Vec<usize>) { + let mut g = c.benchmark_group(format!("k-smallest/{order}")); + + for log_n in 20..23 { + let n = 1 << log_n; + + let vals = vals(n); + + for log_k in 7..10 { + let k = 1 << log_k; + + let params = format!("{log_n}/{log_k}"); + let input = (k, &vals); + g.bench_with_input(BenchmarkId::new("strict", ¶ms), &input, strict); + g.bench_with_input(BenchmarkId::new("relaxed", ¶ms), &input, relaxed); + } + } + + g.finish() +} + +fn k_smallest_asc(c: &mut Criterion) { + k_smallest(c, "asc", ascending); +} + +fn k_smallest_rand(c: &mut Criterion) { + k_smallest(c, "rand", random); +} + +fn k_smallest_desc(c: &mut Criterion) { + k_smallest(c, "desc", descending); +} + +criterion_group!(benches, k_smallest_asc, k_smallest_rand, k_smallest_desc); +criterion_main!(benches);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/powerset.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/powerset.rs new file mode 100644 index 0000000..018333d3 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/powerset.rs
@@ -0,0 +1,97 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use itertools::Itertools; + +// Keep aggregate generated elements the same, regardless of powerset length. +const TOTAL_ELEMENTS: usize = 1 << 12; +const fn calc_iters(n: usize) -> usize { + TOTAL_ELEMENTS / (1 << n) +} + +fn powerset_n(c: &mut Criterion, n: usize) { + let id = format!("powerset {}", n); + c.bench_function(id.as_str(), move |b| { + b.iter(|| { + for _ in 0..calc_iters(n) { + for elt in (0..n).powerset() { + black_box(elt); + } + } + }) + }); +} + +fn powerset_n_fold(c: &mut Criterion, n: usize) { + let id = format!("powerset {} fold", n); + c.bench_function(id.as_str(), move |b| { + b.iter(|| { + for _ in 0..calc_iters(n) { + (0..n).powerset().fold(0, |s, elt| s + black_box(elt).len()); + } + }) + }); +} + +fn powerset_0(c: &mut Criterion) { + powerset_n(c, 0); +} + +fn powerset_1(c: &mut Criterion) { + powerset_n(c, 1); +} + +fn powerset_2(c: &mut Criterion) { + powerset_n(c, 2); +} + +fn powerset_4(c: &mut Criterion) { + powerset_n(c, 4); +} + +fn powerset_8(c: &mut Criterion) { + powerset_n(c, 8); +} + +fn powerset_12(c: &mut Criterion) { + powerset_n(c, 12); +} + +fn powerset_0_fold(c: &mut Criterion) { + powerset_n_fold(c, 0); +} + +fn powerset_1_fold(c: &mut Criterion) { + powerset_n_fold(c, 1); +} + +fn powerset_2_fold(c: &mut Criterion) { + powerset_n_fold(c, 2); +} + +fn powerset_4_fold(c: &mut Criterion) { + powerset_n_fold(c, 4); +} + +fn powerset_8_fold(c: &mut Criterion) { + powerset_n_fold(c, 8); +} + +fn powerset_12_fold(c: &mut Criterion) { + powerset_n_fold(c, 12); +} + +criterion_group!( + benches, + powerset_0, + powerset_1, + powerset_2, + powerset_4, + powerset_8, + powerset_12, + powerset_0_fold, + powerset_1_fold, + powerset_2_fold, + powerset_4_fold, + powerset_8_fold, + powerset_12_fold, +); +criterion_main!(benches);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/specializations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/specializations.rs new file mode 100644 index 0000000..aca6a55 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/specializations.rs
@@ -0,0 +1,670 @@ +#![allow(unstable_name_collisions)] + +use criterion::black_box; +use criterion::BenchmarkId; +use itertools::Itertools; + +const NTH_INPUTS: &[usize] = &[0, 1, 2, 4, 8]; + +/// Create multiple functions each defining a benchmark group about iterator +/// methods. +/// +/// Each created group has functions with the following ids: +/// +/// - `next`, `size_hint`, `count`, `last`, `nth`, `collect`, `fold` +/// - and when marked as `DoubleEndedIterator`: `next_back`, `nth_back`, `rfold` +/// - and when marked as `ExactSizeIterator`: `len` +/// +/// Note that this macro can be called only once. +macro_rules! bench_specializations { + ( + $( + $name:ident { + $($extra:ident)* + {$( + $init:stmt; + )*} + $iterator:expr + } + )* + ) => { + $( + #[allow(unused_must_use)] + fn $name(c: &mut ::criterion::Criterion) { + let mut bench_group = c.benchmark_group(stringify!($name)); + $( + $init + )* + let bench_first_its = { + let mut bench_idx = 0; + [0; 1000].map(|_| { + let mut it = $iterator; + if bench_idx != 0 { + it.nth(bench_idx - 1); + } + bench_idx += 1; + it + }) + }; + bench_specializations!(@Iterator bench_group bench_first_its: $iterator); + $( + bench_specializations!(@$extra bench_group bench_first_its: $iterator); + )* + bench_group.finish(); + } + )* + + ::criterion::criterion_group!(benches, $($name, )*); + ::criterion::criterion_main!(benches); + }; + + (@Iterator $group:ident $first_its:ident: $iterator:expr) => { + $group.bench_function("next", |bencher| bencher.iter(|| { + let mut it = $iterator; + while let Some(x) = it.next() { + black_box(x); + } + })); + $group.bench_function("size_hint", |bencher| bencher.iter(|| { + $first_its.iter().for_each(|it| { + black_box(it.size_hint()); + }) + })); + $group.bench_function("count", |bencher| bencher.iter(|| { + $iterator.count() + })); + $group.bench_function("last", |bencher| bencher.iter(|| { + $iterator.last() + })); + for n in NTH_INPUTS { + $group.bench_with_input(BenchmarkId::new("nth", n), n, |bencher, n| bencher.iter(|| { + for start in 0_usize..10 { + let mut it = $iterator; + if let Some(s) = start.checked_sub(1) { + black_box(it.nth(s)); + } + while let Some(x) = it.nth(*n) { + black_box(x); + } + } + })); + } + $group.bench_function("collect", |bencher| bencher.iter(|| { + $iterator.collect::<Vec<_>>() + })); + $group.bench_function("fold", |bencher| bencher.iter(|| { + $iterator.fold((), |(), x| { + black_box(x); + }) + })); + }; + + (@DoubleEndedIterator $group:ident $_first_its:ident: $iterator:expr) => { + $group.bench_function("next_back", |bencher| bencher.iter(|| { + let mut it = $iterator; + while let Some(x) = it.next_back() { + black_box(x); + } + })); + for n in NTH_INPUTS { + $group.bench_with_input(BenchmarkId::new("nth_back", n), n, |bencher, n| bencher.iter(|| { + for start in 0_usize..10 { + let mut it = $iterator; + if let Some(s) = start.checked_sub(1) { + black_box(it.nth_back(s)); + } + while let Some(x) = it.nth_back(*n) { + black_box(x); + } + } + })); + } + $group.bench_function("rfold", |bencher| bencher.iter(|| { + $iterator.rfold((), |(), x| { + black_box(x); + }) + })); + }; + + (@ExactSizeIterator $group:ident $first_its:ident: $_iterator:expr) => { + $group.bench_function("len", |bencher| bencher.iter(|| { + $first_its.iter().for_each(|it| { + black_box(it.len()); + }) + })); + }; +} + +// Usage examples: +// - For `ZipLongest::fold` only: cargo bench --bench specializations +// zip_longest/fold +// - For `.combinations(k).nth(8)`: cargo bench --bench specializations +// combinations./nth/8 +bench_specializations! { + interleave { + { + let v1 = black_box(vec![0; 1024]); + let v2 = black_box(vec![0; 768]); + } + v1.iter().interleave(&v2) + } + interleave_shortest { + { + let v1 = black_box(vec![0; 1024]); + let v2 = black_box(vec![0; 768]); + } + v1.iter().interleave_shortest(&v2) + } + batching { + { + let v = black_box(vec![0; 1024]); + } + v.iter().batching(Iterator::next) + } + tuple_windows1 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().tuple_windows::<(_,)>() + } + tuple_windows2 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().tuple_windows::<(_, _)>() + } + tuple_windows3 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().tuple_windows::<(_, _, _)>() + } + tuple_windows4 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().tuple_windows::<(_, _, _, _)>() + } + circular_tuple_windows1 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().circular_tuple_windows::<(_,)>() + } + circular_tuple_windows2 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().circular_tuple_windows::<(_, _)>() + } + circular_tuple_windows3 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().circular_tuple_windows::<(_, _, _)>() + } + circular_tuple_windows4 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().circular_tuple_windows::<(_, _, _, _)>() + } + tuples1 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().tuples::<(_,)>() + } + tuples2 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().tuples::<(_, _)>() + } + tuples3 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().tuples::<(_, _, _)>() + } + tuples4 { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().tuples::<(_, _, _, _)>() + } + tuple_buffer { + ExactSizeIterator + { + let v = black_box(vec![0; 11]); + // Short but the buffer can't have 12 or more elements. + } + { + let mut it = v.iter().tuples::<(_, _, _, _, _, _, _, _, _, _, _, _)>(); + it.next(); // No element but it fills the buffer. + it.into_buffer() + } + } + cartesian_product { + { + let v = black_box(vec![0; 16]); + } + itertools::iproduct!(&v, &v, &v) + } + multi_cartesian_product { + { + let vs = black_box([0; 3].map(|_| vec![0; 16])); + } + vs.iter().multi_cartesian_product() + } + coalesce { + { + let v = black_box(vec![0; 1024]); + } + v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) }) + } + dedup { + { + let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); + } + v.iter().dedup() + } + dedup_by { + { + let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); + } + v.iter().dedup_by(PartialOrd::ge) + } + dedup_with_count { + { + let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); + } + v.iter().dedup_with_count() + } + dedup_by_with_count { + { + let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); + } + v.iter().dedup_by_with_count(PartialOrd::ge) + } + duplicates { + DoubleEndedIterator + { + let v = black_box((0..32).cycle().take(1024).collect_vec()); + } + v.iter().duplicates() + } + duplicates_by { + DoubleEndedIterator + { + let v = black_box((0..1024).collect_vec()); + } + v.iter().duplicates_by(|x| *x % 10) + } + unique { + DoubleEndedIterator + { + let v = black_box((0..32).cycle().take(1024).collect_vec()); + } + v.iter().unique() + } + unique_by { + DoubleEndedIterator + { + let v = black_box((0..1024).collect_vec()); + } + v.iter().unique_by(|x| *x % 50) + } + take_while_inclusive { + { + let v = black_box((0..1024).collect_vec()); + } + v.iter().take_while_inclusive(|x| **x < 1000) + } + pad_using { + DoubleEndedIterator + ExactSizeIterator + { + let v = black_box((0..1024).collect_vec()); + } + v.iter().copied().pad_using(2048, |i| 5 * i) + } + positions { + DoubleEndedIterator + { + let v = black_box((0..1024).collect_vec()); + } + v.iter().positions(|x| x % 5 == 0) + } + update { + DoubleEndedIterator + ExactSizeIterator + { + let v = black_box((0_i32..1024).collect_vec()); + } + v.iter().copied().update(|x| *x *= 7) + } + tuple_combinations1 { + { + let v = black_box(vec![0; 1024]); + } + v.iter().tuple_combinations::<(_,)>() + } + tuple_combinations2 { + { + let v = black_box(vec![0; 64]); + } + v.iter().tuple_combinations::<(_, _)>() + } + tuple_combinations3 { + { + let v = black_box(vec![0; 64]); + } + v.iter().tuple_combinations::<(_, _, _)>() + } + tuple_combinations4 { + { + let v = black_box(vec![0; 64]); + } + v.iter().tuple_combinations::<(_, _, _, _)>() + } + intersperse { + { + let v = black_box(vec![0; 1024]); + let n = black_box(0); + } + v.iter().intersperse(&n) + } + intersperse_with { + { + let v = black_box(vec![0; 1024]); + let n = black_box(0); + } + v.iter().intersperse_with(|| &n) + } + combinations1 { + { + let v = black_box(vec![0; 1792]); + } + v.iter().combinations(1) + } + combinations2 { + { + let v = black_box(vec![0; 60]); + } + v.iter().combinations(2) + } + combinations3 { + { + let v = black_box(vec![0; 23]); + } + v.iter().combinations(3) + } + combinations4 { + { + let v = black_box(vec![0; 16]); + } + v.iter().combinations(4) + } + combinations_with_replacement1 { + { + let v = black_box(vec![0; 4096]); + } + v.iter().combinations_with_replacement(1) + } + combinations_with_replacement2 { + { + let v = black_box(vec![0; 90]); + } + v.iter().combinations_with_replacement(2) + } + combinations_with_replacement3 { + { + let v = black_box(vec![0; 28]); + } + v.iter().combinations_with_replacement(3) + } + combinations_with_replacement4 { + { + let v = black_box(vec![0; 16]); + } + v.iter().combinations_with_replacement(4) + } + permutations1 { + { + let v = black_box(vec![0; 1024]); + } + v.iter().permutations(1) + } + permutations2 { + { + let v = black_box(vec![0; 36]); + } + v.iter().permutations(2) + } + permutations3 { + { + let v = black_box(vec![0; 12]); + } + v.iter().permutations(3) + } + permutations4 { + { + let v = black_box(vec![0; 8]); + } + v.iter().permutations(4) + } + powerset { + { + let v = black_box(vec![0; 10]); + } + v.iter().powerset() + } + while_some { + {} + (0..) + .map(black_box) + .map(|i| char::from_digit(i, 16)) + .while_some() + } + with_position { + ExactSizeIterator + { + let v = black_box((0..10240).collect_vec()); + } + v.iter().with_position() + } + zip_longest { + DoubleEndedIterator + ExactSizeIterator + { + let xs = black_box(vec![0; 1024]); + let ys = black_box(vec![0; 768]); + } + xs.iter().zip_longest(ys.iter()) + } + zip_eq { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + v.iter().zip_eq(v.iter().rev()) + } + multizip { + DoubleEndedIterator + ExactSizeIterator + { + let v1 = black_box(vec![0; 1024]); + let v2 = black_box(vec![0; 768]); + let v3 = black_box(vec![0; 2048]); + } + itertools::multizip((&v1, &v2, &v3)) + } + izip { + DoubleEndedIterator + ExactSizeIterator + { + let v1 = black_box(vec![0; 1024]); + let v2 = black_box(vec![0; 768]); + let v3 = black_box(vec![0; 2048]); + } + itertools::izip!(&v1, &v2, &v3) + } + put_back { + { + let v = black_box(vec![0; 1024]); + } + itertools::put_back(&v).with_value(black_box(&0)) + } + put_back_n { + { + let v1 = black_box(vec![0; 1024]); + let v2 = black_box(vec![0; 16]); + } + { + let mut it = itertools::put_back_n(&v1); + for n in &v2 { + it.put_back(n); + } + it + } + } + exactly_one_error { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + } + // Use `at_most_one` would be similar. + v.iter().exactly_one().unwrap_err() + } + multipeek { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + let n = black_box(16); + } + { + let mut it = v.iter().multipeek(); + for _ in 0..n { + it.peek(); + } + it + } + } + peek_nth { + ExactSizeIterator + { + let v = black_box(vec![0; 1024]); + let n = black_box(16); + } + { + let mut it = itertools::peek_nth(&v); + it.peek_nth(n); + it + } + } + repeat_n { + DoubleEndedIterator + ExactSizeIterator + {} + itertools::repeat_n(black_box(0), black_box(1024)) + } + merge { + { + let v1 = black_box((0..1024).collect_vec()); + let v2 = black_box((0..768).collect_vec()); + } + v1.iter().merge(&v2) + } + merge_by { + { + let v1 = black_box((0..1024).collect_vec()); + let v2 = black_box((0..768).collect_vec()); + } + v1.iter().merge_by(&v2, PartialOrd::ge) + } + merge_join_by_ordering { + { + let v1 = black_box((0..1024).collect_vec()); + let v2 = black_box((0..768).collect_vec()); + } + v1.iter().merge_join_by(&v2, Ord::cmp) + } + merge_join_by_bool { + { + let v1 = black_box((0..1024).collect_vec()); + let v2 = black_box((0..768).collect_vec()); + } + v1.iter().merge_join_by(&v2, PartialOrd::ge) + } + kmerge { + { + let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]); + } + vs.iter().kmerge() + } + kmerge_by { + { + let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]); + } + vs.iter().kmerge_by(PartialOrd::ge) + } + map_into { + DoubleEndedIterator + ExactSizeIterator + { + let v = black_box(vec![0_u8; 1024]); + } + v.iter().copied().map_into::<u32>() + } + map_ok { + DoubleEndedIterator + ExactSizeIterator + { + let v = black_box((0_u32..1024) + .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) }) + .collect_vec()); + } + v.iter().copied().map_ok(|x| x + 1) + } + filter_ok { + DoubleEndedIterator + { + let v = black_box((0_u32..1024) + .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) }) + .collect_vec()); + } + v.iter().copied().filter_ok(|x| x % 3 == 0) + } + filter_map_ok { + DoubleEndedIterator + { + let v = black_box((0_u32..1024) + .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) }) + .collect_vec()); + } + v.iter().copied().filter_map_ok(|x| if x % 3 == 0 { Some(x + 1) } else { None }) + } + flatten_ok { + DoubleEndedIterator + { + let d = black_box(vec![0; 8]); + let v = black_box((0..512) + .map(|x| if x % 2 == 0 { Ok(&d) } else { Err(x) }) + .collect_vec()); + } + v.iter().copied().flatten_ok() + } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tree_reduce.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tree_reduce.rs new file mode 100644 index 0000000..722a78cd --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tree_reduce.rs
@@ -0,0 +1,147 @@ +#![allow(deprecated)] + +use criterion::{criterion_group, criterion_main, Criterion}; +use itertools::{cloned, Itertools}; + +trait IterEx: Iterator { + // Another efficient implementation against which to compare, + // but needs `std` so is less desirable. + fn tree_reduce_vec<F>(self, mut f: F) -> Option<Self::Item> + where + F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized, + { + let hint = self.size_hint().0; + let cap = std::mem::size_of::<usize>() * 8 - hint.leading_zeros() as usize; + let mut stack = Vec::with_capacity(cap); + self.enumerate().for_each(|(mut i, mut x)| { + while (i & 1) != 0 { + x = f(stack.pop().unwrap(), x); + i >>= 1; + } + stack.push(x); + }); + stack.into_iter().fold1(f) + } +} +impl<T: Iterator> IterEx for T {} + +macro_rules! def_benchs { + ($N:expr, + $FUN:ident, + $BENCH_NAME:ident, + ) => { + mod $BENCH_NAME { + use super::*; + + pub fn sum(c: &mut Criterion) { + let v: Vec<u32> = (0..$N).collect(); + + c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " sum"), move |b| { + b.iter(|| cloned(&v).$FUN(|x, y| x + y)) + }); + } + + pub fn complex_iter(c: &mut Criterion) { + let u = (3..).take($N / 2); + let v = (5..).take($N / 2); + let it = u.chain(v); + + c.bench_function( + &(stringify!($BENCH_NAME).replace('_', " ") + " complex iter"), + move |b| b.iter(|| it.clone().map(|x| x as f32).$FUN(f32::atan2)), + ); + } + + pub fn string_format(c: &mut Criterion) { + // This goes quadratic with linear `fold1`, so use a smaller + // size to not waste too much time in travis. The allocations + // in here are so expensive anyway that it'll still take + // way longer per iteration than the other two benchmarks. + let v: Vec<u32> = (0..($N / 4)).collect(); + + c.bench_function( + &(stringify!($BENCH_NAME).replace('_', " ") + " string format"), + move |b| { + b.iter(|| { + cloned(&v).map(|x| x.to_string()).$FUN(|x, y| format!("{} + {}", x, y)) + }) + }, + ); + } + } + + criterion_group!( + $BENCH_NAME, + $BENCH_NAME::sum, + $BENCH_NAME::complex_iter, + $BENCH_NAME::string_format, + ); + }; +} + +def_benchs! { + 10_000, + fold1, + fold1_10k, +} + +def_benchs! { + 10_000, + tree_reduce, + tree_reduce_stack_10k, +} + +def_benchs! { + 10_000, + tree_reduce_vec, + tree_reduce_vec_10k, +} + +def_benchs! { + 100, + fold1, + fold1_100, +} + +def_benchs! { + 100, + tree_reduce, + tree_reduce_stack_100, +} + +def_benchs! { + 100, + tree_reduce_vec, + tree_reduce_vec_100, +} + +def_benchs! { + 8, + fold1, + fold1_08, +} + +def_benchs! { + 8, + tree_reduce, + tree_reduce_stack_08, +} + +def_benchs! { + 8, + tree_reduce_vec, + tree_reduce_vec_08, +} + +criterion_main!( + fold1_10k, + tree_reduce_stack_10k, + tree_reduce_vec_10k, + fold1_100, + tree_reduce_stack_100, + tree_reduce_vec_100, + fold1_08, + tree_reduce_stack_08, + tree_reduce_vec_08, +);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tuple_combinations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tuple_combinations.rs similarity index 100% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tuple_combinations.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tuple_combinations.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tuples.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tuples.rs similarity index 92% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tuples.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tuples.rs index ea50aaae..2eca347 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tuples.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tuples.rs
@@ -33,7 +33,7 @@ s4(s[0], s[1], s[2], s[3]) } -fn sum_t1(s: &(&u32, )) -> u32 { +fn sum_t1(s: &(&u32,)) -> u32 { s1(*s.0) } @@ -60,9 +60,9 @@ $WINDOWS:ident; $FOR_CHUNKS:ident, $FOR_WINDOWS:ident - ) => ( + ) => { fn $FOR_CHUNKS(c: &mut Criterion) { - let v: Vec<u32> = (0.. $N * 1_000).collect(); + let v: Vec<u32> = (0..$N * 1_000).collect(); let mut s = 0; c.bench_function(&stringify!($FOR_CHUNKS).replace('_', " "), move |b| { b.iter(|| { @@ -90,7 +90,7 @@ } fn $TUPLES(c: &mut Criterion) { - let v: Vec<u32> = (0.. $N * 1_000).collect(); + let v: Vec<u32> = (0..$N * 1_000).collect(); let mut s = 0; c.bench_function(&stringify!($TUPLES).replace('_', " "), move |b| { b.iter(|| { @@ -103,7 +103,7 @@ } fn $CHUNKS(c: &mut Criterion) { - let v: Vec<u32> = (0.. $N * 1_000).collect(); + let v: Vec<u32> = (0..$N * 1_000).collect(); let mut s = 0; c.bench_function(&stringify!($CHUNKS).replace('_', " "), move |b| { b.iter(|| { @@ -150,10 +150,10 @@ $TUPLE_WINDOWS, $WINDOWS, ); - ) + }; } -def_benchs!{ +def_benchs! { 1; benches_1, sum_t1, @@ -166,7 +166,7 @@ for_windows_1 } -def_benchs!{ +def_benchs! { 2; benches_2, sum_t2, @@ -179,7 +179,7 @@ for_windows_2 } -def_benchs!{ +def_benchs! { 3; benches_3, sum_t3, @@ -192,7 +192,7 @@ for_windows_3 } -def_benchs!{ +def_benchs! { 4; benches_4, sum_t4, @@ -205,9 +205,4 @@ for_windows_4 } -criterion_main!( - benches_1, - benches_2, - benches_3, - benches_4, -); +criterion_main!(benches_1, benches_2, benches_3, benches_4,);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/examples/iris.data b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/examples/iris.data similarity index 100% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/examples/iris.data rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/examples/iris.data
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/examples/iris.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/examples/iris.rs similarity index 81% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/examples/iris.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/examples/iris.rs index 987d9e9..e7783436 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/examples/iris.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/examples/iris.rs
@@ -3,14 +3,13 @@ /// and does some simple manipulations. /// /// Iterators and itertools functionality are used throughout. - use itertools::Itertools; use std::collections::HashMap; use std::iter::repeat; use std::num::ParseFloatError; use std::str::FromStr; -static DATA: &'static str = include_str!("iris.data"); +static DATA: &str = include_str!("iris.data"); #[derive(Clone, Debug)] struct Iris { @@ -18,6 +17,7 @@ data: [f32; 4], } +#[allow(dead_code)] // fields are currently ignored #[derive(Clone, Debug)] enum ParseError { Numeric(ParseFloatError), @@ -26,7 +26,7 @@ impl From<ParseFloatError> for ParseError { fn from(err: ParseFloatError) -> Self { - ParseError::Numeric(err) + Self::Numeric(err) } } @@ -35,8 +35,8 @@ type Err = ParseError; fn from_str(s: &str) -> Result<Self, Self::Err> { - let mut iris = Iris { name: "".into(), data: [0.; 4] }; - let mut parts = s.split(",").map(str::trim); + let mut iris = Self { name: "".into(), data: [0.; 4] }; + let mut parts = s.split(',').map(str::trim); // using Iterator::by_ref() for (index, part) in parts.by_ref().take(4).enumerate() { @@ -45,7 +45,7 @@ if let Some(name) = parts.next() { iris.name = name.into(); } else { - return Err(ParseError::Other("Missing name")) + return Err(ParseError::Other("Missing name")); } Ok(iris) } @@ -53,12 +53,10 @@ fn main() { // using Itertools::fold_results to create the result of parsing - let irises = DATA.lines() - .map(str::parse) - .fold_ok(Vec::new(), |mut v, iris: Iris| { - v.push(iris); - v - }); + let irises = DATA.lines().map(str::parse).fold_ok(Vec::new(), |mut v, iris: Iris| { + v.push(iris); + v + }); let mut irises = match irises { Err(e) => { println!("Error parsing: {:?}", e); @@ -74,19 +72,16 @@ let mut plot_symbols = "+ox".chars().cycle(); let mut symbolmap = HashMap::new(); - // using Itertools::group_by - for (species, species_group) in &irises.iter().group_by(|iris| &iris.name) { + // using Itertools::chunk_by + for (species, species_chunk) in &irises.iter().chunk_by(|iris| &iris.name) { // assign a plot symbol - symbolmap.entry(species).or_insert_with(|| { - plot_symbols.next().unwrap() - }); + symbolmap.entry(species).or_insert_with(|| plot_symbols.next().unwrap()); println!("{} (symbol={})", species, symbolmap[species]); - for iris in species_group { + for iris in species_chunk { // using Itertools::format for lazy formatting println!("{:>3.1}", iris.data.iter().format(", ")); } - } // Look at all combinations of the four columns
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/coalesce.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/coalesce.rs new file mode 100644 index 0000000..5c0ddd45 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/coalesce.rs
@@ -0,0 +1,274 @@ +use std::fmt; +use std::iter::FusedIterator; + +use crate::size_hint; + +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct CoalesceBy<I, F, C> +where + I: Iterator, + C: CountItem<I::Item>, +{ + iter: I, + /// `last` is `None` while no item have been taken out of `iter` (at + /// definition). Then `last` will be `Some(Some(item))` until `iter` is + /// exhausted, in which case `last` will be `Some(None)`. + last: Option<Option<C::CItem>>, + f: F, +} + +impl<I, F, C> Clone for CoalesceBy<I, F, C> +where + I: Clone + Iterator, + F: Clone, + C: CountItem<I::Item>, + C::CItem: Clone, +{ + clone_fields!(last, iter, f); +} + +impl<I, F, C> fmt::Debug for CoalesceBy<I, F, C> +where + I: Iterator + fmt::Debug, + C: CountItem<I::Item>, + C::CItem: fmt::Debug, +{ + debug_fmt_fields!(CoalesceBy, iter, last); +} + +pub trait CoalescePredicate<Item, T> { + fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)>; +} + +impl<I, F, C> Iterator for CoalesceBy<I, F, C> +where + I: Iterator, + F: CoalescePredicate<I::Item, C::CItem>, + C: CountItem<I::Item>, +{ + type Item = C::CItem; + + fn next(&mut self) -> Option<Self::Item> { + let Self { iter, last, f } = self; + // this fuses the iterator + let init = match last { + Some(elt) => elt.take(), + None => { + *last = Some(None); + iter.next().map(C::new) + } + }?; + + Some( + iter.try_fold(init, |accum, next| match f.coalesce_pair(accum, next) { + Ok(joined) => Ok(joined), + Err((last_, next_)) => { + *last = Some(Some(next_)); + Err(last_) + } + }) + .unwrap_or_else(|x| x), + ) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let (low, hi) = size_hint::add_scalar( + self.iter.size_hint(), + matches!(self.last, Some(Some(_))) as usize, + ); + ((low > 0) as usize, hi) + } + + fn fold<Acc, FnAcc>(self, acc: Acc, mut fn_acc: FnAcc) -> Acc + where + FnAcc: FnMut(Acc, Self::Item) -> Acc, + { + let Self { mut iter, last, mut f } = self; + if let Some(last) = last.unwrap_or_else(|| iter.next().map(C::new)) { + let (last, acc) = + iter.fold((last, acc), |(last, acc), elt| match f.coalesce_pair(last, elt) { + Ok(joined) => (joined, acc), + Err((last_, next_)) => (next_, fn_acc(acc, last_)), + }); + fn_acc(acc, last) + } else { + acc + } + } +} + +impl<I, F, C> FusedIterator for CoalesceBy<I, F, C> +where + I: Iterator, + F: CoalescePredicate<I::Item, C::CItem>, + C: CountItem<I::Item>, +{ +} + +pub struct NoCount; + +pub struct WithCount; + +pub trait CountItem<T> { + type CItem; + fn new(t: T) -> Self::CItem; +} + +impl<T> CountItem<T> for NoCount { + type CItem = T; + #[inline(always)] + fn new(t: T) -> T { + t + } +} + +impl<T> CountItem<T> for WithCount { + type CItem = (usize, T); + #[inline(always)] + fn new(t: T) -> (usize, T) { + (1, t) + } +} + +/// An iterator adaptor that may join together adjacent elements. +/// +/// See [`.coalesce()`](crate::Itertools::coalesce) for more information. +pub type Coalesce<I, F> = CoalesceBy<I, F, NoCount>; + +impl<F, Item, T> CoalescePredicate<Item, T> for F +where + F: FnMut(T, Item) -> Result<T, (T, T)>, +{ + fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)> { + self(t, item) + } +} + +/// Create a new `Coalesce`. +pub fn coalesce<I, F>(iter: I, f: F) -> Coalesce<I, F> +where + I: Iterator, +{ + Coalesce { last: None, iter, f } +} + +/// An iterator adaptor that removes repeated duplicates, determining equality +/// using a comparison function. +/// +/// See [`.dedup_by()`](crate::Itertools::dedup_by) or +/// [`.dedup()`](crate::Itertools::dedup) for more information. +pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, NoCount>; + +#[derive(Clone)] +pub struct DedupPred2CoalescePred<DP>(DP); + +impl<DP> fmt::Debug for DedupPred2CoalescePred<DP> { + debug_fmt_fields!(DedupPred2CoalescePred,); +} + +pub trait DedupPredicate<T> { + // TODO replace by Fn(&T, &T)->bool once Rust supports it + fn dedup_pair(&mut self, a: &T, b: &T) -> bool; +} + +impl<DP, T> CoalescePredicate<T, T> for DedupPred2CoalescePred<DP> +where + DP: DedupPredicate<T>, +{ + fn coalesce_pair(&mut self, t: T, item: T) -> Result<T, (T, T)> { + if self.0.dedup_pair(&t, &item) { + Ok(t) + } else { + Err((t, item)) + } + } +} + +#[derive(Clone, Debug)] +pub struct DedupEq; + +impl<T: PartialEq> DedupPredicate<T> for DedupEq { + fn dedup_pair(&mut self, a: &T, b: &T) -> bool { + a == b + } +} + +impl<T, F: FnMut(&T, &T) -> bool> DedupPredicate<T> for F { + fn dedup_pair(&mut self, a: &T, b: &T) -> bool { + self(a, b) + } +} + +/// Create a new `DedupBy`. +pub fn dedup_by<I, Pred>(iter: I, dedup_pred: Pred) -> DedupBy<I, Pred> +where + I: Iterator, +{ + DedupBy { last: None, iter, f: DedupPred2CoalescePred(dedup_pred) } +} + +/// An iterator adaptor that removes repeated duplicates. +/// +/// See [`.dedup()`](crate::Itertools::dedup) for more information. +pub type Dedup<I> = DedupBy<I, DedupEq>; + +/// Create a new `Dedup`. +pub fn dedup<I>(iter: I) -> Dedup<I> +where + I: Iterator, +{ + dedup_by(iter, DedupEq) +} + +/// An iterator adaptor that removes repeated duplicates, while keeping a count +/// of how many repeated elements were present. This will determine equality +/// using a comparison function. +/// +/// See [`.dedup_by_with_count()`](crate::Itertools::dedup_by_with_count) or +/// [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more +/// information. +pub type DedupByWithCount<I, Pred> = + CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, WithCount>; + +#[derive(Clone, Debug)] +pub struct DedupPredWithCount2CoalescePred<DP>(DP); + +impl<DP, T> CoalescePredicate<T, (usize, T)> for DedupPredWithCount2CoalescePred<DP> +where + DP: DedupPredicate<T>, +{ + fn coalesce_pair( + &mut self, + (c, t): (usize, T), + item: T, + ) -> Result<(usize, T), ((usize, T), (usize, T))> { + if self.0.dedup_pair(&t, &item) { + Ok((c + 1, t)) + } else { + Err(((c, t), (1, item))) + } + } +} + +/// An iterator adaptor that removes repeated duplicates, while keeping a count +/// of how many repeated elements were present. +/// +/// See [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more +/// information. +pub type DedupWithCount<I> = DedupByWithCount<I, DedupEq>; + +/// Create a new `DedupByWithCount`. +pub fn dedup_by_with_count<I, Pred>(iter: I, dedup_pred: Pred) -> DedupByWithCount<I, Pred> +where + I: Iterator, +{ + DedupByWithCount { last: None, iter, f: DedupPredWithCount2CoalescePred(dedup_pred) } +} + +/// Create a new `DedupWithCount`. +pub fn dedup_with_count<I>(iter: I) -> DedupWithCount<I> +where + I: Iterator, +{ + dedup_by_with_count(iter, DedupEq) +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/map.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/map.rs similarity index 87% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/map.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/map.rs index cf5e5a0..2ca30d6 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/map.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/map.rs
@@ -4,8 +4,8 @@ #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct MapSpecialCase<I, F> { - iter: I, - f: F, + pub(crate) iter: I, + pub(crate) f: F, } pub trait MapSpecialCaseFn<T> { @@ -67,10 +67,6 @@ /// See [`.map_ok()`](crate::Itertools::map_ok) for more information. pub type MapOk<I, F> = MapSpecialCase<I, MapSpecialCaseFnOk<F>>; -/// See [`MapOk`]. -#[deprecated(note = "Use MapOk instead", since = "0.10.0")] -pub type MapResults<I, F> = MapOk<I, F>; - impl<F, T, U, E> MapSpecialCaseFn<Result<T, E>> for MapSpecialCaseFnOk<F> where F: FnMut(T) -> U, @@ -94,10 +90,7 @@ I: Iterator<Item = Result<T, E>>, F: FnMut(T) -> U, { - MapSpecialCase { - iter, - f: MapSpecialCaseFnOk(f), - } + MapSpecialCase { iter, f: MapSpecialCaseFnOk(f) } } /// An iterator adapter to apply `Into` conversion to each element. @@ -112,13 +105,20 @@ } } -#[derive(Clone, Debug)] pub struct MapSpecialCaseFnInto<U>(PhantomData<U>); +impl<U> std::fmt::Debug for MapSpecialCaseFnInto<U> { + debug_fmt_fields!(MapSpecialCaseFnInto, 0); +} + +impl<U> Clone for MapSpecialCaseFnInto<U> { + #[inline] + fn clone(&self) -> Self { + Self(PhantomData) + } +} + /// Create a new [`MapInto`] iterator. pub fn map_into<I, R>(iter: I) -> MapInto<I, R> { - MapSpecialCase { - iter, - f: MapSpecialCaseFnInto(PhantomData), - } + MapSpecialCase { iter, f: MapSpecialCaseFnInto(PhantomData) } }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/mod.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/mod.rs new file mode 100644 index 0000000..5df2db1 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/mod.rs
@@ -0,0 +1,1209 @@ +//! Licensed under the Apache License, Version 2.0 +//! <https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +//! <https://opensource.org/licenses/MIT>, at your +//! option. This file may not be copied, modified, or distributed +//! except according to those terms. + +mod coalesce; +pub(crate) mod map; +mod multi_product; +pub use self::coalesce::*; +pub use self::map::{map_into, map_ok, MapInto, MapOk}; +#[cfg(feature = "use_alloc")] +pub use self::multi_product::*; + +use crate::size_hint::{self, SizeHint}; +use std::fmt; +use std::iter::{Enumerate, FromIterator, Fuse, FusedIterator}; +use std::marker::PhantomData; + +/// An iterator adaptor that alternates elements from two iterators until both +/// run out. +/// +/// This iterator is *fused*. +/// +/// See [`.interleave()`](crate::Itertools::interleave) for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Interleave<I, J> { + i: Fuse<I>, + j: Fuse<J>, + next_coming_from_j: bool, +} + +/// Create an iterator that interleaves elements in `i` and `j`. +/// +/// [`IntoIterator`] enabled version of +/// [`Itertools::interleave`](crate::Itertools::interleave). +pub fn interleave<I, J>( + i: I, + j: J, +) -> Interleave<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter> +where + I: IntoIterator, + J: IntoIterator<Item = I::Item>, +{ + Interleave { i: i.into_iter().fuse(), j: j.into_iter().fuse(), next_coming_from_j: false } +} + +impl<I, J> Iterator for Interleave<I, J> +where + I: Iterator, + J: Iterator<Item = I::Item>, +{ + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.next_coming_from_j = !self.next_coming_from_j; + if self.next_coming_from_j { + match self.i.next() { + None => self.j.next(), + r => r, + } + } else { + match self.j.next() { + None => self.i.next(), + r => r, + } + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + size_hint::add(self.i.size_hint(), self.j.size_hint()) + } + + fn fold<B, F>(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + let Self { mut i, mut j, next_coming_from_j } = self; + if next_coming_from_j { + match j.next() { + Some(y) => init = f(init, y), + None => return i.fold(init, f), + } + } + let res = i.try_fold(init, |mut acc, x| { + acc = f(acc, x); + match j.next() { + Some(y) => Ok(f(acc, y)), + None => Err(acc), + } + }); + match res { + Ok(acc) => j.fold(acc, f), + Err(acc) => i.fold(acc, f), + } + } +} + +impl<I, J> FusedIterator for Interleave<I, J> +where + I: Iterator, + J: Iterator<Item = I::Item>, +{ +} + +/// An iterator adaptor that alternates elements from the two iterators until +/// one of them runs out. +/// +/// This iterator is *fused*. +/// +/// See [`.interleave_shortest()`](crate::Itertools::interleave_shortest) +/// for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct InterleaveShortest<I, J> +where + I: Iterator, + J: Iterator<Item = I::Item>, +{ + i: I, + j: J, + next_coming_from_j: bool, +} + +/// Create a new `InterleaveShortest` iterator. +pub fn interleave_shortest<I, J>(i: I, j: J) -> InterleaveShortest<I, J> +where + I: Iterator, + J: Iterator<Item = I::Item>, +{ + InterleaveShortest { i, j, next_coming_from_j: false } +} + +impl<I, J> Iterator for InterleaveShortest<I, J> +where + I: Iterator, + J: Iterator<Item = I::Item>, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let e = if self.next_coming_from_j { self.j.next() } else { self.i.next() }; + if e.is_some() { + self.next_coming_from_j = !self.next_coming_from_j; + } + e + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + let (curr_hint, next_hint) = { + let i_hint = self.i.size_hint(); + let j_hint = self.j.size_hint(); + if self.next_coming_from_j { + (j_hint, i_hint) + } else { + (i_hint, j_hint) + } + }; + let (curr_lower, curr_upper) = curr_hint; + let (next_lower, next_upper) = next_hint; + let (combined_lower, combined_upper) = + size_hint::mul_scalar(size_hint::min(curr_hint, next_hint), 2); + let lower = if curr_lower > next_lower { combined_lower + 1 } else { combined_lower }; + let upper = { + let extra_elem = match (curr_upper, next_upper) { + (_, None) => false, + (None, Some(_)) => true, + (Some(curr_max), Some(next_max)) => curr_max > next_max, + }; + if extra_elem { + combined_upper.and_then(|x| x.checked_add(1)) + } else { + combined_upper + } + }; + (lower, upper) + } + + fn fold<B, F>(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + let Self { mut i, mut j, next_coming_from_j } = self; + if next_coming_from_j { + match j.next() { + Some(y) => init = f(init, y), + None => return init, + } + } + let res = i.try_fold(init, |mut acc, x| { + acc = f(acc, x); + match j.next() { + Some(y) => Ok(f(acc, y)), + None => Err(acc), + } + }); + match res { + Ok(val) => val, + Err(val) => val, + } + } +} + +impl<I, J> FusedIterator for InterleaveShortest<I, J> +where + I: FusedIterator, + J: FusedIterator<Item = I::Item>, +{ +} + +#[derive(Clone, Debug)] +/// An iterator adaptor that allows putting back a single +/// item to the front of the iterator. +/// +/// Iterator element type is `I::Item`. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct PutBack<I> +where + I: Iterator, +{ + top: Option<I::Item>, + iter: I, +} + +/// Create an iterator where you can put back a single item +pub fn put_back<I>(iterable: I) -> PutBack<I::IntoIter> +where + I: IntoIterator, +{ + PutBack { top: None, iter: iterable.into_iter() } +} + +impl<I> PutBack<I> +where + I: Iterator, +{ + /// put back value `value` (builder method) + pub fn with_value(mut self, value: I::Item) -> Self { + self.put_back(value); + self + } + + /// Split the `PutBack` into its parts. + #[inline] + pub fn into_parts(self) -> (Option<I::Item>, I) { + let Self { top, iter } = self; + (top, iter) + } + + /// Put back a single value to the front of the iterator. + /// + /// If a value is already in the put back slot, it is returned. + #[inline] + pub fn put_back(&mut self, x: I::Item) -> Option<I::Item> { + self.top.replace(x) + } +} + +impl<I> Iterator for PutBack<I> +where + I: Iterator, +{ + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option<Self::Item> { + match self.top { + None => self.iter.next(), + ref mut some => some.take(), + } + } + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + // Not ExactSizeIterator because size may be larger than usize + size_hint::add_scalar(self.iter.size_hint(), self.top.is_some() as usize) + } + + fn count(self) -> usize { + self.iter.count() + (self.top.is_some() as usize) + } + + fn last(self) -> Option<Self::Item> { + self.iter.last().or(self.top) + } + + fn nth(&mut self, n: usize) -> Option<Self::Item> { + match self.top { + None => self.iter.nth(n), + ref mut some => { + if n == 0 { + some.take() + } else { + *some = None; + self.iter.nth(n - 1) + } + } + } + } + + fn all<G>(&mut self, mut f: G) -> bool + where + G: FnMut(Self::Item) -> bool, + { + if let Some(elt) = self.top.take() { + if !f(elt) { + return false; + } + } + self.iter.all(f) + } + + fn fold<Acc, G>(mut self, init: Acc, mut f: G) -> Acc + where + G: FnMut(Acc, Self::Item) -> Acc, + { + let mut accum = init; + if let Some(elt) = self.top.take() { + accum = f(accum, elt); + } + self.iter.fold(accum, f) + } +} + +#[derive(Debug, Clone)] +/// An iterator adaptor that iterates over the cartesian product of +/// the element sets of two iterators `I` and `J`. +/// +/// Iterator element type is `(I::Item, J::Item)`. +/// +/// See [`.cartesian_product()`](crate::Itertools::cartesian_product) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Product<I, J> +where + I: Iterator, +{ + a: I, + /// `a_cur` is `None` while no item have been taken out of `a` (at + /// definition). Then `a_cur` will be `Some(Some(item))` until `a` is + /// exhausted, in which case `a_cur` will be `Some(None)`. + a_cur: Option<Option<I::Item>>, + b: J, + b_orig: J, +} + +/// Create a new cartesian product iterator +/// +/// Iterator element type is `(I::Item, J::Item)`. +pub fn cartesian_product<I, J>(i: I, j: J) -> Product<I, J> +where + I: Iterator, + J: Clone + Iterator, + I::Item: Clone, +{ + Product { a_cur: None, a: i, b: j.clone(), b_orig: j } +} + +impl<I, J> Iterator for Product<I, J> +where + I: Iterator, + J: Clone + Iterator, + I::Item: Clone, +{ + type Item = (I::Item, J::Item); + + fn next(&mut self) -> Option<Self::Item> { + let Self { a, a_cur, b, b_orig } = self; + let elt_b = match b.next() { + None => { + *b = b_orig.clone(); + match b.next() { + None => return None, + Some(x) => { + *a_cur = Some(a.next()); + x + } + } + } + Some(x) => x, + }; + a_cur.get_or_insert_with(|| a.next()).as_ref().map(|a| (a.clone(), elt_b)) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + // Not ExactSizeIterator because size may be larger than usize + // Compute a * b_orig + b for both lower and upper bound + let mut sh = size_hint::mul(self.a.size_hint(), self.b_orig.size_hint()); + if matches!(self.a_cur, Some(Some(_))) { + sh = size_hint::add(sh, self.b.size_hint()); + } + sh + } + + fn fold<Acc, G>(self, mut accum: Acc, mut f: G) -> Acc + where + G: FnMut(Acc, Self::Item) -> Acc, + { + // use a split loop to handle the loose a_cur as well as avoiding to + // clone b_orig at the end. + let Self { mut a, a_cur, mut b, b_orig } = self; + if let Some(mut elt_a) = a_cur.unwrap_or_else(|| a.next()) { + loop { + accum = b.fold(accum, |acc, elt| f(acc, (elt_a.clone(), elt))); + + // we can only continue iterating a if we had a first element; + if let Some(next_elt_a) = a.next() { + b = b_orig.clone(); + elt_a = next_elt_a; + } else { + break; + } + } + } + accum + } +} + +impl<I, J> FusedIterator for Product<I, J> +where + I: FusedIterator, + J: Clone + FusedIterator, + I::Item: Clone, +{ +} + +/// A “meta iterator adaptor”. Its closure receives a reference to the iterator +/// and may pick off as many elements as it likes, to produce the next iterator +/// element. +/// +/// Iterator element type is `X` if the return type of `F` is `Option<X>`. +/// +/// See [`.batching()`](crate::Itertools::batching) for more information. +#[derive(Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Batching<I, F> { + f: F, + iter: I, +} + +impl<I, F> fmt::Debug for Batching<I, F> +where + I: fmt::Debug, +{ + debug_fmt_fields!(Batching, iter); +} + +/// Create a new Batching iterator. +pub fn batching<I, F>(iter: I, f: F) -> Batching<I, F> { + Batching { f, iter } +} + +impl<B, F, I> Iterator for Batching<I, F> +where + I: Iterator, + F: FnMut(&mut I) -> Option<B>, +{ + type Item = B; + #[inline] + fn next(&mut self) -> Option<Self::Item> { + (self.f)(&mut self.iter) + } +} + +/// An iterator adaptor that borrows from a `Clone`-able iterator +/// to only pick off elements while the predicate returns `true`. +/// +/// See [`.take_while_ref()`](crate::Itertools::take_while_ref) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct TakeWhileRef<'a, I: 'a, F> { + iter: &'a mut I, + f: F, +} + +impl<I, F> fmt::Debug for TakeWhileRef<'_, I, F> +where + I: Iterator + fmt::Debug, +{ + debug_fmt_fields!(TakeWhileRef, iter); +} + +/// Create a new `TakeWhileRef` from a reference to clonable iterator. +pub fn take_while_ref<I, F>(iter: &mut I, f: F) -> TakeWhileRef<I, F> +where + I: Iterator + Clone, +{ + TakeWhileRef { iter, f } +} + +impl<I, F> Iterator for TakeWhileRef<'_, I, F> +where + I: Iterator + Clone, + F: FnMut(&I::Item) -> bool, +{ + type Item = I::Item; + + fn next(&mut self) -> Option<Self::Item> { + let old = self.iter.clone(); + match self.iter.next() { + None => None, + Some(elt) => { + if (self.f)(&elt) { + Some(elt) + } else { + *self.iter = old; + None + } + } + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (0, self.iter.size_hint().1) + } +} + +/// An iterator adaptor that filters `Option<A>` iterator elements +/// and produces `A`. Stops on the first `None` encountered. +/// +/// See [`.while_some()`](crate::Itertools::while_some) for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct WhileSome<I> { + iter: I, +} + +/// Create a new `WhileSome<I>`. +pub fn while_some<I>(iter: I) -> WhileSome<I> { + WhileSome { iter } +} + +impl<I, A> Iterator for WhileSome<I> +where + I: Iterator<Item = Option<A>>, +{ + type Item = A; + + fn next(&mut self) -> Option<Self::Item> { + match self.iter.next() { + None | Some(None) => None, + Some(elt) => elt, + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (0, self.iter.size_hint().1) + } + + fn fold<B, F>(mut self, acc: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + let res = self.iter.try_fold(acc, |acc, item| match item { + Some(item) => Ok(f(acc, item)), + None => Err(acc), + }); + + match res { + Ok(val) => val, + Err(val) => val, + } + } +} + +/// An iterator to iterate through all combinations in a `Clone`-able iterator +/// that produces tuples of a specific size. +/// +/// See [`.tuple_combinations()`](crate::Itertools::tuple_combinations) for more +/// information. +#[derive(Clone, Debug)] +#[must_use = "this iterator adaptor is not lazy but does nearly nothing unless consumed"] +pub struct TupleCombinations<I, T> +where + I: Iterator, + T: HasCombination<I>, +{ + iter: T::Combination, + _mi: PhantomData<I>, +} + +pub trait HasCombination<I>: Sized { + type Combination: From<I> + Iterator<Item = Self>; +} + +/// Create a new `TupleCombinations` from a clonable iterator. +pub fn tuple_combinations<T, I>(iter: I) -> TupleCombinations<I, T> +where + I: Iterator + Clone, + I::Item: Clone, + T: HasCombination<I>, +{ + TupleCombinations { iter: T::Combination::from(iter), _mi: PhantomData } +} + +impl<I, T> Iterator for TupleCombinations<I, T> +where + I: Iterator, + T: HasCombination<I>, +{ + type Item = T; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next() + } + + fn size_hint(&self) -> SizeHint { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.count() + } + + fn fold<B, F>(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.fold(init, f) + } +} + +impl<I, T> FusedIterator for TupleCombinations<I, T> +where + I: FusedIterator, + T: HasCombination<I>, +{ +} + +#[derive(Clone, Debug)] +pub struct Tuple1Combination<I> { + iter: I, +} + +impl<I> From<I> for Tuple1Combination<I> { + fn from(iter: I) -> Self { + Self { iter } + } +} + +impl<I: Iterator> Iterator for Tuple1Combination<I> { + type Item = (I::Item,); + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|x| (x,)) + } + + fn size_hint(&self) -> SizeHint { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.count() + } + + fn fold<B, F>(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.map(|x| (x,)).fold(init, f) + } +} + +impl<I: Iterator> HasCombination<I> for (I::Item,) { + type Combination = Tuple1Combination<I>; +} + +macro_rules! impl_tuple_combination { + ($C:ident $P:ident ; $($X:ident)*) => ( + #[derive(Clone, Debug)] + pub struct $C<I: Iterator> { + item: Option<I::Item>, + iter: I, + c: $P<I>, + } + + impl<I: Iterator + Clone> From<I> for $C<I> { + fn from(mut iter: I) -> Self { + Self { + item: iter.next(), + iter: iter.clone(), + c: iter.into(), + } + } + } + + impl<I: Iterator + Clone> From<I> for $C<Fuse<I>> { + fn from(iter: I) -> Self { + Self::from(iter.fuse()) + } + } + + impl<I, A> Iterator for $C<I> + where I: Iterator<Item = A> + Clone, + A: Clone, + { + type Item = (A, $(ignore_ident!($X, A)),*); + + fn next(&mut self) -> Option<Self::Item> { + if let Some(($($X,)*)) = self.c.next() { + let z = self.item.clone().unwrap(); + Some((z, $($X),*)) + } else { + self.item = self.iter.next(); + self.item.clone().and_then(|z| { + self.c = self.iter.clone().into(); + self.c.next().map(|($($X,)*)| (z, $($X),*)) + }) + } + } + + fn size_hint(&self) -> SizeHint { + const K: usize = 1 + count_ident!($($X)*); + let (mut n_min, mut n_max) = self.iter.size_hint(); + n_min = checked_binomial(n_min, K).unwrap_or(usize::MAX); + n_max = n_max.and_then(|n| checked_binomial(n, K)); + size_hint::add(self.c.size_hint(), (n_min, n_max)) + } + + fn count(self) -> usize { + const K: usize = 1 + count_ident!($($X)*); + let n = self.iter.count(); + checked_binomial(n, K).unwrap() + self.c.count() + } + + fn fold<B, F>(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + // We outline this closure to prevent it from unnecessarily + // capturing the type parameters `I`, `B`, and `F`. Not doing + // so ended up causing exponentially big types during MIR + // inlining when building itertools with optimizations enabled. + // + // This change causes a small improvement to compile times in + // release mode. + type CurrTuple<A> = (A, $(ignore_ident!($X, A)),*); + type PrevTuple<A> = ($(ignore_ident!($X, A),)*); + fn map_fn<A: Clone>(z: &A) -> impl FnMut(PrevTuple<A>) -> CurrTuple<A> + '_ { + move |($($X,)*)| (z.clone(), $($X),*) + } + let Self { c, item, mut iter } = self; + if let Some(z) = item.as_ref() { + init = c + .map(map_fn::<A>(z)) + .fold(init, &mut f); + } + while let Some(z) = iter.next() { + let c: $P<I> = iter.clone().into(); + init = c + .map(map_fn::<A>(&z)) + .fold(init, &mut f); + } + init + } + } + + impl<I, A> HasCombination<I> for (A, $(ignore_ident!($X, A)),*) + where I: Iterator<Item = A> + Clone, + I::Item: Clone + { + type Combination = $C<Fuse<I>>; + } + ) +} + +// This snippet generates the twelve `impl_tuple_combination!` invocations: +// use core::iter; +// use itertools::Itertools; +// +// for i in 2..=12 { +// println!("impl_tuple_combination!(Tuple{arity}Combination +// Tuple{prev}Combination; {idents});", arity = i, +// prev = i - 1, +// idents = ('a'..'z').take(i - 1).join(" "), +// ); +// } +// It could probably be replaced by a bit more macro cleverness. +impl_tuple_combination!(Tuple2Combination Tuple1Combination; a); +impl_tuple_combination!(Tuple3Combination Tuple2Combination; a b); +impl_tuple_combination!(Tuple4Combination Tuple3Combination; a b c); +impl_tuple_combination!(Tuple5Combination Tuple4Combination; a b c d); +impl_tuple_combination!(Tuple6Combination Tuple5Combination; a b c d e); +impl_tuple_combination!(Tuple7Combination Tuple6Combination; a b c d e f); +impl_tuple_combination!(Tuple8Combination Tuple7Combination; a b c d e f g); +impl_tuple_combination!(Tuple9Combination Tuple8Combination; a b c d e f g h); +impl_tuple_combination!(Tuple10Combination Tuple9Combination; a b c d e f g h i); +impl_tuple_combination!(Tuple11Combination Tuple10Combination; a b c d e f g h i j); +impl_tuple_combination!(Tuple12Combination Tuple11Combination; a b c d e f g h i j k); + +// https://en.wikipedia.org/wiki/Binomial_coefficient#In_programming_languages +pub(crate) fn checked_binomial(mut n: usize, mut k: usize) -> Option<usize> { + if n < k { + return Some(0); + } + // `factorial(n) / factorial(n - k) / factorial(k)` but trying to avoid it + // overflows: + k = (n - k).min(k); // symmetry + let mut c = 1; + for i in 1..=k { + c = (c / i).checked_mul(n)?.checked_add((c % i).checked_mul(n)? / i)?; + n -= 1; + } + Some(c) +} + +#[test] +fn test_checked_binomial() { + // With the first row: [1, 0, 0, ...] and the first column full of 1s, we check + // row by row the recurrence relation of binomials (which is an equivalent + // definition). For n >= 1 and k >= 1 we have: + // binomial(n, k) == binomial(n - 1, k - 1) + binomial(n - 1, k) + const LIMIT: usize = 500; + let mut row = vec![Some(0); LIMIT + 1]; + row[0] = Some(1); + for n in 0..=LIMIT { + for k in 0..=LIMIT { + assert_eq!(row[k], checked_binomial(n, k)); + } + row = std::iter::once(Some(1)) + .chain((1..=LIMIT).map(|k| row[k - 1]?.checked_add(row[k]?))) + .collect(); + } +} + +/// An iterator adapter to filter values within a nested `Result::Ok`. +/// +/// See [`.filter_ok()`](crate::Itertools::filter_ok) for more information. +#[derive(Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct FilterOk<I, F> { + iter: I, + f: F, +} + +impl<I, F> fmt::Debug for FilterOk<I, F> +where + I: fmt::Debug, +{ + debug_fmt_fields!(FilterOk, iter); +} + +/// Create a new `FilterOk` iterator. +pub fn filter_ok<I, F, T, E>(iter: I, f: F) -> FilterOk<I, F> +where + I: Iterator<Item = Result<T, E>>, + F: FnMut(&T) -> bool, +{ + FilterOk { iter, f } +} + +impl<I, F, T, E> Iterator for FilterOk<I, F> +where + I: Iterator<Item = Result<T, E>>, + F: FnMut(&T) -> bool, +{ + type Item = Result<T, E>; + + fn next(&mut self) -> Option<Self::Item> { + let f = &mut self.f; + self.iter.find(|res| match res { + Ok(t) => f(t), + _ => true, + }) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (0, self.iter.size_hint().1) + } + + fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.filter(|v| v.as_ref().map(&mut f).unwrap_or(true)).fold(init, fold_f) + } + + fn collect<C>(self) -> C + where + C: FromIterator<Self::Item>, + { + let mut f = self.f; + self.iter.filter(|v| v.as_ref().map(&mut f).unwrap_or(true)).collect() + } +} + +impl<I, F, T, E> DoubleEndedIterator for FilterOk<I, F> +where + I: DoubleEndedIterator<Item = Result<T, E>>, + F: FnMut(&T) -> bool, +{ + fn next_back(&mut self) -> Option<Self::Item> { + let f = &mut self.f; + self.iter.rfind(|res| match res { + Ok(t) => f(t), + _ => true, + }) + } + + fn rfold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.filter(|v| v.as_ref().map(&mut f).unwrap_or(true)).rfold(init, fold_f) + } +} + +impl<I, F, T, E> FusedIterator for FilterOk<I, F> +where + I: FusedIterator<Item = Result<T, E>>, + F: FnMut(&T) -> bool, +{ +} + +/// An iterator adapter to filter and apply a transformation on values within a +/// nested `Result::Ok`. +/// +/// See [`.filter_map_ok()`](crate::Itertools::filter_map_ok) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Clone)] +pub struct FilterMapOk<I, F> { + iter: I, + f: F, +} + +impl<I, F> fmt::Debug for FilterMapOk<I, F> +where + I: fmt::Debug, +{ + debug_fmt_fields!(FilterMapOk, iter); +} + +fn transpose_result<T, E>(result: Result<Option<T>, E>) -> Option<Result<T, E>> { + match result { + Ok(Some(v)) => Some(Ok(v)), + Ok(None) => None, + Err(e) => Some(Err(e)), + } +} + +/// Create a new `FilterOk` iterator. +pub fn filter_map_ok<I, F, T, U, E>(iter: I, f: F) -> FilterMapOk<I, F> +where + I: Iterator<Item = Result<T, E>>, + F: FnMut(T) -> Option<U>, +{ + FilterMapOk { iter, f } +} + +impl<I, F, T, U, E> Iterator for FilterMapOk<I, F> +where + I: Iterator<Item = Result<T, E>>, + F: FnMut(T) -> Option<U>, +{ + type Item = Result<U, E>; + + fn next(&mut self) -> Option<Self::Item> { + let f = &mut self.f; + self.iter.find_map(|res| match res { + Ok(t) => f(t).map(Ok), + Err(e) => Some(Err(e)), + }) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (0, self.iter.size_hint().1) + } + + fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.filter_map(|v| transpose_result(v.map(&mut f))).fold(init, fold_f) + } + + fn collect<C>(self) -> C + where + C: FromIterator<Self::Item>, + { + let mut f = self.f; + self.iter.filter_map(|v| transpose_result(v.map(&mut f))).collect() + } +} + +impl<I, F, T, U, E> DoubleEndedIterator for FilterMapOk<I, F> +where + I: DoubleEndedIterator<Item = Result<T, E>>, + F: FnMut(T) -> Option<U>, +{ + fn next_back(&mut self) -> Option<Self::Item> { + let f = &mut self.f; + self.iter.by_ref().rev().find_map(|res| match res { + Ok(t) => f(t).map(Ok), + Err(e) => Some(Err(e)), + }) + } + + fn rfold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.filter_map(|v| transpose_result(v.map(&mut f))).rfold(init, fold_f) + } +} + +impl<I, F, T, U, E> FusedIterator for FilterMapOk<I, F> +where + I: FusedIterator<Item = Result<T, E>>, + F: FnMut(T) -> Option<U>, +{ +} + +/// An iterator adapter to get the positions of each element that matches a +/// predicate. +/// +/// See [`.positions()`](crate::Itertools::positions) for more information. +#[derive(Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Positions<I, F> { + iter: Enumerate<I>, + f: F, +} + +impl<I, F> fmt::Debug for Positions<I, F> +where + I: fmt::Debug, +{ + debug_fmt_fields!(Positions, iter); +} + +/// Create a new `Positions` iterator. +pub fn positions<I, F>(iter: I, f: F) -> Positions<I, F> +where + I: Iterator, + F: FnMut(I::Item) -> bool, +{ + let iter = iter.enumerate(); + Positions { iter, f } +} + +impl<I, F> Iterator for Positions<I, F> +where + I: Iterator, + F: FnMut(I::Item) -> bool, +{ + type Item = usize; + + fn next(&mut self) -> Option<Self::Item> { + let f = &mut self.f; + self.iter.find_map(|(count, val)| f(val).then_some(count)) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (0, self.iter.size_hint().1) + } + + fn fold<B, G>(self, init: B, mut func: G) -> B + where + G: FnMut(B, Self::Item) -> B, + { + let mut f = self.f; + self.iter.fold(init, |mut acc, (count, val)| { + if f(val) { + acc = func(acc, count); + } + acc + }) + } +} + +impl<I, F> DoubleEndedIterator for Positions<I, F> +where + I: DoubleEndedIterator + ExactSizeIterator, + F: FnMut(I::Item) -> bool, +{ + fn next_back(&mut self) -> Option<Self::Item> { + let f = &mut self.f; + self.iter.by_ref().rev().find_map(|(count, val)| f(val).then_some(count)) + } + + fn rfold<B, G>(self, init: B, mut func: G) -> B + where + G: FnMut(B, Self::Item) -> B, + { + let mut f = self.f; + self.iter.rfold(init, |mut acc, (count, val)| { + if f(val) { + acc = func(acc, count); + } + acc + }) + } +} + +impl<I, F> FusedIterator for Positions<I, F> +where + I: FusedIterator, + F: FnMut(I::Item) -> bool, +{ +} + +/// An iterator adapter to apply a mutating function to each element before +/// yielding it. +/// +/// See [`.update()`](crate::Itertools::update) for more information. +#[derive(Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Update<I, F> { + iter: I, + f: F, +} + +impl<I, F> fmt::Debug for Update<I, F> +where + I: fmt::Debug, +{ + debug_fmt_fields!(Update, iter); +} + +/// Create a new `Update` iterator. +pub fn update<I, F>(iter: I, f: F) -> Update<I, F> +where + I: Iterator, + F: FnMut(&mut I::Item), +{ + Update { iter, f } +} + +impl<I, F> Iterator for Update<I, F> +where + I: Iterator, + F: FnMut(&mut I::Item), +{ + type Item = I::Item; + + fn next(&mut self) -> Option<Self::Item> { + if let Some(mut v) = self.iter.next() { + (self.f)(&mut v); + Some(v) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } + + fn fold<Acc, G>(self, init: Acc, mut g: G) -> Acc + where + G: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, mut v| { + f(&mut v); + g(acc, v) + }) + } + + // if possible, re-use inner iterator specializations in collect + fn collect<C>(self) -> C + where + C: FromIterator<Self::Item>, + { + let mut f = self.f; + self.iter + .map(move |mut v| { + f(&mut v); + v + }) + .collect() + } +} + +impl<I, F> ExactSizeIterator for Update<I, F> +where + I: ExactSizeIterator, + F: FnMut(&mut I::Item), +{ +} + +impl<I, F> DoubleEndedIterator for Update<I, F> +where + I: DoubleEndedIterator, + F: FnMut(&mut I::Item), +{ + fn next_back(&mut self) -> Option<Self::Item> { + if let Some(mut v) = self.iter.next_back() { + (self.f)(&mut v); + Some(v) + } else { + None + } + } +} + +impl<I, F> FusedIterator for Update<I, F> +where + I: FusedIterator, + F: FnMut(&mut I::Item), +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/multi_product.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/multi_product.rs new file mode 100644 index 0000000..7d65a33 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/multi_product.rs
@@ -0,0 +1,212 @@ +#![cfg(feature = "use_alloc")] +use Option::{self as State, None as ProductEnded, Some as ProductInProgress}; +use Option::{self as CurrentItems, None as NotYetPopulated, Some as Populated}; + +use alloc::vec::Vec; + +use crate::size_hint; + +#[derive(Clone)] +/// An iterator adaptor that iterates over the cartesian product of +/// multiple iterators of type `I`. +/// +/// An iterator element type is `Vec<I::Item>`. +/// +/// See [`.multi_cartesian_product()`](crate::Itertools::multi_cartesian_product) +/// for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct MultiProduct<I>(State<MultiProductInner<I>>) +where + I: Iterator + Clone, + I::Item: Clone; + +#[derive(Clone)] +/// Internals for `MultiProduct`. +struct MultiProductInner<I> +where + I: Iterator + Clone, + I::Item: Clone, +{ + /// Holds the iterators. + iters: Vec<MultiProductIter<I>>, + /// Not populated at the beginning then it holds the current item of each + /// iterator. + cur: CurrentItems<Vec<I::Item>>, +} + +impl<I> std::fmt::Debug for MultiProduct<I> +where + I: Iterator + Clone + std::fmt::Debug, + I::Item: Clone + std::fmt::Debug, +{ + debug_fmt_fields!(MultiProduct, 0); +} + +impl<I> std::fmt::Debug for MultiProductInner<I> +where + I: Iterator + Clone + std::fmt::Debug, + I::Item: Clone + std::fmt::Debug, +{ + debug_fmt_fields!(MultiProductInner, iters, cur); +} + +/// Create a new cartesian product iterator over an arbitrary number +/// of iterators of the same type. +/// +/// Iterator element is of type `Vec<H::Item::Item>`. +pub fn multi_cartesian_product<H>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter> +where + H: Iterator, + H::Item: IntoIterator, + <H::Item as IntoIterator>::IntoIter: Clone, + <H::Item as IntoIterator>::Item: Clone, +{ + let inner = MultiProductInner { + iters: iters.map(|i| MultiProductIter::new(i.into_iter())).collect(), + cur: NotYetPopulated, + }; + MultiProduct(ProductInProgress(inner)) +} + +#[derive(Clone, Debug)] +/// Holds the state of a single iterator within a `MultiProduct`. +struct MultiProductIter<I> +where + I: Iterator + Clone, + I::Item: Clone, +{ + iter: I, + iter_orig: I, +} + +impl<I> MultiProductIter<I> +where + I: Iterator + Clone, + I::Item: Clone, +{ + fn new(iter: I) -> Self { + Self { iter: iter.clone(), iter_orig: iter } + } +} + +impl<I> Iterator for MultiProduct<I> +where + I: Iterator + Clone, + I::Item: Clone, +{ + type Item = Vec<I::Item>; + + fn next(&mut self) -> Option<Self::Item> { + // This fuses the iterator. + let inner = self.0.as_mut()?; + match &mut inner.cur { + Populated(values) => { + debug_assert!(!inner.iters.is_empty()); + // Find (from the right) a non-finished iterator and + // reset the finished ones encountered. + for (iter, item) in inner.iters.iter_mut().zip(values.iter_mut()).rev() { + if let Some(new) = iter.iter.next() { + *item = new; + return Some(values.clone()); + } else { + iter.iter = iter.iter_orig.clone(); + // `cur` is populated so the untouched `iter_orig` can not be empty. + *item = iter.iter.next().unwrap(); + } + } + self.0 = ProductEnded; + None + } + // Only the first time. + NotYetPopulated => { + let next: Option<Vec<_>> = inner.iters.iter_mut().map(|i| i.iter.next()).collect(); + if next.is_none() || inner.iters.is_empty() { + // This cartesian product had at most one item to generate and now ends. + self.0 = ProductEnded; + } else { + inner.cur.clone_from(&next); + } + next + } + } + } + + fn count(self) -> usize { + match self.0 { + ProductEnded => 0, + // The iterator is fresh so the count is the product of the length of each iterator: + // - If one of them is empty, stop counting. + // - Less `count()` calls than the general case. + ProductInProgress(MultiProductInner { iters, cur: NotYetPopulated }) => iters + .into_iter() + .map(|iter| iter.iter_orig.count()) + .try_fold(1, |product, count| if count == 0 { None } else { Some(product * count) }) + .unwrap_or_default(), + // The general case. + ProductInProgress(MultiProductInner { iters, cur: Populated(_) }) => { + iters.into_iter().fold(0, |mut acc, iter| { + if acc != 0 { + acc *= iter.iter_orig.count(); + } + acc + iter.iter.count() + }) + } + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + match &self.0 { + ProductEnded => (0, Some(0)), + ProductInProgress(MultiProductInner { iters, cur: NotYetPopulated }) => iters + .iter() + .map(|iter| iter.iter_orig.size_hint()) + .fold((1, Some(1)), size_hint::mul), + ProductInProgress(MultiProductInner { iters, cur: Populated(_) }) => { + if let [first, tail @ ..] = &iters[..] { + tail.iter().fold(first.iter.size_hint(), |mut sh, iter| { + sh = size_hint::mul(sh, iter.iter_orig.size_hint()); + size_hint::add(sh, iter.iter.size_hint()) + }) + } else { + // Since it is populated, this cartesian product has started so `iters` is not + // empty. + unreachable!() + } + } + } + } + + fn last(self) -> Option<Self::Item> { + let MultiProductInner { iters, cur } = self.0?; + // Collect the last item of each iterator of the product. + if let Populated(values) = cur { + let mut count = iters.len(); + let last = iters + .into_iter() + .zip(values) + .map(|(i, value)| { + i.iter.last().unwrap_or_else(|| { + // The iterator is empty, use its current `value`. + count -= 1; + value + }) + }) + .collect(); + if count == 0 { + // `values` was the last item. + None + } else { + Some(last) + } + } else { + iters.into_iter().map(|i| i.iter.last()).collect() + } + } +} + +impl<I> std::iter::FusedIterator for MultiProduct<I> +where + I: Iterator + Clone, + I::Item: Clone, +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations.rs new file mode 100644 index 0000000..8cda4a5 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations.rs
@@ -0,0 +1,305 @@ +use core::array; +use core::borrow::BorrowMut; +use std::fmt; +use std::iter::FusedIterator; + +use super::lazy_buffer::LazyBuffer; +use alloc::vec::Vec; + +use crate::adaptors::checked_binomial; + +/// Iterator for `Vec` valued combinations returned by +/// [`.combinations()`](crate::Itertools::combinations) +pub type Combinations<I> = CombinationsGeneric<I, Vec<usize>>; +/// Iterator for const generic combinations returned by +/// [`.array_combinations()`](crate::Itertools::array_combinations) +pub type ArrayCombinations<I, const K: usize> = CombinationsGeneric<I, [usize; K]>; + +/// Create a new `Combinations` from a clonable iterator. +pub fn combinations<I: Iterator>(iter: I, k: usize) -> Combinations<I> +where + I::Item: Clone, +{ + Combinations::new(iter, (0..k).collect()) +} + +/// Create a new `ArrayCombinations` from a clonable iterator. +pub fn array_combinations<I: Iterator, const K: usize>(iter: I) -> ArrayCombinations<I, K> +where + I::Item: Clone, +{ + ArrayCombinations::new(iter, array::from_fn(|i| i)) +} + +/// An iterator to iterate through all the `k`-length combinations in an +/// iterator. +/// +/// See [`.combinations()`](crate::Itertools::combinations) and +/// [`.array_combinations()`](crate::Itertools::array_combinations) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct CombinationsGeneric<I: Iterator, Idx> { + indices: Idx, + pool: LazyBuffer<I>, + first: bool, +} + +/// A type holding indices of elements in a pool or buffer of items from an +/// inner iterator and used to pick out different combinations in a generic way. +pub trait PoolIndex<T>: BorrowMut<[usize]> { + type Item; + + fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> Self::Item + where + T: Clone; + + fn len(&self) -> usize { + self.borrow().len() + } +} + +impl<T> PoolIndex<T> for Vec<usize> { + type Item = Vec<T>; + + fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> Vec<T> + where + T: Clone, + { + pool.get_at(self) + } +} + +impl<T, const K: usize> PoolIndex<T> for [usize; K] { + type Item = [T; K]; + + fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> [T; K] + where + T: Clone, + { + pool.get_array(*self) + } +} + +impl<I, Idx> Clone for CombinationsGeneric<I, Idx> +where + I: Iterator + Clone, + I::Item: Clone, + Idx: Clone, +{ + clone_fields!(indices, pool, first); +} + +impl<I, Idx> fmt::Debug for CombinationsGeneric<I, Idx> +where + I: Iterator + fmt::Debug, + I::Item: fmt::Debug, + Idx: fmt::Debug, +{ + debug_fmt_fields!(Combinations, indices, pool, first); +} + +impl<I: Iterator, Idx: PoolIndex<I::Item>> CombinationsGeneric<I, Idx> { + /// Constructor with arguments the inner iterator and the initial state for + /// the indices. + fn new(iter: I, indices: Idx) -> Self { + Self { indices, pool: LazyBuffer::new(iter), first: true } + } + + /// Returns the length of a combination produced by this iterator. + #[inline] + pub fn k(&self) -> usize { + self.indices.len() + } + + /// Returns the (current) length of the pool from which combination elements + /// are selected. This value can change between invocations of + /// [`next`](Combinations::next). + #[inline] + pub fn n(&self) -> usize { + self.pool.len() + } + + /// Returns a reference to the source pool. + #[inline] + pub(crate) fn src(&self) -> &LazyBuffer<I> { + &self.pool + } + + /// Return the length of the inner iterator and the count of remaining + /// combinations. + pub(crate) fn n_and_count(self) -> (usize, usize) { + let Self { indices, pool, first } = self; + let n = pool.count(); + (n, remaining_for(n, first, indices.borrow()).unwrap()) + } + + /// Initialises the iterator by filling a buffer with elements from the + /// iterator. Returns true if there are no combinations, false otherwise. + fn init(&mut self) -> bool { + self.pool.prefill(self.k()); + let done = self.k() > self.n(); + if !done { + self.first = false; + } + + done + } + + /// Increments indices representing the combination to advance to the next + /// (in lexicographic order by increasing sequence) combination. For example + /// if we have n=4 & k=2 then `[0, 1] -> [0, 2] -> [0, 3] -> [1, 2] -> ...` + /// + /// Returns true if we've run out of combinations, false otherwise. + fn increment_indices(&mut self) -> bool { + // Borrow once instead of noise each time it's indexed + let indices = self.indices.borrow_mut(); + + if indices.is_empty() { + return true; // Done + } + // Scan from the end, looking for an index to increment + let mut i: usize = indices.len() - 1; + + // Check if we need to consume more from the iterator + if indices[i] == self.pool.len() - 1 { + self.pool.get_next(); // may change pool size + } + + while indices[i] == i + self.pool.len() - indices.len() { + if i > 0 { + i -= 1; + } else { + // Reached the last combination + return true; + } + } + + // Increment index, and reset the ones to its right + indices[i] += 1; + for j in i + 1..indices.len() { + indices[j] = indices[j - 1] + 1; + } + // If we've made it this far, we haven't run out of combos + false + } + + /// Returns the n-th item or the number of successful steps. + pub(crate) fn try_nth(&mut self, n: usize) -> Result<<Self as Iterator>::Item, usize> + where + I: Iterator, + I::Item: Clone, + { + let done = if self.first { self.init() } else { self.increment_indices() }; + if done { + return Err(0); + } + for i in 0..n { + if self.increment_indices() { + return Err(i + 1); + } + } + Ok(self.indices.extract_item(&self.pool)) + } +} + +impl<I, Idx> Iterator for CombinationsGeneric<I, Idx> +where + I: Iterator, + I::Item: Clone, + Idx: PoolIndex<I::Item>, +{ + type Item = Idx::Item; + fn next(&mut self) -> Option<Self::Item> { + let done = if self.first { self.init() } else { self.increment_indices() }; + + if done { + return None; + } + + Some(self.indices.extract_item(&self.pool)) + } + + fn nth(&mut self, n: usize) -> Option<Self::Item> { + self.try_nth(n).ok() + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let (mut low, mut upp) = self.pool.size_hint(); + low = remaining_for(low, self.first, self.indices.borrow()).unwrap_or(usize::MAX); + upp = upp.and_then(|upp| remaining_for(upp, self.first, self.indices.borrow())); + (low, upp) + } + + #[inline] + fn count(self) -> usize { + self.n_and_count().1 + } +} + +impl<I, Idx> FusedIterator for CombinationsGeneric<I, Idx> +where + I: Iterator, + I::Item: Clone, + Idx: PoolIndex<I::Item>, +{ +} + +impl<I: Iterator> Combinations<I> { + /// Resets this `Combinations` back to an initial state for combinations of + /// length `k` over the same pool data source. If `k` is larger than the + /// current length of the data pool an attempt is made to prefill the + /// pool so that it holds `k` elements. + pub(crate) fn reset(&mut self, k: usize) { + self.first = true; + + if k < self.indices.len() { + self.indices.truncate(k); + for i in 0..k { + self.indices[i] = i; + } + } else { + for i in 0..self.indices.len() { + self.indices[i] = i; + } + self.indices.extend(self.indices.len()..k); + self.pool.prefill(k); + } + } +} + +/// For a given size `n`, return the count of remaining combinations or None if +/// it would overflow. +fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> { + let k = indices.len(); + if n < k { + Some(0) + } else if first { + checked_binomial(n, k) + } else { + // https://en.wikipedia.org/wiki/Combinatorial_number_system + // http://www.site.uottawa.ca/~lucia/courses/5165-09/GenCombObj.pdf + + // The combinations generated after the current one can be counted by counting + // as follows: + // - The subsequent combinations that differ in indices[0]: If subsequent + // combinations differ in indices[0], then their value for indices[0] must be + // at least 1 greater than the current indices[0]. As indices is strictly + // monotonically sorted, this means we can effectively choose k values from (n + // - 1 - indices[0]), leading to binomial(n - 1 - indices[0], k) + // possibilities. + // - The subsequent combinations with same indices[0], but differing indices[1]: + // Here we can choose k - 1 values from (n - 1 - indices[1]) values, leading + // to binomial(n - 1 - indices[1], k - 1) possibilities. + // - (...) + // - The subsequent combinations with same indices[0..=i], but differing + // indices[i]: Here we can choose k - i values from (n - 1 - indices[i]) + // values: binomial(n - 1 - indices[i], k - i). Since subsequent combinations + // can in any index, we must sum up the aforementioned binomial coefficients. + + // Below, `n0` resembles indices[i]. + indices + .iter() + .enumerate() + .try_fold(0usize, |sum, (i, n0)| sum.checked_add(checked_binomial(n - 1 - *n0, k - i)?)) + } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations_with_replacement.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations_with_replacement.rs new file mode 100644 index 0000000..21ef322e --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations_with_replacement.rs
@@ -0,0 +1,184 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use std::fmt; +use std::iter::FusedIterator; + +use super::lazy_buffer::LazyBuffer; +use crate::adaptors::checked_binomial; + +/// An iterator to iterate through all the `n`-length combinations in an +/// iterator, with replacement. +/// +/// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement) +/// for more information. +#[derive(Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct CombinationsWithReplacement<I> +where + I: Iterator, + I::Item: Clone, +{ + indices: Box<[usize]>, + pool: LazyBuffer<I>, + first: bool, +} + +impl<I> fmt::Debug for CombinationsWithReplacement<I> +where + I: Iterator + fmt::Debug, + I::Item: fmt::Debug + Clone, +{ + debug_fmt_fields!(CombinationsWithReplacement, indices, pool, first); +} + +/// Create a new `CombinationsWithReplacement` from a clonable iterator. +pub fn combinations_with_replacement<I>(iter: I, k: usize) -> CombinationsWithReplacement<I> +where + I: Iterator, + I::Item: Clone, +{ + let indices = alloc::vec![0; k].into_boxed_slice(); + let pool: LazyBuffer<I> = LazyBuffer::new(iter); + + CombinationsWithReplacement { indices, pool, first: true } +} + +impl<I> CombinationsWithReplacement<I> +where + I: Iterator, + I::Item: Clone, +{ + /// Increments indices representing the combination to advance to the next + /// (in lexicographic order by increasing sequence) combination. + /// + /// Returns true if we've run out of combinations, false otherwise. + fn increment_indices(&mut self) -> bool { + // Check if we need to consume more from the iterator + // This will run while we increment our first index digit + self.pool.get_next(); + + // Work out where we need to update our indices + let mut increment = None; + for (i, indices_int) in self.indices.iter().enumerate().rev() { + if *indices_int < self.pool.len() - 1 { + increment = Some((i, indices_int + 1)); + break; + } + } + match increment { + // If we can update the indices further + Some((increment_from, increment_value)) => { + // We need to update the rightmost non-max value + // and all those to the right + self.indices[increment_from..].fill(increment_value); + false + } + // Otherwise, we're done + None => true, + } + } +} + +impl<I> Iterator for CombinationsWithReplacement<I> +where + I: Iterator, + I::Item: Clone, +{ + type Item = Vec<I::Item>; + + fn next(&mut self) -> Option<Self::Item> { + if self.first { + // In empty edge cases, stop iterating immediately + if !(self.indices.is_empty() || self.pool.get_next()) { + return None; + } + self.first = false; + } else if self.increment_indices() { + return None; + } + Some(self.pool.get_at(&self.indices)) + } + + fn nth(&mut self, n: usize) -> Option<Self::Item> { + if self.first { + // In empty edge cases, stop iterating immediately + if !(self.indices.is_empty() || self.pool.get_next()) { + return None; + } + self.first = false; + } else if self.increment_indices() { + return None; + } + for _ in 0..n { + if self.increment_indices() { + return None; + } + } + Some(self.pool.get_at(&self.indices)) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let (mut low, mut upp) = self.pool.size_hint(); + low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX); + upp = upp.and_then(|upp| remaining_for(upp, self.first, &self.indices)); + (low, upp) + } + + fn count(self) -> usize { + let Self { indices, pool, first } = self; + let n = pool.count(); + remaining_for(n, first, &indices).unwrap() + } +} + +impl<I> FusedIterator for CombinationsWithReplacement<I> +where + I: Iterator, + I::Item: Clone, +{ +} + +/// For a given size `n`, return the count of remaining combinations with +/// replacement or None if it would overflow. +fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> { + // With a "stars and bars" representation, choose k values with replacement from + // n values is like choosing k out of k + n − 1 positions (hence binomial(k + // + n - 1, k) possibilities) to place k stars and therefore n - 1 bars. + // Example (n=4, k=6): ***|*||** represents [0,0,0,1,3,3]. + let count = |n: usize, k: usize| { + let positions = if n == 0 { k.saturating_sub(1) } else { (n - 1).checked_add(k)? }; + checked_binomial(positions, k) + }; + let k = indices.len(); + if first { + count(n, k) + } else { + // The algorithm is similar to the one for combinations *without replacement*, + // except we choose values *with replacement* and indices are *non-strictly* + // monotonically sorted. + + // The combinations generated after the current one can be counted by counting + // as follows: + // - The subsequent combinations that differ in indices[0]: If subsequent + // combinations differ in indices[0], then their value for indices[0] must be + // at least 1 greater than the current indices[0]. As indices is monotonically + // sorted, this means we can effectively choose k values with replacement from + // (n - 1 - indices[0]), leading to count(n - 1 - indices[0], k) + // possibilities. + // - The subsequent combinations with same indices[0], but differing indices[1]: + // Here we can choose k - 1 values with replacement from (n - 1 - indices[1]) + // values, leading to count(n - 1 - indices[1], k - 1) possibilities. + // - (...) + // - The subsequent combinations with same indices[0..=i], but differing + // indices[i]: Here we can choose k - i values with replacement from (n - 1 - + // indices[i]) values: count(n - 1 - indices[i], k - i). Since subsequent + // combinations can in any index, we must sum up the aforementioned binomial + // coefficients. + + // Below, `n0` resembles indices[i]. + indices + .iter() + .enumerate() + .try_fold(0usize, |sum, (i, n0)| sum.checked_add(count(n - 1 - *n0, k - i)?)) + } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/concat_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/concat_impl.rs new file mode 100644 index 0000000..0a281a1 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/concat_impl.rs
@@ -0,0 +1,28 @@ +/// Combine all an iterator's elements into one element by using [`Extend`]. +/// +/// [`IntoIterator`]-enabled version of +/// [`Itertools::concat`](crate::Itertools::concat). +/// +/// This combinator will extend the first item with each of the rest of the +/// items of the iterator. If the iterator is empty, the default value of +/// `I::Item` is returned. +/// +/// ```rust +/// use itertools::concat; +/// +/// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]]; +/// assert_eq!(concat(input), vec![1, 2, 3, 4, 5, 6]); +/// ``` +pub fn concat<I>(iterable: I) -> I::Item +where + I: IntoIterator, + I::Item: Extend<<<I as IntoIterator>::Item as IntoIterator>::Item> + IntoIterator + Default, +{ + iterable + .into_iter() + .reduce(|mut a, b| { + a.extend(b); + a + }) + .unwrap_or_default() +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/cons_tuples_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/cons_tuples_impl.rs new file mode 100644 index 0000000..854f47b5 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/cons_tuples_impl.rs
@@ -0,0 +1,36 @@ +use crate::adaptors::map::{MapSpecialCase, MapSpecialCaseFn}; + +macro_rules! impl_cons_iter( + ($_A:ident, $_B:ident, ) => (); // stop + + ($A:ident, $($B:ident,)*) => ( + impl_cons_iter!($($B,)*); + #[allow(non_snake_case)] + impl<$($B),*, X> MapSpecialCaseFn<(($($B,)*), X)> for ConsTuplesFn { + type Out = ($($B,)* X, ); + fn call(&mut self, (($($B,)*), X): (($($B,)*), X)) -> Self::Out { + ($($B,)* X, ) + } + } + ); +); + +impl_cons_iter!(A, B, C, D, E, F, G, H, I, J, K, L,); + +#[derive(Debug, Clone)] +pub struct ConsTuplesFn; + +/// An iterator that maps an iterator of tuples like +/// `((A, B), C)` to an iterator of `(A, B, C)`. +/// +/// Used by the `iproduct!()` macro. +pub type ConsTuples<I> = MapSpecialCase<I, ConsTuplesFn>; + +/// Create an iterator that maps for example iterators of +/// `((A, B), C)` to `(A, B, C)`. +pub fn cons_tuples<I>(iterable: I) -> ConsTuples<I::IntoIter> +where + I: IntoIterator, +{ + ConsTuples { iter: iterable.into_iter(), f: ConsTuplesFn } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/diff.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/diff.rs new file mode 100644 index 0000000..aa62a207 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/diff.rs
@@ -0,0 +1,105 @@ +//! "Diff"ing iterators for caching elements to sequential collections without +//! requiring the new elements' iterator to be `Clone`. +//! +//! [`Diff`] (produced by the [`diff_with`] function) +//! describes the difference between two non-`Clone` iterators `I` and `J` after +//! breaking ASAP from a lock-step comparison. + +use std::fmt; + +use crate::free::put_back; +use crate::structs::PutBack; + +/// A type returned by the [`diff_with`] function. +/// +/// `Diff` represents the way in which the elements yielded by the iterator `I` +/// differ to some iterator `J`. +pub enum Diff<I, J> +where + I: Iterator, + J: Iterator, +{ + /// The index of the first non-matching element along with both iterator's + /// remaining elements starting with the first mis-match. + FirstMismatch(usize, PutBack<I>, PutBack<J>), + /// The total number of elements that were in `J` along with the remaining + /// elements of `I`. + Shorter(usize, PutBack<I>), + /// The total number of elements that were in `I` along with the remaining + /// elements of `J`. + Longer(usize, PutBack<J>), +} + +impl<I, J> fmt::Debug for Diff<I, J> +where + I: Iterator, + J: Iterator, + PutBack<I>: fmt::Debug, + PutBack<J>: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::FirstMismatch(idx, i, j) => { + f.debug_tuple("FirstMismatch").field(idx).field(i).field(j).finish() + } + Self::Shorter(idx, i) => f.debug_tuple("Shorter").field(idx).field(i).finish(), + Self::Longer(idx, j) => f.debug_tuple("Longer").field(idx).field(j).finish(), + } + } +} + +impl<I, J> Clone for Diff<I, J> +where + I: Iterator, + J: Iterator, + PutBack<I>: Clone, + PutBack<J>: Clone, +{ + fn clone(&self) -> Self { + match self { + Self::FirstMismatch(idx, i, j) => Self::FirstMismatch(*idx, i.clone(), j.clone()), + Self::Shorter(idx, i) => Self::Shorter(*idx, i.clone()), + Self::Longer(idx, j) => Self::Longer(*idx, j.clone()), + } + } +} + +/// Compares every element yielded by both `i` and `j` with the given function +/// in lock-step and returns a [`Diff`] which describes how `j` differs from +/// `i`. +/// +/// If the number of elements yielded by `j` is less than the number of elements +/// yielded by `i`, the number of `j` elements yielded will be returned along +/// with `i`'s remaining elements as `Diff::Shorter`. +/// +/// If the two elements of a step differ, the index of those elements along with +/// the remaining elements of both `i` and `j` are returned as +/// `Diff::FirstMismatch`. +/// +/// If `i` becomes exhausted before `j` becomes exhausted, the number of +/// elements in `i` along with the remaining `j` elements will be returned as +/// `Diff::Longer`. +pub fn diff_with<I, J, F>(i: I, j: J, mut is_equal: F) -> Option<Diff<I::IntoIter, J::IntoIter>> +where + I: IntoIterator, + J: IntoIterator, + F: FnMut(&I::Item, &J::Item) -> bool, +{ + let mut i = i.into_iter(); + let mut j = j.into_iter(); + let mut idx = 0; + while let Some(i_elem) = i.next() { + match j.next() { + None => return Some(Diff::Shorter(idx, put_back(i).with_value(i_elem))), + Some(j_elem) => { + if !is_equal(&i_elem, &j_elem) { + let remaining_i = put_back(i).with_value(i_elem); + let remaining_j = put_back(j).with_value(j_elem); + return Some(Diff::FirstMismatch(idx, remaining_i, remaining_j)); + } + } + } + idx += 1; + } + j.next().map(|j_elem| Diff::Longer(idx, put_back(j).with_value(j_elem))) +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/duplicates_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/duplicates_impl.rs similarity index 89% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/duplicates_impl.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/duplicates_impl.rs index 28eda44..eb1347d 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/duplicates_impl.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/duplicates_impl.rs
@@ -2,8 +2,8 @@ mod private { use std::collections::HashMap; - use std::hash::Hash; use std::fmt; + use std::hash::Hash; #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] @@ -22,14 +22,7 @@ impl<I: Iterator, Key: Eq + Hash, F> DuplicatesBy<I, Key, F> { pub(crate) fn new(iter: I, key_method: F) -> Self { - DuplicatesBy { - iter, - meta: Meta { - used: HashMap::new(), - pending: 0, - key_method, - }, - } + Self { iter, meta: Meta { used: HashMap::new(), pending: 0, key_method } } } } @@ -44,8 +37,9 @@ where Key: Eq + Hash, { - /// Takes an item and returns it back to the caller if it's the second time we see it. - /// Otherwise the item is consumed and None is returned + /// Takes an item and returns it back to the caller if it's the second + /// time we see it. Otherwise the item is consumed and None is + /// returned #[inline(always)] fn filter<I>(&mut self, item: I) -> Option<I> where @@ -77,7 +71,7 @@ type Item = I::Item; fn next(&mut self) -> Option<Self::Item> { - let DuplicatesBy { iter, meta } = self; + let Self { iter, meta } = self; iter.find_map(|v| meta.filter(v)) } @@ -109,7 +103,7 @@ F: KeyMethod<Key, I::Item>, { fn next_back(&mut self) -> Option<Self::Item> { - let DuplicatesBy { iter, meta } = self; + let Self { iter, meta } = self; iter.rev().find_map(|v| meta.filter(v)) } } @@ -121,8 +115,9 @@ fn make(&mut self, value: V) -> Self::Container; } - /// Apply the identity function to elements before checking them for equality. - #[derive(Debug)] + /// Apply the identity function to elements before checking them for + /// equality. + #[derive(Debug, Clone)] pub struct ById; impl<V> KeyMethod<V, V> for ById { type Container = JustValue<V>; @@ -132,7 +127,9 @@ } } - /// Apply a user-supplied function to elements before checking them for equality. + /// Apply a user-supplied function to elements before checking them for + /// equality. + #[derive(Clone)] pub struct ByFn<F>(pub(crate) F); impl<F> fmt::Debug for ByFn<F> { debug_fmt_fields!(ByFn,); @@ -148,8 +145,9 @@ } } - // Implementors of this trait can hold onto a key and a value but only give access to one of them - // at a time. This allows the key and the value to be the same value internally + // Implementors of this trait can hold onto a key and a value but only give + // access to one of them at a time. This allows the key and the value to be + // the same value internally pub trait KeyXorValue<K, V> { fn key_ref(&self) -> &K; fn key(self) -> K; @@ -187,7 +185,8 @@ /// An iterator adapter to filter for duplicate elements. /// -/// See [`.duplicates_by()`](crate::Itertools::duplicates_by) for more information. +/// See [`.duplicates_by()`](crate::Itertools::duplicates_by) for more +/// information. pub type DuplicatesBy<I, V, F> = private::DuplicatesBy<I, V, private::ByFn<F>>; /// Create a new `DuplicatesBy` iterator. @@ -213,4 +212,3 @@ { Duplicates::new(iter, private::ById) } -
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/either_or_both.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/either_or_both.rs similarity index 79% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/either_or_both.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/either_or_both.rs index cf65fe7..288bd76 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/either_or_both.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/either_or_both.rs
@@ -6,7 +6,7 @@ /// Value that either holds a single A or B, or both. #[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub enum EitherOrBoth<A, B> { +pub enum EitherOrBoth<A, B = A> { /// Both values are present. Both(A, B), /// Only the left value of type `A` is present. @@ -29,19 +29,13 @@ /// If `Left`, return true. Otherwise, return false. /// Exclusive version of [`has_left`](EitherOrBoth::has_left). pub fn is_left(&self) -> bool { - match *self { - Left(_) => true, - _ => false, - } + matches!(self, Left(_)) } /// If `Right`, return true. Otherwise, return false. /// Exclusive version of [`has_right`](EitherOrBoth::has_right). pub fn is_right(&self) -> bool { - match *self { - Right(_) => true, - _ => false, - } + matches!(self, Right(_)) } /// If `Both`, return true. Otherwise, return false. @@ -49,7 +43,8 @@ self.as_ref().both().is_some() } - /// If `Left`, or `Both`, return `Some` with the left value. Otherwise, return `None`. + /// If `Left`, or `Both`, return `Some` with the left value. Otherwise, + /// return `None`. pub fn left(self) -> Option<A> { match self { Left(left) | Both(left, _) => Some(left), @@ -57,7 +52,8 @@ } } - /// If `Right`, or `Both`, return `Some` with the right value. Otherwise, return `None`. + /// If `Right`, or `Both`, return `Some` with the right value. Otherwise, + /// return `None`. pub fn right(self) -> Option<B> { match self { Right(right) | Both(_, right) => Some(right), @@ -65,7 +61,17 @@ } } - /// If `Left`, return `Some` with the left value. If `Right` or `Both`, return `None`. + /// Return tuple of options corresponding to the left and right value + /// respectively + /// + /// If `Left` return `(Some(..), None)`, if `Right` return + /// `(None,Some(..))`, else return `(Some(..),Some(..))` + pub fn left_and_right(self) -> (Option<A>, Option<B>) { + self.map_any(Some, Some).or_default() + } + + /// If `Left`, return `Some` with the left value. If `Right` or `Both`, + /// return `None`. /// /// # Examples /// @@ -90,7 +96,8 @@ } } - /// If `Right`, return `Some` with the right value. If `Left` or `Both`, return `None`. + /// If `Right`, return `Some` with the right value. If `Left` or `Both`, + /// return `None`. /// /// # Examples /// @@ -115,7 +122,8 @@ } } - /// If `Both`, return `Some` containing the left and right values. Otherwise, return `None`. + /// If `Both`, return `Some` containing the left and right values. + /// Otherwise, return `None`. pub fn both(self) -> Option<(A, B)> { match self { Both(a, b) => Some((a, b)), @@ -123,7 +131,8 @@ } } - /// If `Left` or `Both`, return the left value. Otherwise, convert the right value and return it. + /// If `Left` or `Both`, return the left value. Otherwise, convert the right + /// value and return it. pub fn into_left(self) -> A where B: Into<A>, @@ -134,7 +143,8 @@ } } - /// If `Right` or `Both`, return the right value. Otherwise, convert the left value and return it. + /// If `Right` or `Both`, return the right value. Otherwise, convert the + /// left value and return it. pub fn into_right(self) -> B where A: Into<B>, @@ -154,7 +164,8 @@ } } - /// Converts from `&mut EitherOrBoth<A, B>` to `EitherOrBoth<&mut A, &mut B>`. + /// Converts from `&mut EitherOrBoth<A, B>` to `EitherOrBoth<&mut A, &mut + /// B>`. pub fn as_mut(&mut self) -> EitherOrBoth<&mut A, &mut B> { match *self { Left(ref mut left) => Left(left), @@ -163,7 +174,8 @@ } } - /// Converts from `&EitherOrBoth<A, B>` to `EitherOrBoth<&_, &_>` using the [`Deref`] trait. + /// Converts from `&EitherOrBoth<A, B>` to `EitherOrBoth<&_, &_>` using the + /// [`Deref`] trait. pub fn as_deref(&self) -> EitherOrBoth<&A::Target, &B::Target> where A: Deref, @@ -176,7 +188,8 @@ } } - /// Converts from `&mut EitherOrBoth<A, B>` to `EitherOrBoth<&mut _, &mut _>` using the [`DerefMut`] trait. + /// Converts from `&mut EitherOrBoth<A, B>` to `EitherOrBoth<&mut _, &mut + /// _>` using the [`DerefMut`] trait. pub fn as_deref_mut(&mut self) -> EitherOrBoth<&mut A::Target, &mut B::Target> where A: DerefMut, @@ -198,8 +211,9 @@ } } - /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, b)` variants. If it is - /// present rewrapping the result in `self`'s original variant. + /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, b)` + /// variants. If it is present rewrapping the result in `self`'s + /// original variant. pub fn map_left<F, M>(self, f: F) -> EitherOrBoth<M, B> where F: FnOnce(A) -> M, @@ -211,8 +225,9 @@ } } - /// Apply the function `f` on the value `b` in `Right(b)` or `Both(a, b)` variants. - /// If it is present rewrapping the result in `self`'s original variant. + /// Apply the function `f` on the value `b` in `Right(b)` or `Both(a, b)` + /// variants. If it is present rewrapping the result in `self`'s + /// original variant. pub fn map_right<F, M>(self, f: F) -> EitherOrBoth<A, M> where F: FnOnce(B) -> M, @@ -239,8 +254,8 @@ } } - /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, _)` variants if it is - /// present. + /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, _)` + /// variants if it is present. pub fn left_and_then<F, L>(self, f: F) -> EitherOrBoth<L, B> where F: FnOnce(A) -> EitherOrBoth<L, B>, @@ -263,10 +278,11 @@ } } - /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present. - /// Otherwise, returns the wrapped value for the present element, and the supplied - /// value for the other. The first (`l`) argument is used for a missing `Left` - /// value. The second (`r`) argument is used for a missing `Right` value. + /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if + /// present. Otherwise, returns the wrapped value for the present + /// element, and the supplied value for the other. The first (`l`) + /// argument is used for a missing `Left` value. The second (`r`) + /// argument is used for a missing `Right` value. /// /// Arguments passed to `or` are eagerly evaluated; if you are passing /// the result of a function call, it is recommended to use [`or_else`], @@ -290,25 +306,26 @@ } } - /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present. - /// Otherwise, returns the wrapped value for the present element, and the [`default`](Default::default) - /// for the other. + /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if + /// present. Otherwise, returns the wrapped value for the present + /// element, and the [`default`](Default::default) for the other. pub fn or_default(self) -> (A, B) where A: Default, B: Default, { match self { - EitherOrBoth::Left(l) => (l, B::default()), - EitherOrBoth::Right(r) => (A::default(), r), - EitherOrBoth::Both(l, r) => (l, r), + Left(l) => (l, B::default()), + Right(r) => (A::default(), r), + Both(l, r) => (l, r), } } - /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present. - /// Otherwise, returns the wrapped value for the present element, and computes the - /// missing value with the supplied closure. The first argument (`l`) is used for a - /// missing `Left` value. The second argument (`r`) is used for a missing `Right` value. + /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if + /// present. Otherwise, returns the wrapped value for the present + /// element, and computes the missing value with the supplied closure. + /// The first argument (`l`) is used for a missing `Left` value. The + /// second argument (`r`) is used for a missing `Right` value. /// /// # Examples /// @@ -327,20 +344,21 @@ } } - /// Returns a mutable reference to the left value. If the left value is not present, - /// it is replaced with `val`. + /// Returns a mutable reference to the left value. If the left value is not + /// present, it is replaced with `val`. pub fn left_or_insert(&mut self, val: A) -> &mut A { self.left_or_insert_with(|| val) } - /// Returns a mutable reference to the right value. If the right value is not present, - /// it is replaced with `val`. + /// Returns a mutable reference to the right value. If the right value is + /// not present, it is replaced with `val`. pub fn right_or_insert(&mut self, val: B) -> &mut B { self.right_or_insert_with(|| val) } - /// If the left value is not present, replace it the value computed by the closure `f`. - /// Returns a mutable reference to the now-present left value. + /// If the left value is not present, replace it the value computed by the + /// closure `f`. Returns a mutable reference to the now-present left + /// value. pub fn left_or_insert_with<F>(&mut self, f: F) -> &mut A where F: FnOnce() -> A, @@ -351,8 +369,9 @@ } } - /// If the right value is not present, replace it the value computed by the closure `f`. - /// Returns a mutable reference to the now-present right value. + /// If the right value is not present, replace it the value computed by the + /// closure `f`. Returns a mutable reference to the now-present right + /// value. pub fn right_or_insert_with<F>(&mut self, f: F) -> &mut B where F: FnOnce() -> B, @@ -363,8 +382,8 @@ } } - /// Sets the `left` value of this instance, and returns a mutable reference to it. - /// Does not affect the `right` value. + /// Sets the `left` value of this instance, and returns a mutable reference + /// to it. Does not affect the `right` value. /// /// # Examples /// ``` @@ -389,7 +408,8 @@ // This is like a map in place operation. We move out of the reference, // change the value, and then move back into the reference. unsafe { - // SAFETY: We know this pointer is valid for reading since we got it from a reference. + // SAFETY: We know this pointer is valid for reading since we got it from a + // reference. let right = std::ptr::read(right as *mut _); // SAFETY: Again, we know the pointer is valid since we got it from a reference. std::ptr::write(self as *mut _, Both(val, right)); @@ -406,8 +426,8 @@ } } - /// Sets the `right` value of this instance, and returns a mutable reference to it. - /// Does not affect the `left` value. + /// Sets the `right` value of this instance, and returns a mutable reference + /// to it. Does not affect the `left` value. /// /// # Examples /// ``` @@ -431,7 +451,8 @@ // This is like a map in place operation. We move out of the reference, // change the value, and then move back into the reference. unsafe { - // SAFETY: We know this pointer is valid for reading since we got it from a reference. + // SAFETY: We know this pointer is valid for reading since we got it from a + // reference. let left = std::ptr::read(left as *mut _); // SAFETY: Again, we know the pointer is valid since we got it from a reference. std::ptr::write(self as *mut _, Both(left, val)); @@ -447,8 +468,8 @@ } } - /// Set `self` to `Both(..)`, containing the specified left and right values, - /// and returns a mutable reference to those values. + /// Set `self` to `Both(..)`, containing the specified left and right + /// values, and returns a mutable reference to those values. pub fn insert_both(&mut self, left: A, right: B) -> (&mut A, &mut B) { *self = Both(left, right); if let Both(left, right) = self { @@ -462,15 +483,25 @@ } impl<T> EitherOrBoth<T, T> { - /// Return either value of left, right, or apply a function `f` to both values if both are present. - /// The input function has to return the same type as both Right and Left carry. - /// + /// Return either value of left, right, or apply a function `f` to both + /// values if both are present. The input function has to return the + /// same type as both Right and Left carry. + /// + /// This function can be used to preferrably extract the left resp. right + /// value, but fall back to the other (i.e. right resp. left) if the + /// preferred one is not present. + /// /// # Examples /// ``` /// # use itertools::EitherOrBoth; /// assert_eq!(EitherOrBoth::Both(3, 7).reduce(u32::max), 7); /// assert_eq!(EitherOrBoth::Left(3).reduce(u32::max), 3); /// assert_eq!(EitherOrBoth::Right(7).reduce(u32::max), 7); + /// + /// // Extract the left value if present, fall back to the right otherwise. + /// assert_eq!(EitherOrBoth::Left("left").reduce(|l, _r| l), "left"); + /// assert_eq!(EitherOrBoth::Right("right").reduce(|l, _r| l), "right"); + /// assert_eq!(EitherOrBoth::Both("left", "right").reduce(|l, _r| l), "left"); /// ``` pub fn reduce<F>(self, f: F) -> T where @@ -484,12 +515,21 @@ } } -impl<A, B> Into<Option<Either<A, B>>> for EitherOrBoth<A, B> { - fn into(self) -> Option<Either<A, B>> { - match self { - EitherOrBoth::Left(l) => Some(Either::Left(l)), - EitherOrBoth::Right(r) => Some(Either::Right(r)), - _ => None, +impl<A, B> From<EitherOrBoth<A, B>> for Option<Either<A, B>> { + fn from(value: EitherOrBoth<A, B>) -> Self { + match value { + Left(l) => Some(Either::Left(l)), + Right(r) => Some(Either::Right(r)), + Both(..) => None, + } + } +} + +impl<A, B> From<Either<A, B>> for EitherOrBoth<A, B> { + fn from(either: Either<A, B>) -> Self { + match either { + Either::Left(l) => Left(l), + Either::Right(l) => Right(l), } } }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/exactly_one_err.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/exactly_one_err.rs similarity index 63% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/exactly_one_err.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/exactly_one_err.rs index c54ae77..767d0e7f7 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/exactly_one_err.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/exactly_one_err.rs
@@ -8,14 +8,15 @@ use crate::size_hint; -/// Iterator returned for the error case of `IterTools::exactly_one()` +/// Iterator returned for the error case of `Itertools::exactly_one()` /// This iterator yields exactly the same elements as the input iterator. /// -/// During the execution of `exactly_one` the iterator must be mutated. This wrapper -/// effectively "restores" the state of the input iterator when it's handed back. +/// During the execution of `exactly_one` the iterator must be mutated. This +/// wrapper effectively "restores" the state of the input iterator when it's +/// handed back. /// -/// This is very similar to `PutBackN` except this iterator only supports 0-2 elements and does not -/// use a `Vec`. +/// This is very similar to `PutBackN` except this iterator only supports 0-2 +/// elements and does not use a `Vec`. #[derive(Clone)] pub struct ExactlyOneError<I> where @@ -54,26 +55,37 @@ Some(Either::Left([first, second])) => { self.first_two = Some(Either::Right(second)); Some(first) - }, - Some(Either::Right(second)) => { - Some(second) } - None => { - self.inner.next() - } + Some(Either::Right(second)) => Some(second), + None => self.inner.next(), } } fn size_hint(&self) -> (usize, Option<usize>) { size_hint::add_scalar(self.inner.size_hint(), self.additional_len()) } -} + fn fold<B, F>(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + match self.first_two { + Some(Either::Left([first, second])) => { + init = f(init, first); + init = f(init, second); + } + Some(Either::Right(second)) => init = f(init, second), + None => {} + } + self.inner.fold(init, f) + } +} impl<I> ExactSizeIterator for ExactlyOneError<I> where I: ExactSizeIterator {} -impl<I> Display for ExactlyOneError<I> - where I: Iterator, +impl<I> Display for ExactlyOneError<I> +where + I: Iterator, { fn fmt(&self, f: &mut Formatter) -> FmtResult { let additional = self.additional_len(); @@ -85,26 +97,30 @@ } } -impl<I> Debug for ExactlyOneError<I> - where I: Iterator + Debug, - I::Item: Debug, +impl<I> Debug for ExactlyOneError<I> +where + I: Iterator + Debug, + I::Item: Debug, { fn fmt(&self, f: &mut Formatter) -> FmtResult { + let mut dbg = f.debug_struct("ExactlyOneError"); match &self.first_two { Some(Either::Left([first, second])) => { - write!(f, "ExactlyOneError[First: {:?}, Second: {:?}, RemainingIter: {:?}]", first, second, self.inner) - }, + dbg.field("first", first).field("second", second); + } Some(Either::Right(second)) => { - write!(f, "ExactlyOneError[Second: {:?}, RemainingIter: {:?}]", second, self.inner) + dbg.field("second", second); } - None => { - write!(f, "ExactlyOneError[RemainingIter: {:?}]", self.inner) - } + None => {} } + dbg.field("inner", &self.inner).finish() } } #[cfg(feature = "use_std")] -impl<I> Error for ExactlyOneError<I> where I: Iterator + Debug, I::Item: Debug, {} - - +impl<I> Error for ExactlyOneError<I> +where + I: Iterator + Debug, + I::Item: Debug, +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/extrema_set.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/extrema_set.rs similarity index 92% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/extrema_set.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/extrema_set.rs index ae12836..9e2d5a95 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/extrema_set.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/extrema_set.rs
@@ -1,3 +1,4 @@ +use alloc::{vec, vec::Vec}; use std::cmp::Ordering; /// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`. @@ -42,7 +43,5 @@ F: FnMut(&I::Item) -> K, Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering, { - min_set_impl(it, key_for, |it1, it2, key1, key2| { - compare(it2, it1, key2, key1) - }) + min_set_impl(it, key_for, |it1, it2, key1, key2| compare(it2, it1, key2, key1)) }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/flatten_ok.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/flatten_ok.rs similarity index 76% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/flatten_ok.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/flatten_ok.rs index 21ae1f72..c1e06b5 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/flatten_ok.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/flatten_ok.rs
@@ -9,11 +9,7 @@ I: Iterator<Item = Result<T, E>>, T: IntoIterator, { - FlattenOk { - iter, - inner_front: None, - inner_back: None, - } + FlattenOk { iter, inner_front: None, inner_back: None } } /// An iterator adaptor that flattens `Result::Ok` values and @@ -72,16 +68,37 @@ } } + fn fold<B, F>(self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + // Front + let mut acc = match self.inner_front { + Some(x) => x.fold(init, |a, o| f(a, Ok(o))), + None => init, + }; + + acc = self.iter.fold(acc, |acc, x| match x { + Ok(it) => it.into_iter().fold(acc, |a, o| f(a, Ok(o))), + Err(e) => f(acc, Err(e)), + }); + + // Back + match self.inner_back { + Some(x) => x.fold(acc, |a, o| f(a, Ok(o))), + None => acc, + } + } + fn size_hint(&self) -> (usize, Option<usize>) { let inner_hint = |inner: &Option<T::IntoIter>| { - inner - .as_ref() - .map(Iterator::size_hint) - .unwrap_or((0, Some(0))) + inner.as_ref().map(Iterator::size_hint).unwrap_or((0, Some(0))) }; let inner_front = inner_hint(&self.inner_front); let inner_back = inner_hint(&self.inner_back); - // The outer iterator `Ok` case could be (0, None) as we don't know its size_hint yet. + // The outer iterator `Ok` case could be (0, None) as we don't know its + // size_hint yet. let outer = match self.iter.size_hint() { (0, Some(0)) => (0, Some(0)), _ => (0, None), @@ -130,6 +147,29 @@ } } } + + fn rfold<B, F>(self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + // Back + let mut acc = match self.inner_back { + Some(x) => x.rfold(init, |a, o| f(a, Ok(o))), + None => init, + }; + + acc = self.iter.rfold(acc, |acc, x| match x { + Ok(it) => it.into_iter().rfold(acc, |a, o| f(a, Ok(o))), + Err(e) => f(acc, Err(e)), + }); + + // Front + match self.inner_front { + Some(x) => x.rfold(acc, |a, o| f(a, Ok(o))), + None => acc, + } + } } impl<I, T, E> Clone for FlattenOk<I, T, E> @@ -147,13 +187,7 @@ T: IntoIterator, T::IntoIter: fmt::Debug, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlattenOk") - .field("iter", &self.iter) - .field("inner_front", &self.inner_front) - .field("inner_back", &self.inner_back) - .finish() - } + debug_fmt_fields!(FlattenOk, iter, inner_front, inner_back); } /// Only the iterator being flattened needs to implement [`FusedIterator`].
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/format.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/format.rs similarity index 74% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/format.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/format.rs index c4cb65d..f909ec9 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/format.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/format.rs
@@ -9,7 +9,8 @@ /// See [`.format_with()`](crate::Itertools::format_with) for more information. pub struct FormatWith<'a, I, F> { sep: &'a str, - /// FormatWith uses interior mutability because Display::fmt takes &self. + /// `FormatWith` uses interior mutability because `Display::fmt` takes + /// `&self`. inner: Cell<Option<(I, F)>>, } @@ -22,7 +23,7 @@ /// for more information. pub struct Format<'a, I> { sep: &'a str, - /// Format uses interior mutability because Display::fmt takes &self. + /// `Format` uses interior mutability because `Display::fmt` takes `&self`. inner: Cell<Option<I>>, } @@ -31,23 +32,17 @@ I: Iterator, F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result, { - FormatWith { - sep: separator, - inner: Cell::new(Some((iter, f))), - } + FormatWith { sep: separator, inner: Cell::new(Some((iter, f))) } } pub fn new_format_default<I>(iter: I, separator: &str) -> Format<'_, I> where I: Iterator, { - Format { - sep: separator, - inner: Cell::new(Some(iter)), - } + Format { sep: separator, inner: Cell::new(Some(iter)) } } -impl<'a, I, F> fmt::Display for FormatWith<'a, I, F> +impl<I, F> fmt::Display for FormatWith<'_, I, F> where I: Iterator, F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result, @@ -71,7 +66,17 @@ } } -impl<'a, I> Format<'a, I> +impl<I, F> fmt::Debug for FormatWith<'_, I, F> +where + I: Iterator, + F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl<I> Format<'_, I> where I: Iterator, { @@ -115,7 +120,7 @@ impl_format! {Display Debug UpperExp LowerExp UpperHex LowerHex Octal Binary Pointer} -impl<'a, I, F> Clone for FormatWith<'a, I, F> +impl<I, F> Clone for FormatWith<'_, I, F> where (I, F): Clone, { @@ -124,24 +129,19 @@ into: &'r FormatWith<'a, I, F>, inner: Option<(I, F)>, } - // This ensures we preserve the state of the original `FormatWith` if `Clone` panics - impl<'r, 'a, I, F> Drop for PutBackOnDrop<'r, 'a, I, F> { + // This ensures we preserve the state of the original `FormatWith` if `Clone` + // panics + impl<I, F> Drop for PutBackOnDrop<'_, '_, I, F> { fn drop(&mut self) { self.into.inner.set(self.inner.take()) } } - let pbod = PutBackOnDrop { - inner: self.inner.take(), - into: self, - }; - Self { - inner: Cell::new(pbod.inner.clone()), - sep: self.sep, - } + let pbod = PutBackOnDrop { inner: self.inner.take(), into: self }; + Self { inner: Cell::new(pbod.inner.clone()), sep: self.sep } } } -impl<'a, I> Clone for Format<'a, I> +impl<I> Clone for Format<'_, I> where I: Clone, { @@ -150,19 +150,14 @@ into: &'r Format<'a, I>, inner: Option<I>, } - // This ensures we preserve the state of the original `FormatWith` if `Clone` panics - impl<'r, 'a, I> Drop for PutBackOnDrop<'r, 'a, I> { + // This ensures we preserve the state of the original `FormatWith` if `Clone` + // panics + impl<I> Drop for PutBackOnDrop<'_, '_, I> { fn drop(&mut self) { self.into.inner.set(self.inner.take()) } } - let pbod = PutBackOnDrop { - inner: self.inner.take(), - into: self, - }; - Self { - inner: Cell::new(pbod.inner.clone()), - sep: self.sep, - } + let pbod = PutBackOnDrop { inner: self.inner.take(), into: self }; + Self { inner: Cell::new(pbod.inner.clone()), sep: self.sep } } }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/free.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/free.rs similarity index 72% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/free.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/free.rs index 19e3e28..4c68205 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/free.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/free.rs
@@ -10,30 +10,24 @@ type VecIntoIter<T> = alloc::vec::IntoIter<T>; #[cfg(feature = "use_alloc")] -use alloc::{ - string::String, -}; +use alloc::string::String; -use crate::Itertools; use crate::intersperse::{Intersperse, IntersperseWith}; +use crate::Itertools; -pub use crate::adaptors::{ - interleave, - merge, - put_back, -}; +pub use crate::adaptors::{interleave, put_back}; #[cfg(feature = "use_alloc")] -pub use crate::put_back_n_impl::put_back_n; +pub use crate::kmerge_impl::kmerge; +pub use crate::merge_join::{merge, merge_join_by}; #[cfg(feature = "use_alloc")] pub use crate::multipeek_impl::multipeek; #[cfg(feature = "use_alloc")] pub use crate::peek_nth::peek_nth; #[cfg(feature = "use_alloc")] -pub use crate::kmerge_impl::kmerge; -pub use crate::zip_eq_impl::zip_eq; -pub use crate::merge_join::merge_join_by; +pub use crate::put_back_n_impl::put_back_n; #[cfg(feature = "use_alloc")] pub use crate::rciter_impl::rciter; +pub use crate::zip_eq_impl::zip_eq; /// Iterate `iterable` with a particular value inserted between each element. /// @@ -42,11 +36,12 @@ /// ``` /// use itertools::intersperse; /// -/// itertools::assert_equal(intersperse((0..3), 8), vec![0, 8, 1, 8, 2]); +/// itertools::assert_equal(intersperse(0..3, 8), vec![0, 8, 1, 8, 2]); /// ``` pub fn intersperse<I>(iterable: I, element: I::Item) -> Intersperse<I::IntoIter> - where I: IntoIterator, - <I as IntoIterator>::Item: Clone +where + I: IntoIterator, + <I as IntoIterator>::Item: Clone, { Itertools::intersperse(iterable.into_iter(), element) } @@ -60,12 +55,13 @@ /// use itertools::intersperse_with; /// /// let mut i = 10; -/// itertools::assert_equal(intersperse_with((0..3), || { i -= 1; i }), vec![0, 9, 1, 8, 2]); +/// itertools::assert_equal(intersperse_with(0..3, || { i -= 1; i }), vec![0, 9, 1, 8, 2]); /// assert_eq!(i, 8); /// ``` pub fn intersperse_with<I, F>(iterable: I, element: F) -> IntersperseWith<I::IntoIter, F> - where I: IntoIterator, - F: FnMut() -> I::Item +where + I: IntoIterator, + F: FnMut() -> I::Item, { Itertools::intersperse_with(iterable.into_iter(), element) } @@ -79,10 +75,12 @@ /// /// for (i, elt) in enumerate(&[1, 2, 3]) { /// /* loop body */ +/// # let _ = (i, elt); /// } /// ``` pub fn enumerate<I>(iterable: I) -> iter::Enumerate<I::IntoIter> - where I: IntoIterator +where + I: IntoIterator, { iterable.into_iter().enumerate() } @@ -96,11 +94,13 @@ /// /// for elt in rev(&[1, 2, 3]) { /// /* loop body */ +/// # let _ = elt; /// } /// ``` pub fn rev<I>(iterable: I) -> iter::Rev<I::IntoIter> - where I: IntoIterator, - I::IntoIter: DoubleEndedIterator +where + I: IntoIterator, + I::IntoIter: DoubleEndedIterator, { iterable.into_iter().rev() } @@ -108,7 +108,7 @@ /// Converts the arguments to iterators and zips them. /// /// [`IntoIterator`] enabled version of [`Iterator::zip`]. -/// +/// /// ## Example /// /// ``` @@ -121,23 +121,26 @@ /// } /// assert_eq!(result, vec![(1, 'a'),(2, 'b'),(3, 'c')]); /// ``` -#[deprecated(note="Use [std::iter::zip](https://doc.rust-lang.org/std/iter/fn.zip.html) instead", since="0.10.4")] +#[deprecated( + note = "Use [std::iter::zip](https://doc.rust-lang.org/std/iter/fn.zip.html) instead", + since = "0.10.4" +)] pub fn zip<I, J>(i: I, j: J) -> Zip<I::IntoIter, J::IntoIter> - where I: IntoIterator, - J: IntoIterator +where + I: IntoIterator, + J: IntoIterator, { i.into_iter().zip(j) } - -/// Takes two iterables and creates a new iterator over both in sequence. +/// Takes two iterables and creates a new iterator over both in sequence. /// /// [`IntoIterator`] enabled version of [`Iterator::chain`]. /// /// ## Example /// ``` /// use itertools::chain; -/// +/// /// let mut result:Vec<i32> = Vec::new(); /// /// for element in chain(&[1, 2, 3], &[4]) { @@ -145,14 +148,18 @@ /// } /// assert_eq!(result, vec![1, 2, 3, 4]); /// ``` -pub fn chain<I, J>(i: I, j: J) -> iter::Chain<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter> - where I: IntoIterator, - J: IntoIterator<Item = I::Item> +pub fn chain<I, J>( + i: I, + j: J, +) -> iter::Chain<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter> +where + I: IntoIterator, + J: IntoIterator<Item = I::Item>, { i.into_iter().chain(j) } -/// Create an iterator that clones each element from &T to T +/// Create an iterator that clones each element from `&T` to `T`. /// /// [`IntoIterator`] enabled version of [`Iterator::cloned`]. /// @@ -161,9 +168,10 @@ /// /// assert_eq!(cloned(b"abc").next(), Some(b'a')); /// ``` -pub fn cloned<'a, I, T: 'a>(iterable: I) -> iter::Cloned<I::IntoIter> - where I: IntoIterator<Item=&'a T>, - T: Clone, +pub fn cloned<'a, I, T>(iterable: I) -> iter::Cloned<I::IntoIter> +where + I: IntoIterator<Item = &'a T>, + T: Clone + 'a, { iterable.into_iter().cloned() } @@ -178,8 +186,9 @@ /// assert_eq!(fold(&[1., 2., 3.], 0., |a, &b| f32::max(a, b)), 3.); /// ``` pub fn fold<I, B, F>(iterable: I, init: B, f: F) -> B - where I: IntoIterator, - F: FnMut(B, I::Item) -> B +where + I: IntoIterator, + F: FnMut(B, I::Item) -> B, { iterable.into_iter().fold(init, f) } @@ -194,8 +203,9 @@ /// assert!(all(&[1, 2, 3], |elt| *elt > 0)); /// ``` pub fn all<I, F>(iterable: I, f: F) -> bool - where I: IntoIterator, - F: FnMut(I::Item) -> bool +where + I: IntoIterator, + F: FnMut(I::Item) -> bool, { iterable.into_iter().all(f) } @@ -210,8 +220,9 @@ /// assert!(any(&[0, -1, 2], |elt| *elt > 0)); /// ``` pub fn any<I, F>(iterable: I, f: F) -> bool - where I: IntoIterator, - F: FnMut(I::Item) -> bool +where + I: IntoIterator, + F: FnMut(I::Item) -> bool, { iterable.into_iter().any(f) } @@ -226,8 +237,9 @@ /// assert_eq!(max(0..10), Some(9)); /// ``` pub fn max<I>(iterable: I) -> Option<I::Item> - where I: IntoIterator, - I::Item: Ord +where + I: IntoIterator, + I::Item: Ord, { iterable.into_iter().max() } @@ -242,14 +254,14 @@ /// assert_eq!(min(0..10), Some(0)); /// ``` pub fn min<I>(iterable: I) -> Option<I::Item> - where I: IntoIterator, - I::Item: Ord +where + I: IntoIterator, + I::Item: Ord, { iterable.into_iter().min() } - -/// Combine all iterator elements into one String, separated by `sep`. +/// Combine all iterator elements into one `String`, separated by `sep`. /// /// [`IntoIterator`] enabled version of [`Itertools::join`]. /// @@ -260,8 +272,9 @@ /// ``` #[cfg(feature = "use_alloc")] pub fn join<I>(iterable: I, sep: &str) -> String - where I: IntoIterator, - I::Item: Display +where + I: IntoIterator, + I::Item: Display, { iterable.into_iter().join(sep) } @@ -278,9 +291,29 @@ /// ``` #[cfg(feature = "use_alloc")] pub fn sorted<I>(iterable: I) -> VecIntoIter<I::Item> - where I: IntoIterator, - I::Item: Ord +where + I: IntoIterator, + I::Item: Ord, { iterable.into_iter().sorted() } +/// Sort all iterator elements into a new iterator in ascending order. +/// This sort is unstable (i.e., may reorder equal elements). +/// +/// [`IntoIterator`] enabled version of [`Itertools::sorted_unstable`]. +/// +/// ``` +/// use itertools::sorted_unstable; +/// use itertools::assert_equal; +/// +/// assert_equal(sorted_unstable("rust".chars()), "rstu".chars()); +/// ``` +#[cfg(feature = "use_alloc")] +pub fn sorted_unstable<I>(iterable: I) -> VecIntoIter<I::Item> +where + I: IntoIterator, + I::Item: Ord, +{ + iterable.into_iter().sorted_unstable() +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/group_map.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/group_map.rs similarity index 65% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/group_map.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/group_map.rs index a2d0ebb..3dcee83 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/group_map.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/group_map.rs
@@ -9,8 +9,9 @@ /// See [`.into_group_map()`](crate::Itertools::into_group_map) /// for more information. pub fn into_group_map<I, K, V>(iter: I) -> HashMap<K, Vec<V>> - where I: Iterator<Item=(K, V)>, - K: Hash + Eq, +where + I: Iterator<Item = (K, V)>, + K: Hash + Eq, { let mut lookup = HashMap::new(); @@ -21,12 +22,11 @@ lookup } -pub fn into_group_map_by<I, K, V>(iter: I, f: impl Fn(&V) -> K) -> HashMap<K, Vec<V>> - where - I: Iterator<Item=V>, - K: Hash + Eq, +pub fn into_group_map_by<I, K, V, F>(iter: I, mut f: F) -> HashMap<K, Vec<V>> +where + I: Iterator<Item = V>, + K: Hash + Eq, + F: FnMut(&V) -> K, { - into_group_map( - iter.map(|v| (f(&v), v)) - ) + into_group_map(iter.map(|v| (f(&v), v))) }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/groupbylazy.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/groupbylazy.rs similarity index 79% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/groupbylazy.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/groupbylazy.rs index 80c6f09..bb0087cc 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/groupbylazy.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/groupbylazy.rs
@@ -1,14 +1,15 @@ -use std::cell::{Cell, RefCell}; use alloc::vec::{self, Vec}; +use std::cell::{Cell, RefCell}; -/// A trait to unify `FnMut` for `GroupBy` with the chunk key in `IntoChunks` +/// A trait to unify `FnMut` for `ChunkBy` with the chunk key in `IntoChunks` trait KeyFunction<A> { type Key; fn call_mut(&mut self, arg: A) -> Self::Key; } -impl<A, K, F: ?Sized> KeyFunction<A> for F - where F: FnMut(A) -> K +impl<A, K, F> KeyFunction<A> for F +where + F: FnMut(A) -> K + ?Sized, { type Key = K; #[inline] @@ -17,7 +18,6 @@ } } - /// `ChunkIndex` acts like the grouping key function for `IntoChunks` #[derive(Debug, Clone)] struct ChunkIndex { @@ -29,11 +29,7 @@ impl ChunkIndex { #[inline(always)] fn new(size: usize) -> Self { - ChunkIndex { - size, - index: 0, - key: 0, - } + Self { size, index: 0, key: 0 } } } @@ -52,7 +48,8 @@ #[derive(Clone)] struct GroupInner<K, I, F> - where I: Iterator +where + I: Iterator, { key: F, iter: I, @@ -65,19 +62,21 @@ /// Least index for which we still have elements buffered oldest_buffered_group: usize, /// Group index for `buffer[0]` -- the slots - /// bottom_group..oldest_buffered_group are unused and will be erased when + /// `bottom_group..oldest_buffered_group` are unused and will be erased when /// that range is large enough. bottom_group: usize, /// Buffered groups, from `bottom_group` (index 0) to `top_group`. buffer: Vec<vec::IntoIter<I::Item>>, - /// index of last group iter that was dropped, usize::MAX == none + /// index of last group iter that was dropped, + /// `usize::MAX` initially when no group was dropped dropped_group: usize, } impl<K, I, F> GroupInner<K, I, F> - where I: Iterator, - F: for<'a> KeyFunction<&'a I::Item, Key=K>, - K: PartialEq, +where + I: Iterator, + F: for<'a> KeyFunction<&'a I::Item, Key = K>, + K: PartialEq, { /// `client`: Index of group that requests next element #[inline(always)] @@ -90,9 +89,8 @@ */ if client < self.oldest_buffered_group { None - } else if client < self.top_group || - (client == self.top_group && - self.buffer.len() > self.top_group - self.bottom_group) + } else if client < self.top_group + || (client == self.top_group && self.buffer.len() > self.top_group - self.bottom_group) { self.lookup_buffer(client) } else if self.done { @@ -115,11 +113,14 @@ if elt.is_none() && client == self.oldest_buffered_group { // FIXME: VecDeque is unfortunately not zero allocation when empty, // so we do this job manually. - // `bottom_group..oldest_buffered_group` is unused, and if it's large enough, erase it. + // `bottom_group..oldest_buffered_group` is unused, and if it's large enough, + // erase it. self.oldest_buffered_group += 1; // skip forward further empty queues too - while self.buffer.get(self.oldest_buffered_group - self.bottom_group) - .map_or(false, |buf| buf.len() == 0) + while self + .buffer + .get(self.oldest_buffered_group - self.bottom_group) + .map_or(false, |buf| buf.len() == 0) { self.oldest_buffered_group += 1; } @@ -144,12 +145,14 @@ fn next_element(&mut self) -> Option<I::Item> { debug_assert!(!self.done); match self.iter.next() { - None => { self.done = true; None } + None => { + self.done = true; + None + } otherwise => otherwise, } } - #[inline(never)] fn step_buffering(&mut self, client: usize) -> Option<I::Item> { // requested a later group -- walk through the current group up to @@ -171,11 +174,13 @@ let key = self.key.call_mut(&elt); match self.current_key.take() { None => {} - Some(old_key) => if old_key != key { - self.current_key = Some(key); - first_elt = Some(elt); - break; - }, + Some(old_key) => { + if old_key != key { + self.current_key = Some(key); + first_elt = Some(elt); + break; + } + } } self.current_key = Some(key); if self.top_group != self.dropped_group { @@ -194,7 +199,8 @@ } fn push_next_group(&mut self, group: Vec<I::Item>) { - // When we add a new buffered group, fill up slots between oldest_buffered_group and top_group + // When we add a new buffered group, fill up slots between oldest_buffered_group + // and top_group while self.top_group - self.bottom_group > self.buffer.len() { if self.buffer.is_empty() { self.bottom_group += 1; @@ -220,12 +226,14 @@ let key = self.key.call_mut(&elt); match self.current_key.take() { None => {} - Some(old_key) => if old_key != key { - self.current_key = Some(key); - self.current_elt = Some(elt); - self.top_group += 1; - return None; - }, + Some(old_key) => { + if old_key != key { + self.current_key = Some(key); + self.current_elt = Some(elt); + self.top_group += 1; + return None; + } + } } self.current_key = Some(key); Some(elt) @@ -261,7 +269,8 @@ } impl<K, I, F> GroupInner<K, I, F> - where I: Iterator, +where + I: Iterator, { /// Called when a group is dropped fn drop_group(&mut self, client: usize) { @@ -272,10 +281,14 @@ } } -/// `GroupBy` is the storage for the lazy grouping operation. +#[deprecated(note = "Use `ChunkBy` instead", since = "0.13.0")] +/// See [`ChunkBy`](crate::structs::ChunkBy). +pub type GroupBy<K, I, F> = ChunkBy<K, I, F>; + +/// `ChunkBy` is the storage for the lazy grouping operation. /// /// If the groups are consumed in their original order, or if each -/// group is dropped without keeping it around, then `GroupBy` uses +/// group is dropped without keeping it around, then `ChunkBy` uses /// no allocations. It needs allocations only if several group iterators /// are alive at the same time. /// @@ -284,10 +297,11 @@ /// value. It should be stored in a local variable or temporary and /// iterated. /// -/// See [`.group_by()`](crate::Itertools::group_by) for more information. +/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct GroupBy<K, I, F> - where I: Iterator, +pub struct ChunkBy<K, I, F> +where + I: Iterator, { inner: RefCell<GroupInner<K, I, F>>, // the group iterator's current index. Keep this in the main value @@ -296,11 +310,12 @@ } /// Create a new -pub fn new<K, J, F>(iter: J, f: F) -> GroupBy<K, J::IntoIter, F> - where J: IntoIterator, - F: FnMut(&J::Item) -> K, +pub fn new<K, J, F>(iter: J, f: F) -> ChunkBy<K, J::IntoIter, F> +where + J: IntoIterator, + F: FnMut(&J::Item) -> K, { - GroupBy { + ChunkBy { inner: RefCell::new(GroupInner { key: f, iter: iter.into_iter(), @@ -317,13 +332,15 @@ } } -impl<K, I, F> GroupBy<K, I, F> - where I: Iterator, +impl<K, I, F> ChunkBy<K, I, F> +where + I: Iterator, { /// `client`: Index of group that requests next element fn step(&self, client: usize) -> Option<I::Item> - where F: FnMut(&I::Item) -> K, - K: PartialEq, + where + F: FnMut(&I::Item) -> K, + K: PartialEq, { self.inner.borrow_mut().step(client) } @@ -334,11 +351,12 @@ } } -impl<'a, K, I, F> IntoIterator for &'a GroupBy<K, I, F> - where I: Iterator, - I::Item: 'a, - F: FnMut(&I::Item) -> K, - K: PartialEq +impl<'a, K, I, F> IntoIterator for &'a ChunkBy<K, I, F> +where + I: Iterator, + I::Item: 'a, + F: FnMut(&I::Item) -> K, + K: PartialEq, { type Item = (K, Group<'a, K, I, F>); type IntoIter = Groups<'a, K, I, F>; @@ -348,26 +366,29 @@ } } - /// An iterator that yields the Group iterators. /// /// Iterator element type is `(K, Group)`: /// the group's key `K` and the group's iterator. /// -/// See [`.group_by()`](crate::Itertools::group_by) for more information. +/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Groups<'a, K: 'a, I: 'a, F: 'a> - where I: Iterator, - I::Item: 'a +pub struct Groups<'a, K, I, F> +where + I: Iterator + 'a, + I::Item: 'a, + K: 'a, + F: 'a, { - parent: &'a GroupBy<K, I, F>, + parent: &'a ChunkBy<K, I, F>, } impl<'a, K, I, F> Iterator for Groups<'a, K, I, F> - where I: Iterator, - I::Item: 'a, - F: FnMut(&I::Item) -> K, - K: PartialEq +where + I: Iterator, + I::Item: 'a, + F: FnMut(&I::Item) -> K, + K: PartialEq, { type Item = (K, Group<'a, K, I, F>); @@ -378,11 +399,7 @@ let inner = &mut *self.parent.inner.borrow_mut(); inner.step(index).map(|elt| { let key = inner.group_key(index); - (key, Group { - parent: self.parent, - index, - first: Some(elt), - }) + (key, Group { parent: self.parent, index, first: Some(elt) }) }) } } @@ -390,18 +407,22 @@ /// An iterator for the elements in a single group. /// /// Iterator element type is `I::Item`. -pub struct Group<'a, K: 'a, I: 'a, F: 'a> - where I: Iterator, - I::Item: 'a, +pub struct Group<'a, K, I, F> +where + I: Iterator + 'a, + I::Item: 'a, + K: 'a, + F: 'a, { - parent: &'a GroupBy<K, I, F>, + parent: &'a ChunkBy<K, I, F>, index: usize, first: Option<I::Item>, } impl<'a, K, I, F> Drop for Group<'a, K, I, F> - where I: Iterator, - I::Item: 'a, +where + I: Iterator, + I::Item: 'a, { fn drop(&mut self) { self.parent.drop_group(self.index); @@ -409,10 +430,11 @@ } impl<'a, K, I, F> Iterator for Group<'a, K, I, F> - where I: Iterator, - I::Item: 'a, - F: FnMut(&I::Item) -> K, - K: PartialEq, +where + I: Iterator, + I::Item: 'a, + F: FnMut(&I::Item) -> K, + K: PartialEq, { type Item = I::Item; #[inline] @@ -428,7 +450,8 @@ /// Create a new pub fn new_chunks<J>(iter: J, size: usize) -> IntoChunks<J::IntoIter> - where J: IntoIterator, +where + J: IntoIterator, { IntoChunks { inner: RefCell::new(GroupInner { @@ -447,10 +470,9 @@ } } - /// `ChunkLazy` is the storage for a lazy chunking operation. /// -/// `IntoChunks` behaves just like `GroupBy`: it is iterable, and +/// `IntoChunks` behaves just like `ChunkBy`: it is iterable, and /// it only buffers if several chunk iterators are alive at the same time. /// /// This type implements [`IntoIterator`] (it is **not** an iterator @@ -463,7 +485,8 @@ /// See [`.chunks()`](crate::Itertools::chunks) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct IntoChunks<I> - where I: Iterator, +where + I: Iterator, { inner: RefCell<GroupInner<usize, I, ChunkIndex>>, // the chunk iterator's current index. Keep this in the main value @@ -472,15 +495,16 @@ } impl<I> Clone for IntoChunks<I> - where I: Clone + Iterator, - I::Item: Clone, +where + I: Clone + Iterator, + I::Item: Clone, { clone_fields!(inner, index); } - impl<I> IntoChunks<I> - where I: Iterator, +where + I: Iterator, { /// `client`: Index of chunk that requests next element fn step(&self, client: usize) -> Option<I::Item> { @@ -494,20 +518,18 @@ } impl<'a, I> IntoIterator for &'a IntoChunks<I> - where I: Iterator, - I::Item: 'a, +where + I: Iterator, + I::Item: 'a, { type Item = Chunk<'a, I>; type IntoIter = Chunks<'a, I>; fn into_iter(self) -> Self::IntoIter { - Chunks { - parent: self, - } + Chunks { parent: self } } } - /// An iterator that yields the Chunk iterators. /// /// Iterator element type is `Chunk`. @@ -515,16 +537,18 @@ /// See [`.chunks()`](crate::Itertools::chunks) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Clone)] -pub struct Chunks<'a, I: 'a> - where I: Iterator, - I::Item: 'a, +pub struct Chunks<'a, I> +where + I: Iterator + 'a, + I::Item: 'a, { parent: &'a IntoChunks<I>, } impl<'a, I> Iterator for Chunks<'a, I> - where I: Iterator, - I::Item: 'a, +where + I: Iterator, + I::Item: 'a, { type Item = Chunk<'a, I>; @@ -533,22 +557,17 @@ let index = self.parent.index.get(); self.parent.index.set(index + 1); let inner = &mut *self.parent.inner.borrow_mut(); - inner.step(index).map(|elt| { - Chunk { - parent: self.parent, - index, - first: Some(elt), - } - }) + inner.step(index).map(|elt| Chunk { parent: self.parent, index, first: Some(elt) }) } } /// An iterator for the elements in a single chunk. /// /// Iterator element type is `I::Item`. -pub struct Chunk<'a, I: 'a> - where I: Iterator, - I::Item: 'a, +pub struct Chunk<'a, I> +where + I: Iterator + 'a, + I::Item: 'a, { parent: &'a IntoChunks<I>, index: usize, @@ -556,8 +575,9 @@ } impl<'a, I> Drop for Chunk<'a, I> - where I: Iterator, - I::Item: 'a, +where + I: Iterator, + I::Item: 'a, { fn drop(&mut self) { self.parent.drop_group(self.index); @@ -565,8 +585,9 @@ } impl<'a, I> Iterator for Chunk<'a, I> - where I: Iterator, - I::Item: 'a, +where + I: Iterator, + I::Item: 'a, { type Item = I::Item; #[inline]
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/grouping_map.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/grouping_map.rs similarity index 64% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/grouping_map.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/grouping_map.rs index bb5b582..b23b372 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/grouping_map.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/grouping_map.rs
@@ -1,50 +1,57 @@ -#![cfg(feature = "use_std")] - -use crate::MinMaxResult; -use std::collections::HashMap; +use crate::{ + adaptors::map::{MapSpecialCase, MapSpecialCaseFn}, + MinMaxResult, +}; use std::cmp::Ordering; +use std::collections::HashMap; use std::hash::Hash; use std::iter::Iterator; use std::ops::{Add, Mul}; -/// A wrapper to allow for an easy [`into_grouping_map_by`](crate::Itertools::into_grouping_map_by) -#[derive(Clone, Debug)] -pub struct MapForGrouping<I, F>(I, F); +/// A wrapper to allow for an easy +/// [`into_grouping_map_by`](crate::Itertools::into_grouping_map_by) +pub type MapForGrouping<I, F> = MapSpecialCase<I, GroupingMapFn<F>>; -impl<I, F> MapForGrouping<I, F> { - pub(crate) fn new(iter: I, key_mapper: F) -> Self { - Self(iter, key_mapper) +#[derive(Clone)] +pub struct GroupingMapFn<F>(F); + +impl<F> std::fmt::Debug for GroupingMapFn<F> { + debug_fmt_fields!(GroupingMapFn,); +} + +impl<V, K, F: FnMut(&V) -> K> MapSpecialCaseFn<V> for GroupingMapFn<F> { + type Out = (K, V); + fn call(&mut self, v: V) -> Self::Out { + ((self.0)(&v), v) } } -impl<K, V, I, F> Iterator for MapForGrouping<I, F> - where I: Iterator<Item = V>, - K: Hash + Eq, - F: FnMut(&V) -> K, -{ - type Item = (K, V); - fn next(&mut self) -> Option<Self::Item> { - self.0.next().map(|val| ((self.1)(&val), val)) - } +pub(crate) fn new_map_for_grouping<K, I: Iterator, F: FnMut(&I::Item) -> K>( + iter: I, + key_mapper: F, +) -> MapForGrouping<I, F> { + MapSpecialCase { iter, f: GroupingMapFn(key_mapper) } } /// Creates a new `GroupingMap` from `iter` pub fn new<I, K, V>(iter: I) -> GroupingMap<I> - where I: Iterator<Item = (K, V)>, - K: Hash + Eq, +where + I: Iterator<Item = (K, V)>, + K: Hash + Eq, { GroupingMap { iter } } -/// `GroupingMapBy` is an intermediate struct for efficient group-and-fold operations. -/// +/// `GroupingMapBy` is an intermediate struct for efficient group-and-fold +/// operations. +/// /// See [`GroupingMap`] for more informations. pub type GroupingMapBy<I, F> = GroupingMap<MapForGrouping<I, F>>; -/// `GroupingMap` is an intermediate struct for efficient group-and-fold operations. -/// It groups elements by their key and at the same time fold each group -/// using some aggregating operation. -/// +/// `GroupingMap` is an intermediate struct for efficient group-and-fold +/// operations. It groups elements by their key and at the same time fold each +/// group using some aggregating operation. +/// /// No method on this struct performs temporary allocations. #[derive(Clone, Debug)] #[must_use = "GroupingMap is lazy and do nothing unless consumed"] @@ -53,32 +60,37 @@ } impl<I, K, V> GroupingMap<I> - where I: Iterator<Item = (K, V)>, - K: Hash + Eq, +where + I: Iterator<Item = (K, V)>, + K: Hash + Eq, { /// This is the generic way to perform any operation on a `GroupingMap`. /// It's suggested to use this method only to implement custom operations /// when the already provided ones are not enough. - /// - /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements - /// of each group sequentially, passing the previously accumulated value, a reference to the key - /// and the current element as arguments, and stores the results in an `HashMap`. /// - /// The `operation` function is invoked on each element with the following parameters: - /// - the current value of the accumulator of the group if there is currently one; + /// Groups elements from the `GroupingMap` source by key and applies + /// `operation` to the elements of each group sequentially, passing the + /// previously accumulated value, a reference to the key and the current + /// element as arguments, and stores the results in an `HashMap`. + /// + /// The `operation` function is invoked on each element with the following + /// parameters: + /// - the current value of the accumulator of the group if there is + /// currently one; /// - a reference to the key of the group this element belongs to; /// - the element from the source being aggregated; - /// - /// If `operation` returns `Some(element)` then the accumulator is updated with `element`, - /// otherwise the previous accumulation is discarded. /// - /// Return a `HashMap` associating the key of each group with the result of aggregation of - /// that group's elements. If the aggregation of the last element of a group discards the - /// accumulator then there won't be an entry associated to that group's key. - /// + /// If `operation` returns `Some(element)` then the accumulator is updated + /// with `element`, otherwise the previous accumulation is discarded. + /// + /// Return a `HashMap` associating the key of each group with the result of + /// aggregation of that group's elements. If the aggregation of the last + /// element of a group discards the accumulator then there won't be an + /// entry associated to that group's key. + /// /// ``` /// use itertools::Itertools; - /// + /// /// let data = vec![2, 8, 5, 7, 9, 0, 4, 10]; /// let lookup = data.into_iter() /// .into_grouping_map_by(|&n| n % 4) @@ -89,7 +101,7 @@ /// Some(acc.unwrap_or(0) + val) /// } /// }); - /// + /// /// assert_eq!(lookup[&0], 4); // 0 resets the accumulator so only 4 is summed /// assert_eq!(lookup[&1], 5 + 9); /// assert_eq!(lookup.get(&2), None); // 10 resets the accumulator and nothing is summed afterward @@ -97,7 +109,8 @@ /// assert_eq!(lookup.len(), 3); // The final keys are only 0, 1 and 2 /// ``` pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R> - where FO: FnMut(Option<R>, &K, V) -> Option<R>, + where + FO: FnMut(Option<R>, &K, V) -> Option<R>, { let mut destination_map = HashMap::new(); @@ -111,70 +124,124 @@ destination_map } - /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements - /// of each group sequentially, passing the previously accumulated value, a reference to the key - /// and the current element as arguments, and stores the results in a new map. + /// Groups elements from the `GroupingMap` source by key and applies + /// `operation` to the elements of each group sequentially, passing the + /// previously accumulated value, a reference to the key and the current + /// element as arguments, and stores the results in a new map. /// - /// `init` is the value from which will be cloned the initial value of each accumulator. + /// `init` is called to obtain the initial value of each accumulator. /// - /// `operation` is a function that is invoked on each element with the following parameters: + /// `operation` is a function that is invoked on each element with the + /// following parameters: /// - the current value of the accumulator of the group; /// - a reference to the key of the group this element belongs to; /// - the element from the source being accumulated. /// - /// Return a `HashMap` associating the key of each group with the result of folding that group's elements. - /// + /// Return a `HashMap` associating the key of each group with the result of + /// folding that group's elements. + /// /// ``` /// use itertools::Itertools; - /// + /// + /// #[derive(Debug, Default)] + /// struct Accumulator { + /// acc: usize, + /// } + /// /// let lookup = (1..=7) /// .into_grouping_map_by(|&n| n % 3) - /// .fold(0, |acc, _key, val| acc + val); - /// - /// assert_eq!(lookup[&0], 3 + 6); - /// assert_eq!(lookup[&1], 1 + 4 + 7); - /// assert_eq!(lookup[&2], 2 + 5); + /// .fold_with(|_key, _val| Default::default(), |Accumulator { acc }, _key, val| { + /// let acc = acc + val; + /// Accumulator { acc } + /// }); + /// + /// assert_eq!(lookup[&0].acc, 3 + 6); + /// assert_eq!(lookup[&1].acc, 1 + 4 + 7); + /// assert_eq!(lookup[&2].acc, 2 + 5); /// assert_eq!(lookup.len(), 3); /// ``` - pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R> - where R: Clone, - FO: FnMut(R, &K, V) -> R, + pub fn fold_with<FI, FO, R>(self, mut init: FI, mut operation: FO) -> HashMap<K, R> + where + FI: FnMut(&K, &V) -> R, + FO: FnMut(R, &K, V) -> R, { self.aggregate(|acc, key, val| { - let acc = acc.unwrap_or_else(|| init.clone()); + let acc = acc.unwrap_or_else(|| init(key, &val)); Some(operation(acc, key, val)) }) } - /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements - /// of each group sequentially, passing the previously accumulated value, a reference to the key - /// and the current element as arguments, and stores the results in a new map. + /// Groups elements from the `GroupingMap` source by key and applies + /// `operation` to the elements of each group sequentially, passing the + /// previously accumulated value, a reference to the key and the current + /// element as arguments, and stores the results in a new map. /// - /// This is similar to [`fold`] but the initial value of the accumulator is the first element of the group. + /// `init` is the value from which will be cloned the initial value of each + /// accumulator. /// - /// `operation` is a function that is invoked on each element with the following parameters: + /// `operation` is a function that is invoked on each element with the + /// following parameters: /// - the current value of the accumulator of the group; /// - a reference to the key of the group this element belongs to; /// - the element from the source being accumulated. /// - /// Return a `HashMap` associating the key of each group with the result of folding that group's elements. - /// - /// [`fold`]: GroupingMap::fold - /// + /// Return a `HashMap` associating the key of each group with the result of + /// folding that group's elements. + /// /// ``` /// use itertools::Itertools; - /// + /// /// let lookup = (1..=7) /// .into_grouping_map_by(|&n| n % 3) - /// .fold_first(|acc, _key, val| acc + val); - /// + /// .fold(0, |acc, _key, val| acc + val); + /// /// assert_eq!(lookup[&0], 3 + 6); /// assert_eq!(lookup[&1], 1 + 4 + 7); /// assert_eq!(lookup[&2], 2 + 5); /// assert_eq!(lookup.len(), 3); /// ``` - pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V> - where FO: FnMut(V, &K, V) -> V, + pub fn fold<FO, R>(self, init: R, operation: FO) -> HashMap<K, R> + where + R: Clone, + FO: FnMut(R, &K, V) -> R, + { + self.fold_with(|_, _| init.clone(), operation) + } + + /// Groups elements from the `GroupingMap` source by key and applies + /// `operation` to the elements of each group sequentially, passing the + /// previously accumulated value, a reference to the key and the current + /// element as arguments, and stores the results in a new map. + /// + /// This is similar to [`fold`] but the initial value of the accumulator is + /// the first element of the group. + /// + /// `operation` is a function that is invoked on each element with the + /// following parameters: + /// - the current value of the accumulator of the group; + /// - a reference to the key of the group this element belongs to; + /// - the element from the source being accumulated. + /// + /// Return a `HashMap` associating the key of each group with the result of + /// folding that group's elements. + /// + /// [`fold`]: GroupingMap::fold + /// + /// ``` + /// use itertools::Itertools; + /// + /// let lookup = (1..=7) + /// .into_grouping_map_by(|&n| n % 3) + /// .reduce(|acc, _key, val| acc + val); + /// + /// assert_eq!(lookup[&0], 3 + 6); + /// assert_eq!(lookup[&1], 1 + 4 + 7); + /// assert_eq!(lookup[&2], 2 + 5); + /// assert_eq!(lookup.len(), 3); + /// ``` + pub fn reduce<FO>(self, mut operation: FO) -> HashMap<K, V> + where + FO: FnMut(V, &K, V) -> V, { self.aggregate(|acc, key, val| { Some(match acc { @@ -184,26 +251,38 @@ }) } - /// Groups elements from the `GroupingMap` source by key and collects the elements of each group in - /// an instance of `C`. The iteration order is preserved when inserting elements. - /// - /// Return a `HashMap` associating the key of each group with the collection containing that group's elements. - /// + /// See [`.reduce()`](GroupingMap::reduce). + #[deprecated(note = "Use .reduce() instead", since = "0.13.0")] + pub fn fold_first<FO>(self, operation: FO) -> HashMap<K, V> + where + FO: FnMut(V, &K, V) -> V, + { + self.reduce(operation) + } + + /// Groups elements from the `GroupingMap` source by key and collects the + /// elements of each group in an instance of `C`. The iteration order is + /// preserved when inserting elements. + /// + /// Return a `HashMap` associating the key of each group with the collection + /// containing that group's elements. + /// /// ``` /// use itertools::Itertools; /// use std::collections::HashSet; - /// + /// /// let lookup = vec![0, 1, 2, 3, 4, 5, 6, 2, 3, 6].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .collect::<HashSet<_>>(); - /// + /// /// assert_eq!(lookup[&0], vec![0, 3, 6].into_iter().collect::<HashSet<_>>()); /// assert_eq!(lookup[&1], vec![1, 4].into_iter().collect::<HashSet<_>>()); /// assert_eq!(lookup[&2], vec![2, 5].into_iter().collect::<HashSet<_>>()); /// assert_eq!(lookup.len(), 3); /// ``` pub fn collect<C>(self) -> HashMap<K, C> - where C: Default + Extend<V>, + where + C: Default + Extend<V>, { let mut destination_map = HashMap::new(); @@ -214,220 +293,244 @@ destination_map } - /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group. - /// + /// Groups elements from the `GroupingMap` source by key and finds the + /// maximum of each group. + /// /// If several elements are equally maximum, the last element is picked. - /// - /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements. - /// + /// + /// Returns a `HashMap` associating the key of each group with the maximum + /// of that group's elements. + /// /// ``` /// use itertools::Itertools; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .max(); - /// + /// /// assert_eq!(lookup[&0], 12); /// assert_eq!(lookup[&1], 7); /// assert_eq!(lookup[&2], 8); /// assert_eq!(lookup.len(), 3); /// ``` pub fn max(self) -> HashMap<K, V> - where V: Ord, + where + V: Ord, { self.max_by(|_, v1, v2| V::cmp(v1, v2)) } - /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group - /// with respect to the specified comparison function. - /// + /// Groups elements from the `GroupingMap` source by key and finds the + /// maximum of each group with respect to the specified comparison + /// function. + /// /// If several elements are equally maximum, the last element is picked. - /// - /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements. - /// + /// + /// Returns a `HashMap` associating the key of each group with the maximum + /// of that group's elements. + /// /// ``` /// use itertools::Itertools; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .max_by(|_key, x, y| y.cmp(x)); - /// + /// /// assert_eq!(lookup[&0], 3); /// assert_eq!(lookup[&1], 1); /// assert_eq!(lookup[&2], 5); /// assert_eq!(lookup.len(), 3); /// ``` pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V> - where F: FnMut(&K, &V, &V) -> Ordering, + where + F: FnMut(&K, &V, &V) -> Ordering, { - self.fold_first(|acc, key, val| match compare(key, &acc, &val) { + self.reduce(|acc, key, val| match compare(key, &acc, &val) { Ordering::Less | Ordering::Equal => val, - Ordering::Greater => acc + Ordering::Greater => acc, }) } - /// Groups elements from the `GroupingMap` source by key and finds the element of each group - /// that gives the maximum from the specified function. - /// + /// Groups elements from the `GroupingMap` source by key and finds the + /// element of each group that gives the maximum from the specified + /// function. + /// /// If several elements are equally maximum, the last element is picked. - /// - /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements. - /// + /// + /// Returns a `HashMap` associating the key of each group with the maximum + /// of that group's elements. + /// /// ``` /// use itertools::Itertools; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .max_by_key(|_key, &val| val % 4); - /// + /// /// assert_eq!(lookup[&0], 3); /// assert_eq!(lookup[&1], 7); /// assert_eq!(lookup[&2], 5); /// assert_eq!(lookup.len(), 3); /// ``` pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V> - where F: FnMut(&K, &V) -> CK, - CK: Ord, + where + F: FnMut(&K, &V) -> CK, + CK: Ord, { self.max_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2))) } - /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group. - /// + /// Groups elements from the `GroupingMap` source by key and finds the + /// minimum of each group. + /// /// If several elements are equally minimum, the first element is picked. - /// - /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements. - /// + /// + /// Returns a `HashMap` associating the key of each group with the minimum + /// of that group's elements. + /// /// ``` /// use itertools::Itertools; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .min(); - /// + /// /// assert_eq!(lookup[&0], 3); /// assert_eq!(lookup[&1], 1); /// assert_eq!(lookup[&2], 5); /// assert_eq!(lookup.len(), 3); /// ``` pub fn min(self) -> HashMap<K, V> - where V: Ord, + where + V: Ord, { self.min_by(|_, v1, v2| V::cmp(v1, v2)) } - /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group - /// with respect to the specified comparison function. - /// + /// Groups elements from the `GroupingMap` source by key and finds the + /// minimum of each group with respect to the specified comparison + /// function. + /// /// If several elements are equally minimum, the first element is picked. - /// - /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements. - /// + /// + /// Returns a `HashMap` associating the key of each group with the minimum + /// of that group's elements. + /// /// ``` /// use itertools::Itertools; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .min_by(|_key, x, y| y.cmp(x)); - /// + /// /// assert_eq!(lookup[&0], 12); /// assert_eq!(lookup[&1], 7); /// assert_eq!(lookup[&2], 8); /// assert_eq!(lookup.len(), 3); /// ``` pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V> - where F: FnMut(&K, &V, &V) -> Ordering, + where + F: FnMut(&K, &V, &V) -> Ordering, { - self.fold_first(|acc, key, val| match compare(key, &acc, &val) { + self.reduce(|acc, key, val| match compare(key, &acc, &val) { Ordering::Less | Ordering::Equal => acc, - Ordering::Greater => val + Ordering::Greater => val, }) } - /// Groups elements from the `GroupingMap` source by key and finds the element of each group - /// that gives the minimum from the specified function. - /// + /// Groups elements from the `GroupingMap` source by key and finds the + /// element of each group that gives the minimum from the specified + /// function. + /// /// If several elements are equally minimum, the first element is picked. - /// - /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements. - /// + /// + /// Returns a `HashMap` associating the key of each group with the minimum + /// of that group's elements. + /// /// ``` /// use itertools::Itertools; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .min_by_key(|_key, &val| val % 4); - /// + /// /// assert_eq!(lookup[&0], 12); /// assert_eq!(lookup[&1], 4); /// assert_eq!(lookup[&2], 8); /// assert_eq!(lookup.len(), 3); /// ``` pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V> - where F: FnMut(&K, &V) -> CK, - CK: Ord, + where + F: FnMut(&K, &V) -> CK, + CK: Ord, { self.min_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2))) } - /// Groups elements from the `GroupingMap` source by key and find the maximum and minimum of - /// each group. - /// + /// Groups elements from the `GroupingMap` source by key and find the + /// maximum and minimum of each group. + /// /// If several elements are equally maximum, the last element is picked. /// If several elements are equally minimum, the first element is picked. - /// - /// See [.minmax()](crate::Itertools::minmax) for the non-grouping version. - /// + /// + /// See [`Itertools::minmax`](crate::Itertools::minmax) for the non-grouping + /// version. + /// /// Differences from the non grouping version: /// - It never produces a `MinMaxResult::NoElements` /// - It doesn't have any speedup - /// - /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements. - /// + /// + /// Returns a `HashMap` associating the key of each group with the minimum + /// and maximum of that group's elements. + /// /// ``` /// use itertools::Itertools; /// use itertools::MinMaxResult::{OneElement, MinMax}; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .minmax(); - /// + /// /// assert_eq!(lookup[&0], MinMax(3, 12)); /// assert_eq!(lookup[&1], MinMax(1, 7)); /// assert_eq!(lookup[&2], OneElement(5)); /// assert_eq!(lookup.len(), 3); /// ``` pub fn minmax(self) -> HashMap<K, MinMaxResult<V>> - where V: Ord, + where + V: Ord, { self.minmax_by(|_, v1, v2| V::cmp(v1, v2)) } - /// Groups elements from the `GroupingMap` source by key and find the maximum and minimum of - /// each group with respect to the specified comparison function. - /// + /// Groups elements from the `GroupingMap` source by key and find the + /// maximum and minimum of each group with respect to the specified + /// comparison function. + /// /// If several elements are equally maximum, the last element is picked. /// If several elements are equally minimum, the first element is picked. - /// + /// /// It has the same differences from the non-grouping version as `minmax`. - /// - /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements. - /// + /// + /// Returns a `HashMap` associating the key of each group with the minimum + /// and maximum of that group's elements. + /// /// ``` /// use itertools::Itertools; /// use itertools::MinMaxResult::{OneElement, MinMax}; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .minmax_by(|_key, x, y| y.cmp(x)); - /// + /// /// assert_eq!(lookup[&0], MinMax(12, 3)); /// assert_eq!(lookup[&1], MinMax(7, 1)); /// assert_eq!(lookup[&2], OneElement(5)); /// assert_eq!(lookup.len(), 3); /// ``` pub fn minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>> - where F: FnMut(&K, &V, &V) -> Ordering, + where + F: FnMut(&K, &V, &V) -> Ordering, { self.aggregate(|acc, key, val| { Some(match acc { @@ -453,83 +556,92 @@ }) } - /// Groups elements from the `GroupingMap` source by key and find the elements of each group - /// that gives the minimum and maximum from the specified function. - /// + /// Groups elements from the `GroupingMap` source by key and find the + /// elements of each group that gives the minimum and maximum from the + /// specified function. + /// /// If several elements are equally maximum, the last element is picked. /// If several elements are equally minimum, the first element is picked. - /// + /// /// It has the same differences from the non-grouping version as `minmax`. - /// - /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements. - /// + /// + /// Returns a `HashMap` associating the key of each group with the minimum + /// and maximum of that group's elements. + /// /// ``` /// use itertools::Itertools; /// use itertools::MinMaxResult::{OneElement, MinMax}; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .minmax_by_key(|_key, &val| val % 4); - /// + /// /// assert_eq!(lookup[&0], MinMax(12, 3)); /// assert_eq!(lookup[&1], MinMax(4, 7)); /// assert_eq!(lookup[&2], OneElement(5)); /// assert_eq!(lookup.len(), 3); /// ``` pub fn minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>> - where F: FnMut(&K, &V) -> CK, - CK: Ord, + where + F: FnMut(&K, &V) -> CK, + CK: Ord, { self.minmax_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2))) } - + /// Groups elements from the `GroupingMap` source by key and sums them. - /// - /// This is just a shorthand for `self.fold_first(|acc, _, val| acc + val)`. - /// It is more limited than `Iterator::sum` since it doesn't use the `Sum` trait. - /// - /// Returns a `HashMap` associating the key of each group with the sum of that group's elements. - /// + /// + /// This is just a shorthand for `self.reduce(|acc, _, val| acc + val)`. + /// It is more limited than `Iterator::sum` since it doesn't use the `Sum` + /// trait. + /// + /// Returns a `HashMap` associating the key of each group with the sum of + /// that group's elements. + /// /// ``` /// use itertools::Itertools; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .sum(); - /// + /// /// assert_eq!(lookup[&0], 3 + 9 + 12); /// assert_eq!(lookup[&1], 1 + 4 + 7); /// assert_eq!(lookup[&2], 5 + 8); /// assert_eq!(lookup.len(), 3); /// ``` pub fn sum(self) -> HashMap<K, V> - where V: Add<V, Output = V> + where + V: Add<V, Output = V>, { - self.fold_first(|acc, _, val| acc + val) + self.reduce(|acc, _, val| acc + val) } /// Groups elements from the `GroupingMap` source by key and multiply them. - /// - /// This is just a shorthand for `self.fold_first(|acc, _, val| acc * val)`. - /// It is more limited than `Iterator::product` since it doesn't use the `Product` trait. - /// - /// Returns a `HashMap` associating the key of each group with the product of that group's elements. - /// + /// + /// This is just a shorthand for `self.reduce(|acc, _, val| acc * val)`. + /// It is more limited than `Iterator::product` since it doesn't use the + /// `Product` trait. + /// + /// Returns a `HashMap` associating the key of each group with the product + /// of that group's elements. + /// /// ``` /// use itertools::Itertools; - /// + /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .product(); - /// + /// /// assert_eq!(lookup[&0], 3 * 9 * 12); /// assert_eq!(lookup[&1], 1 * 4 * 7); /// assert_eq!(lookup[&2], 5 * 8); /// assert_eq!(lookup.len(), 3); /// ``` pub fn product(self) -> HashMap<K, V> - where V: Mul<V, Output = V>, + where + V: Mul<V, Output = V>, { - self.fold_first(|acc, _, val| acc * val) + self.reduce(|acc, _, val| acc * val) } }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/impl_macros.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/impl_macros.rs similarity index 87% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/impl_macros.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/impl_macros.rs index a029843..3db5ba0 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/impl_macros.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/impl_macros.rs
@@ -1,4 +1,4 @@ -//! +//! //! Implementation's internal macros macro_rules! debug_fmt_fields { @@ -27,3 +27,8 @@ macro_rules! ignore_ident{ ($id:ident, $($t:tt)*) => {$($t)*}; } + +macro_rules! count_ident { + () => {0}; + ($i0:ident $($i:ident)*) => {1 + count_ident!($($i)*)}; +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/intersperse.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/intersperse.rs new file mode 100644 index 0000000..36277db0 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/intersperse.rs
@@ -0,0 +1,132 @@ +use super::size_hint; +use std::iter::{Fuse, FusedIterator}; + +pub trait IntersperseElement<Item> { + fn generate(&mut self) -> Item; +} + +#[derive(Debug, Clone)] +pub struct IntersperseElementSimple<Item>(Item); + +impl<Item: Clone> IntersperseElement<Item> for IntersperseElementSimple<Item> { + fn generate(&mut self) -> Item { + self.0.clone() + } +} + +/// An iterator adaptor to insert a particular value +/// between each element of the adapted iterator. +/// +/// Iterator element type is `I::Item` +/// +/// This iterator is *fused*. +/// +/// See [`.intersperse()`](crate::Itertools::intersperse) for more information. +pub type Intersperse<I> = IntersperseWith<I, IntersperseElementSimple<<I as Iterator>::Item>>; + +/// Create a new Intersperse iterator +pub fn intersperse<I>(iter: I, elt: I::Item) -> Intersperse<I> +where + I: Iterator, +{ + intersperse_with(iter, IntersperseElementSimple(elt)) +} + +impl<Item, F: FnMut() -> Item> IntersperseElement<Item> for F { + fn generate(&mut self) -> Item { + self() + } +} + +/// An iterator adaptor to insert a particular value created by a function +/// between each element of the adapted iterator. +/// +/// Iterator element type is `I::Item` +/// +/// This iterator is *fused*. +/// +/// See [`.intersperse_with()`](crate::Itertools::intersperse_with) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Clone, Debug)] +pub struct IntersperseWith<I, ElemF> +where + I: Iterator, +{ + element: ElemF, + iter: Fuse<I>, + /// `peek` is None while no item have been taken out of `iter` (at + /// definition). Then `peek` will alternatively be `Some(None)` and + /// `Some(Some(item))`, where `None` indicates it's time to generate + /// from `element` (unless `iter` is empty). + peek: Option<Option<I::Item>>, +} + +/// Create a new `IntersperseWith` iterator +pub fn intersperse_with<I, ElemF>(iter: I, elt: ElemF) -> IntersperseWith<I, ElemF> +where + I: Iterator, +{ + IntersperseWith { peek: None, iter: iter.fuse(), element: elt } +} + +impl<I, ElemF> Iterator for IntersperseWith<I, ElemF> +where + I: Iterator, + ElemF: IntersperseElement<I::Item>, +{ + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let Self { element, iter, peek } = self; + match peek { + Some(item @ Some(_)) => item.take(), + Some(None) => match iter.next() { + new @ Some(_) => { + *peek = Some(new); + Some(element.generate()) + } + None => None, + }, + None => { + *peek = Some(None); + iter.next() + } + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let mut sh = self.iter.size_hint(); + sh = size_hint::add(sh, sh); + match self.peek { + Some(Some(_)) => size_hint::add_scalar(sh, 1), + Some(None) => sh, + None => size_hint::sub_scalar(sh, 1), + } + } + + fn fold<B, F>(self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + let Self { mut element, mut iter, peek } = self; + let mut accum = init; + + if let Some(x) = peek.unwrap_or_else(|| iter.next()) { + accum = f(accum, x); + } + + iter.fold(accum, |accum, x| { + let accum = f(accum, element.generate()); + f(accum, x) + }) + } +} + +impl<I, ElemF> FusedIterator for IntersperseWith<I, ElemF> +where + I: Iterator, + ElemF: IntersperseElement<I::Item>, +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/iter_index.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/iter_index.rs new file mode 100644 index 0000000..aadaa72 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/iter_index.rs
@@ -0,0 +1,116 @@ +use core::iter::{Skip, Take}; +use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; + +#[cfg(doc)] +use crate::Itertools; + +mod private_iter_index { + use core::ops; + + pub trait Sealed {} + + impl Sealed for ops::Range<usize> {} + impl Sealed for ops::RangeInclusive<usize> {} + impl Sealed for ops::RangeTo<usize> {} + impl Sealed for ops::RangeToInclusive<usize> {} + impl Sealed for ops::RangeFrom<usize> {} + impl Sealed for ops::RangeFull {} +} + +/// Used by [`Itertools::get`] to know which iterator +/// to turn different ranges into. +pub trait IteratorIndex<I>: private_iter_index::Sealed +where + I: Iterator, +{ + /// The type returned for this type of index. + type Output: Iterator<Item = I::Item>; + + /// Returns an adapted iterator for the current index. + /// + /// Prefer calling [`Itertools::get`] instead + /// of calling this directly. + fn index(self, from: I) -> Self::Output; +} + +impl<I> IteratorIndex<I> for Range<usize> +where + I: Iterator, +{ + type Output = Skip<Take<I>>; + + fn index(self, iter: I) -> Self::Output { + iter.take(self.end).skip(self.start) + } +} + +impl<I> IteratorIndex<I> for RangeInclusive<usize> +where + I: Iterator, +{ + type Output = Take<Skip<I>>; + + fn index(self, iter: I) -> Self::Output { + // end - start + 1 without overflowing if possible + let length = if *self.end() == usize::MAX { + assert_ne!(*self.start(), 0); + self.end() - self.start() + 1 + } else { + (self.end() + 1).saturating_sub(*self.start()) + }; + iter.skip(*self.start()).take(length) + } +} + +impl<I> IteratorIndex<I> for RangeTo<usize> +where + I: Iterator, +{ + type Output = Take<I>; + + fn index(self, iter: I) -> Self::Output { + iter.take(self.end) + } +} + +impl<I> IteratorIndex<I> for RangeToInclusive<usize> +where + I: Iterator, +{ + type Output = Take<I>; + + fn index(self, iter: I) -> Self::Output { + assert_ne!(self.end, usize::MAX); + iter.take(self.end + 1) + } +} + +impl<I> IteratorIndex<I> for RangeFrom<usize> +where + I: Iterator, +{ + type Output = Skip<I>; + + fn index(self, iter: I) -> Self::Output { + iter.skip(self.start) + } +} + +impl<I> IteratorIndex<I> for RangeFull +where + I: Iterator, +{ + type Output = I; + + fn index(self, iter: I) -> Self::Output { + iter + } +} + +pub fn get<I, R>(iter: I, index: R) -> R::Output +where + I: IntoIterator, + R: IteratorIndex<I::IntoIter>, +{ + index.index(iter.into_iter()) +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/k_smallest.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/k_smallest.rs new file mode 100644 index 0000000..c1ae1986 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/k_smallest.rs
@@ -0,0 +1,144 @@ +use alloc::vec::Vec; +use core::cmp::Ordering; + +/// Consumes a given iterator, returning the minimum elements in **ascending** +/// order. +pub(crate) fn k_smallest_general<I, F>(iter: I, k: usize, mut comparator: F) -> Vec<I::Item> +where + I: Iterator, + F: FnMut(&I::Item, &I::Item) -> Ordering, +{ + /// Sift the element currently at `origin` away from the root until it is + /// properly ordered. + /// + /// This will leave **larger** elements closer to the root of the heap. + fn sift_down<T, F>(heap: &mut [T], is_less_than: &mut F, mut origin: usize) + where + F: FnMut(&T, &T) -> bool, + { + #[inline] + fn children_of(n: usize) -> (usize, usize) { + (2 * n + 1, 2 * n + 2) + } + + while origin < heap.len() { + let (left_idx, right_idx) = children_of(origin); + if left_idx >= heap.len() { + return; + } + + let replacement_idx = + if right_idx < heap.len() && is_less_than(&heap[left_idx], &heap[right_idx]) { + right_idx + } else { + left_idx + }; + + if is_less_than(&heap[origin], &heap[replacement_idx]) { + heap.swap(origin, replacement_idx); + origin = replacement_idx; + } else { + return; + } + } + } + + if k == 0 { + iter.last(); + return Vec::new(); + } + if k == 1 { + return iter.min_by(comparator).into_iter().collect(); + } + let mut iter = iter.fuse(); + let mut storage: Vec<I::Item> = iter.by_ref().take(k).collect(); + + let mut is_less_than = move |a: &_, b: &_| comparator(a, b) == Ordering::Less; + + // Rearrange the storage into a valid heap by reordering from the + // second-bottom-most layer up to the root. Slightly faster than ordering on + // each insert, but only by a factor of lg(k). The resulting heap has the + // **largest** item on top. + for i in (0..=(storage.len() / 2)).rev() { + sift_down(&mut storage, &mut is_less_than, i); + } + + iter.for_each(|val| { + debug_assert_eq!(storage.len(), k); + if is_less_than(&val, &storage[0]) { + // Treating this as an push-and-pop saves having to write a sift-up + // implementation. https://en.wikipedia.org/wiki/Binary_heap#Insert_then_extract + storage[0] = val; + // We retain the smallest items we've seen so far, but ordered largest first so + // we can drop the largest efficiently. + sift_down(&mut storage, &mut is_less_than, 0); + } + }); + + // Ultimately the items need to be in least-first, strict order, but the heap is + // currently largest-first. To achieve this, repeatedly, + // 1) "pop" the largest item off the heap into the tail slot of the underlying + // storage, + // 2) shrink the logical size of the heap by 1, + // 3) restore the heap property over the remaining items. + let mut heap = &mut storage[..]; + while heap.len() > 1 { + let last_idx = heap.len() - 1; + heap.swap(0, last_idx); + // Sifting over a truncated slice means that the sifting will not disturb + // already popped elements. + heap = &mut heap[..last_idx]; + sift_down(heap, &mut is_less_than, 0); + } + + storage +} + +pub(crate) fn k_smallest_relaxed_general<I, F>(iter: I, k: usize, mut comparator: F) -> Vec<I::Item> +where + I: Iterator, + F: FnMut(&I::Item, &I::Item) -> Ordering, +{ + if k == 0 { + iter.last(); + return Vec::new(); + } + + let mut iter = iter.fuse(); + let mut buf = iter.by_ref().take(2 * k).collect::<Vec<_>>(); + + if buf.len() < k { + buf.sort_unstable_by(&mut comparator); + return buf; + } + + buf.select_nth_unstable_by(k - 1, &mut comparator); + buf.truncate(k); + + iter.for_each(|val| { + if comparator(&val, &buf[k - 1]) != Ordering::Less { + return; + } + + assert_ne!(buf.len(), buf.capacity()); + buf.push(val); + + if buf.len() == 2 * k { + buf.select_nth_unstable_by(k - 1, &mut comparator); + buf.truncate(k); + } + }); + + buf.sort_unstable_by(&mut comparator); + buf.truncate(k); + buf +} + +#[inline] +pub(crate) fn key_to_cmp<T, K, F>(mut key: F) -> impl FnMut(&T, &T) -> Ordering +where + F: FnMut(&T) -> K, + K: Ord, +{ + move |a, b| key(a).cmp(&key(b)) +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/kmerge_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/kmerge_impl.rs similarity index 69% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/kmerge_impl.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/kmerge_impl.rs index 509d5fc..c3e6fdc 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/kmerge_impl.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/kmerge_impl.rs
@@ -1,41 +1,39 @@ use crate::size_hint; -use crate::Itertools; use alloc::vec::Vec; +use std::fmt; use std::iter::FusedIterator; use std::mem::replace; -use std::fmt; /// Head element and Tail iterator pair /// -/// `PartialEq`, `Eq`, `PartialOrd` and `Ord` are implemented by comparing sequences based on -/// first items (which are guaranteed to exist). +/// `PartialEq`, `Eq`, `PartialOrd` and `Ord` are implemented by comparing +/// sequences based on first items (which are guaranteed to exist). /// -/// The meanings of `PartialOrd` and `Ord` are reversed so as to turn the heap used in -/// `KMerge` into a min-heap. +/// The meanings of `PartialOrd` and `Ord` are reversed so as to turn the heap +/// used in `KMerge` into a min-heap. #[derive(Debug)] struct HeadTail<I> - where I: Iterator +where + I: Iterator, { head: I::Item, tail: I, } impl<I> HeadTail<I> - where I: Iterator +where + I: Iterator, { - /// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the `Iterator` is empty. - fn new(mut it: I) -> Option<HeadTail<I>> { + /// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the + /// `Iterator` is empty. + fn new(mut it: I) -> Option<Self> { let head = it.next(); - head.map(|h| { - HeadTail { - head: h, - tail: it, - } - }) + head.map(|h| Self { head: h, tail: it }) } - /// Get the next element and update `head`, returning the old head in `Some`. + /// Get the next element and update `head`, returning the old head in + /// `Some`. /// /// Returns `None` when the tail is exhausted (only `head` then remains). fn next(&mut self) -> Option<I::Item> { @@ -53,15 +51,17 @@ } impl<I> Clone for HeadTail<I> - where I: Iterator + Clone, - I::Item: Clone +where + I: Iterator + Clone, + I::Item: Clone, { clone_fields!(head, tail); } /// Make `data` a heap (min-heap w.r.t the sorting). fn heapify<T, S>(data: &mut [T], mut less_than: S) - where S: FnMut(&T, &T) -> bool +where + S: FnMut(&T, &T) -> bool, { for i in (0..data.len() / 2).rev() { sift_down(data, i, &mut less_than); @@ -70,7 +70,8 @@ /// Sift down element at `index` (`heap` is a min-heap wrt the ordering) fn sift_down<T, S>(heap: &mut [T], index: usize, mut less_than: S) - where S: FnMut(&T, &T) -> bool +where + S: FnMut(&T, &T) -> bool, { debug_assert!(index <= heap.len()); let mut pos = index; @@ -81,7 +82,7 @@ while child + 1 < heap.len() { // pick the smaller of the two children // use arithmetic to avoid an unpredictable branch - child += less_than(&heap[child+1], &heap[child]) as usize; + child += less_than(&heap[child + 1], &heap[child]) as usize; // sift down is done if we are already in order if !less_than(&heap[child], &heap[pos]) { @@ -98,8 +99,9 @@ } } -/// An iterator adaptor that merges an abitrary number of base iterators in ascending order. -/// If all base iterators are sorted (ascending), the result is sorted. +/// An iterator adaptor that merges an abitrary number of base iterators in +/// ascending order. If all base iterators are sorted (ascending), the result is +/// sorted. /// /// Iterator element type is `I::Item`. /// @@ -119,7 +121,7 @@ } } -impl<T, F: FnMut(&T, &T)->bool> KMergePredicate<T> for F { +impl<T, F: FnMut(&T, &T) -> bool> KMergePredicate<T> for F { fn kmerge_pred(&mut self, a: &T, b: &T) -> bool { self(a, b) } @@ -128,19 +130,22 @@ /// Create an iterator that merges elements of the contained iterators using /// the ordering function. /// -/// [`IntoIterator`] enabled version of [`Itertools::kmerge`]. +/// [`IntoIterator`] enabled version of +/// [`Itertools::kmerge`](crate::Itertools::kmerge). /// /// ``` /// use itertools::kmerge; /// /// for elt in kmerge(vec![vec![0, 2, 4], vec![1, 3, 5], vec![6, 7]]) { /// /* loop body */ +/// # let _ = elt; /// } /// ``` pub fn kmerge<I>(iterable: I) -> KMerge<<I::Item as IntoIterator>::IntoIter> - where I: IntoIterator, - I::Item: IntoIterator, - <<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd +where + I: IntoIterator, + I::Item: IntoIterator, + <<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd, { kmerge_by(iterable, KMergeByLt) } @@ -152,29 +157,35 @@ /// /// See [`.kmerge_by()`](crate::Itertools::kmerge_by) for more /// information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[must_use = "this iterator adaptor is not lazy but does nearly nothing unless consumed"] pub struct KMergeBy<I, F> - where I: Iterator, +where + I: Iterator, { heap: Vec<HeadTail<I>>, less_than: F, } impl<I, F> fmt::Debug for KMergeBy<I, F> - where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, +where + I: Iterator + fmt::Debug, + I::Item: fmt::Debug, { debug_fmt_fields!(KMergeBy, heap); } /// Create an iterator that merges elements of the contained iterators. /// -/// [`IntoIterator`] enabled version of [`Itertools::kmerge_by`]. -pub fn kmerge_by<I, F>(iterable: I, mut less_than: F) - -> KMergeBy<<I::Item as IntoIterator>::IntoIter, F> - where I: IntoIterator, - I::Item: IntoIterator, - F: KMergePredicate<<<I as IntoIterator>::Item as IntoIterator>::Item>, +/// [`IntoIterator`] enabled version of +/// [`Itertools::kmerge_by`](crate::Itertools::kmerge_by). +pub fn kmerge_by<I, F>( + iterable: I, + mut less_than: F, +) -> KMergeBy<<I::Item as IntoIterator>::IntoIter, F> +where + I: IntoIterator, + I::Item: IntoIterator, + F: KMergePredicate<<<I as IntoIterator>::Item as IntoIterator>::Item>, { let iter = iterable.into_iter(); let (lower, _) = iter.size_hint(); @@ -185,16 +196,18 @@ } impl<I, F> Clone for KMergeBy<I, F> - where I: Iterator + Clone, - I::Item: Clone, - F: Clone, +where + I: Iterator + Clone, + I::Item: Clone, + F: Clone, { clone_fields!(heap, less_than); } impl<I, F> Iterator for KMergeBy<I, F> - where I: Iterator, - F: KMergePredicate<I::Item> +where + I: Iterator, + F: KMergePredicate<I::Item>, { type Item = I::Item; @@ -202,26 +215,21 @@ if self.heap.is_empty() { return None; } - let result = if let Some(next) = self.heap[0].next() { - next - } else { - self.heap.swap_remove(0).head - }; + let result = + if let Some(next) = self.heap[0].next() { next } else { self.heap.swap_remove(0).head }; let less_than = &mut self.less_than; sift_down(&mut self.heap, 0, |a, b| less_than.kmerge_pred(&a.head, &b.head)); Some(result) } fn size_hint(&self) -> (usize, Option<usize>) { - #[allow(deprecated)] //TODO: once msrv hits 1.51. replace `fold1` with `reduce` - self.heap.iter() - .map(|i| i.size_hint()) - .fold1(size_hint::add) - .unwrap_or((0, Some(0))) + self.heap.iter().map(|i| i.size_hint()).reduce(size_hint::add).unwrap_or((0, Some(0))) } } impl<I, F> FusedIterator for KMergeBy<I, F> - where I: Iterator, - F: KMergePredicate<I::Item> -{} +where + I: Iterator, + F: KMergePredicate<I::Item>, +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lazy_buffer.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lazy_buffer.rs new file mode 100644 index 0000000..38e344d --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lazy_buffer.rs
@@ -0,0 +1,76 @@ +use alloc::vec::Vec; +use std::iter::Fuse; +use std::ops::Index; + +use crate::size_hint::{self, SizeHint}; + +#[derive(Debug, Clone)] +pub struct LazyBuffer<I: Iterator> { + it: Fuse<I>, + buffer: Vec<I::Item>, +} + +impl<I> LazyBuffer<I> +where + I: Iterator, +{ + pub fn new(it: I) -> Self { + Self { it: it.fuse(), buffer: Vec::new() } + } + + pub fn len(&self) -> usize { + self.buffer.len() + } + + pub fn size_hint(&self) -> SizeHint { + size_hint::add_scalar(self.it.size_hint(), self.len()) + } + + pub fn count(self) -> usize { + self.len() + self.it.count() + } + + pub fn get_next(&mut self) -> bool { + if let Some(x) = self.it.next() { + self.buffer.push(x); + true + } else { + false + } + } + + pub fn prefill(&mut self, len: usize) { + let buffer_len = self.buffer.len(); + if len > buffer_len { + let delta = len - buffer_len; + self.buffer.extend(self.it.by_ref().take(delta)); + } + } +} + +impl<I> LazyBuffer<I> +where + I: Iterator, + I::Item: Clone, +{ + pub fn get_at(&self, indices: &[usize]) -> Vec<I::Item> { + indices.iter().map(|i| self.buffer[*i].clone()).collect() + } + + pub fn get_array<const K: usize>(&self, indices: [usize; K]) -> [I::Item; K] { + indices.map(|i| self.buffer[i].clone()) + } +} + +impl<I, J> Index<J> for LazyBuffer<I> +where + I: Iterator, + I::Item: Sized, + Vec<I::Item>: Index<J>, +{ + type Output = <Vec<I::Item> as Index<J>>::Output; + + fn index(&self, index: J) -> &Self::Output { + self.buffer.index(index) + } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lib.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lib.rs similarity index 68% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lib.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lib.rs index c23a65d..7c9f5325 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lib.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lib.rs
@@ -1,6 +1,7 @@ -#![warn(missing_docs)] -#![crate_name="itertools"] +#![warn(missing_docs, clippy::default_numeric_fallback)] +#![crate_name = "itertools"] #![cfg_attr(not(feature = "use_std"), no_std)] +#![doc(test(attr(deny(warnings), allow(deprecated, unstable_name_collisions))))] //! Extra iterator adaptors, functions and macros. //! @@ -8,6 +9,7 @@ //! the [`Itertools`] trait: //! //! ``` +//! # #[allow(unused_imports)] //! use itertools::Itertools; //! ``` //! @@ -29,6 +31,7 @@ //! //! for elt in interleave(&[1, 2, 3], &[2, 3, 4]) { //! /* loop body */ +//! # let _ = elt; //! } //! ``` //! @@ -36,14 +39,17 @@ //! //! - `use_std` //! - Enabled by default. -//! - Disable to compile itertools using `#![no_std]`. This disables -//! any items that depend on collections (like `group_by`, `unique`, -//! `kmerge`, `join` and many more). +//! - Disable to compile itertools using `#![no_std]`. This disables any item +//! that depend on allocations (see the `use_alloc` feature) and hash maps +//! (like `unique`, `counts`, `into_grouping_map` and more). +//! - `use_alloc` +//! - Enabled by default. +//! - Enables any item that depend on allocations (like `chunk_by`, `kmerge`, +//! `join` and many more). //! //! ## Rust Version //! -//! This version of itertools requires Rust 1.32 or later. -#![doc(html_root_url="https://docs.rs/itertools/0.8/")] +//! This version of itertools requires Rust 1.63.0 or later. #[cfg(not(feature = "use_std"))] extern crate core as std; @@ -52,28 +58,26 @@ extern crate alloc; #[cfg(feature = "use_alloc")] -use alloc::{ - string::String, - vec::Vec, -}; +use alloc::{collections::VecDeque, string::String, vec::Vec}; pub use either::Either; use core::borrow::Borrow; +use std::cmp::Ordering; #[cfg(feature = "use_std")] use std::collections::HashMap; -use std::iter::{IntoIterator, once}; -use std::cmp::Ordering; -use std::fmt; #[cfg(feature = "use_std")] use std::collections::HashSet; -#[cfg(feature = "use_std")] -use std::hash::Hash; +use std::fmt; #[cfg(feature = "use_alloc")] use std::fmt::Write; +#[cfg(feature = "use_std")] +use std::hash::Hash; +use std::iter::{once, IntoIterator}; +#[cfg(feature = "use_alloc")] +type VecDequeIntoIter<T> = alloc::collections::vec_deque::IntoIter<T>; #[cfg(feature = "use_alloc")] type VecIntoIter<T> = alloc::vec::IntoIter<T>; -#[cfg(feature = "use_alloc")] use std::iter::FromIterator; #[macro_use] @@ -85,73 +89,56 @@ /// The concrete iterator types. pub mod structs { - pub use crate::adaptors::{ - Dedup, - DedupBy, - DedupWithCount, - DedupByWithCount, - Interleave, - InterleaveShortest, - FilterMapOk, - FilterOk, - Product, - PutBack, - Batching, - MapInto, - MapOk, - Merge, - MergeBy, - TakeWhileRef, - WhileSome, - Coalesce, - TupleCombinations, - Positions, - Update, - }; - #[allow(deprecated)] - pub use crate::adaptors::{MapResults, Step}; #[cfg(feature = "use_alloc")] pub use crate::adaptors::MultiProduct; + pub use crate::adaptors::{ + Batching, Coalesce, Dedup, DedupBy, DedupByWithCount, DedupWithCount, FilterMapOk, + FilterOk, Interleave, InterleaveShortest, MapInto, MapOk, Positions, Product, PutBack, + TakeWhileRef, TupleCombinations, Update, WhileSome, + }; #[cfg(feature = "use_alloc")] - pub use crate::combinations::Combinations; + pub use crate::combinations::{ArrayCombinations, Combinations}; #[cfg(feature = "use_alloc")] pub use crate::combinations_with_replacement::CombinationsWithReplacement; pub use crate::cons_tuples_impl::ConsTuples; + #[cfg(feature = "use_std")] + pub use crate::duplicates_impl::{Duplicates, DuplicatesBy}; pub use crate::exactly_one_err::ExactlyOneError; - pub use crate::format::{Format, FormatWith}; pub use crate::flatten_ok::FlattenOk; + pub use crate::format::{Format, FormatWith}; + #[allow(deprecated)] + #[cfg(feature = "use_alloc")] + pub use crate::groupbylazy::GroupBy; + #[cfg(feature = "use_alloc")] + pub use crate::groupbylazy::{Chunk, ChunkBy, Chunks, Group, Groups, IntoChunks}; #[cfg(feature = "use_std")] pub use crate::grouping_map::{GroupingMap, GroupingMapBy}; - #[cfg(feature = "use_alloc")] - pub use crate::groupbylazy::{IntoChunks, Chunk, Chunks, GroupBy, Group, Groups}; pub use crate::intersperse::{Intersperse, IntersperseWith}; #[cfg(feature = "use_alloc")] pub use crate::kmerge_impl::{KMerge, KMergeBy}; - pub use crate::merge_join::MergeJoinBy; + pub use crate::merge_join::{Merge, MergeBy, MergeJoinBy}; #[cfg(feature = "use_alloc")] pub use crate::multipeek_impl::MultiPeek; + pub use crate::pad_tail::PadUsing; #[cfg(feature = "use_alloc")] pub use crate::peek_nth::PeekNth; - pub use crate::pad_tail::PadUsing; pub use crate::peeking_take_while::PeekingTakeWhile; #[cfg(feature = "use_alloc")] pub use crate::permutations::Permutations; - pub use crate::process_results_impl::ProcessResults; #[cfg(feature = "use_alloc")] pub use crate::powerset::Powerset; + pub use crate::process_results_impl::ProcessResults; #[cfg(feature = "use_alloc")] pub use crate::put_back_n_impl::PutBackN; #[cfg(feature = "use_alloc")] pub use crate::rciter_impl::RcIter; pub use crate::repeatn::RepeatN; #[allow(deprecated)] - pub use crate::sources::{RepeatCall, Unfold, Iterate}; + pub use crate::sources::{Iterate, Unfold}; pub use crate::take_while_inclusive::TakeWhileInclusive; #[cfg(feature = "use_alloc")] pub use crate::tee::Tee; - pub use crate::tuple_impl::{TupleBuffer, TupleWindows, CircularTupleWindows, Tuples}; - #[cfg(feature = "use_std")] - pub use crate::duplicates_impl::{Duplicates, DuplicatesBy}; + pub use crate::tuple_impl::{CircularTupleWindows, TupleBuffer, TupleWindows, Tuples}; #[cfg(feature = "use_std")] pub use crate::unique_impl::{Unique, UniqueBy}; pub use crate::with_position::WithPosition; @@ -162,25 +149,26 @@ /// Traits helpful for using certain `Itertools` methods in generic contexts. pub mod traits { + pub use crate::iter_index::IteratorIndex; pub use crate::tuple_impl::HomogeneousTuple; } -#[allow(deprecated)] -pub use crate::structs::*; pub use crate::concat_impl::concat; pub use crate::cons_tuples_impl::cons_tuples; pub use crate::diff::diff_with; pub use crate::diff::Diff; #[cfg(feature = "use_alloc")] -pub use crate::kmerge_impl::{kmerge_by}; +pub use crate::kmerge_impl::kmerge_by; pub use crate::minmax::MinMaxResult; pub use crate::peeking_take_while::PeekingNext; pub use crate::process_results_impl::process_results; pub use crate::repeatn::repeat_n; #[allow(deprecated)] -pub use crate::sources::{repeat_call, unfold, iterate}; -pub use crate::with_position::Position; +pub use crate::sources::{iterate, unfold}; +#[allow(deprecated)] +pub use crate::structs::*; pub use crate::unziptuple::{multiunzip, MultiUnzip}; +pub use crate::with_position::Position; pub use crate::ziptuple::multizip; mod adaptors; mod either_or_both; @@ -189,25 +177,28 @@ pub mod free; #[doc(inline)] pub use crate::free::*; -mod concat_impl; -mod cons_tuples_impl; #[cfg(feature = "use_alloc")] mod combinations; #[cfg(feature = "use_alloc")] mod combinations_with_replacement; -mod exactly_one_err; +mod concat_impl; +mod cons_tuples_impl; mod diff; -mod flatten_ok; #[cfg(feature = "use_std")] +mod duplicates_impl; +mod exactly_one_err; +#[cfg(feature = "use_alloc")] mod extrema_set; +mod flatten_ok; mod format; -#[cfg(feature = "use_std")] -mod grouping_map; #[cfg(feature = "use_alloc")] mod group_map; #[cfg(feature = "use_alloc")] mod groupbylazy; +#[cfg(feature = "use_std")] +mod grouping_map; mod intersperse; +mod iter_index; #[cfg(feature = "use_alloc")] mod k_smallest; #[cfg(feature = "use_alloc")] @@ -218,6 +209,7 @@ mod minmax; #[cfg(feature = "use_alloc")] mod multipeek_impl; +mod next_array; mod pad_tail; #[cfg(feature = "use_alloc")] mod peek_nth; @@ -239,8 +231,6 @@ mod tee; mod tuple_impl; #[cfg(feature = "use_std")] -mod duplicates_impl; -#[cfg(feature = "use_std")] mod unique_impl; mod unziptuple; mod with_position; @@ -252,7 +242,8 @@ /// Create an iterator over the “cartesian product” of iterators. /// /// Iterator element type is like `(A, B, ..., E)` if formed -/// from iterators `(I, J, ..., M)` with element types `I::Item = A`, `J::Item = B`, etc. +/// from iterators `(I, J, ..., M)` with element types `I::Item = A`, `J::Item = +/// B`, etc. /// /// ``` /// # use itertools::iproduct; @@ -262,6 +253,7 @@ /// // from (0, 0, 0), (0, 0, 1), .., (0, 1, 0), (0, 1, 1), .. etc until (3, 3, 3) /// for (i, j, k) in iproduct!(0..4, 0..4, 0..4) { /// // .. +/// # let _ = (i, j, k); /// } /// # } /// ``` @@ -272,13 +264,22 @@ (@flatten $I:expr, $J:expr, $($K:expr,)*) => ( $crate::iproduct!(@flatten $crate::cons_tuples($crate::iproduct!($I, $J)), $($K,)*) ); - ($I:expr) => ( - $crate::__std_iter::IntoIterator::into_iter($I) + () => ( + $crate::__std_iter::once(()) ); - ($I:expr, $J:expr) => ( - $crate::Itertools::cartesian_product($crate::iproduct!($I), $crate::iproduct!($J)) + ($I:expr $(,)?) => ( + $crate::__std_iter::Iterator::map( + $crate::__std_iter::IntoIterator::into_iter($I), + |elt| (elt,) + ) ); - ($I:expr, $J:expr, $($K:expr),+) => ( + ($I:expr, $J:expr $(,)?) => ( + $crate::Itertools::cartesian_product( + $crate::__std_iter::IntoIterator::into_iter($I), + $crate::__std_iter::IntoIterator::into_iter($J), + ) + ); + ($I:expr, $J:expr, $($K:expr),+ $(,)?) => ( $crate::iproduct!(@flatten $crate::iproduct!($I, $J), $($K,)+) ); } @@ -338,19 +339,24 @@ // binary ($first:expr, $second:expr $(,)*) => { - $crate::izip!($first) - .zip($second) + $crate::__std_iter::Iterator::zip( + $crate::__std_iter::IntoIterator::into_iter($first), + $second, + ) }; // n-ary where n > 2 ( $first:expr $( , $rest:expr )* $(,)* ) => { - $crate::izip!($first) + { + let iter = $crate::__std_iter::IntoIterator::into_iter($first); $( - .zip($rest) + let iter = $crate::__std_iter::Iterator::zip(iter, $rest); )* - .map( + $crate::__std_iter::Iterator::map( + iter, $crate::izip!(@closure a => (a) $( , $rest )*) ) + } }; } @@ -364,7 +370,8 @@ /// /// # Examples /// -/// Empty invocations of `chain!` expand to an invocation of [`std::iter::empty`]: +/// Empty invocations of `chain!` expand to an invocation of +/// [`std::iter::empty`]: /// ``` /// use std::iter; /// use itertools::chain; @@ -373,18 +380,20 @@ /// let _: iter::Empty<i8> = chain!(); /// ``` /// -/// Invocations of `chain!` with one argument expand to [`arg.into_iter()`](IntoIterator): +/// Invocations of `chain!` with one argument expand to +/// [`arg.into_iter()`](IntoIterator): /// ``` -/// use std::{ops::Range, slice}; +/// use std::ops::Range; /// use itertools::chain; -/// let _: <Range<_> as IntoIterator>::IntoIter = chain!((2..6),); // trailing comma optional! +/// let _: <Range<_> as IntoIterator>::IntoIter = chain!(2..6,); // trailing comma optional! /// let _: <&[_] as IntoIterator>::IntoIter = chain!(&[2, 3, 4]); /// ``` /// -/// Invocations of `chain!` with multiple arguments [`.into_iter()`](IntoIterator) each -/// argument, and then [`chain`] them together: +/// Invocations of `chain!` with multiple arguments +/// [`.into_iter()`](IntoIterator) each argument, and then [`chain`] them +/// together: /// ``` -/// use std::{iter::*, ops::Range, slice}; +/// use std::{iter::*, slice}; /// use itertools::{assert_equal, chain}; /// /// // e.g., this: @@ -401,16 +410,16 @@ /// ``` macro_rules! chain { () => { - core::iter::empty() + $crate::__std_iter::empty() }; ($first:expr $(, $rest:expr )* $(,)?) => { { - let iter = core::iter::IntoIterator::into_iter($first); + let iter = $crate::__std_iter::IntoIterator::into_iter($first); $( let iter = - core::iter::Iterator::chain( + $crate::__std_iter::Iterator::chain( iter, - core::iter::IntoIterator::into_iter($rest)); + $crate::__std_iter::IntoIterator::into_iter($rest)); )* iter } @@ -422,15 +431,15 @@ /// /// This trait defines a number of methods. They are divided into two groups: /// -/// * *Adaptors* take an iterator and parameter as input, and return -/// a new iterator value. These are listed first in the trait. An example -/// of an adaptor is [`.interleave()`](Itertools::interleave) +/// * *Adaptors* take an iterator and parameter as input, and return a new +/// iterator value. These are listed first in the trait. An example of an +/// adaptor is [`.interleave()`](Itertools::interleave) /// -/// * *Regular methods* are those that don't return iterators and instead -/// return a regular value of some other kind. -/// [`.next_tuple()`](Itertools::next_tuple) is an example and the first regular -/// method in the list. -pub trait Itertools : Iterator { +/// * *Regular methods* are those that don't return iterators and instead return +/// a regular value of some other kind. +/// [`.next_tuple()`](Itertools::next_tuple) is an example and the first +/// regular method in the list. +pub trait Itertools: Iterator { // adaptors /// Alternate elements from two iterators until both have run out. @@ -446,8 +455,9 @@ /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3, 4, 5, 6]); /// ``` fn interleave<J>(self, other: J) -> Interleave<Self, J::IntoIter> - where J: IntoIterator<Item = Self::Item>, - Self: Sized + where + J: IntoIterator<Item = Self::Item>, + Self: Sized, { interleave(self, other) } @@ -464,8 +474,9 @@ /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3]); /// ``` fn interleave_shortest<J>(self, other: J) -> InterleaveShortest<Self, J::IntoIter> - where J: IntoIterator<Item = Self::Item>, - Self: Sized + where + J: IntoIterator<Item = Self::Item>, + Self: Sized, { adaptors::interleave_shortest(self, other.into_iter()) } @@ -483,8 +494,9 @@ /// itertools::assert_equal((0..3).intersperse(8), vec![0, 8, 1, 8, 2]); /// ``` fn intersperse(self, element: Self::Item) -> Intersperse<Self> - where Self: Sized, - Self::Item: Clone + where + Self: Sized, + Self::Item: Clone, { intersperse::intersperse(self, element) } @@ -504,12 +516,66 @@ /// assert_eq!(i, 8); /// ``` fn intersperse_with<F>(self, element: F) -> IntersperseWith<Self, F> - where Self: Sized, - F: FnMut() -> Self::Item + where + Self: Sized, + F: FnMut() -> Self::Item, { intersperse::intersperse_with(self, element) } + /// Returns an iterator over a subsection of the iterator. + /// + /// Works similarly to [`slice::get`](https://doc.rust-lang.org/std/primitive.slice.html#method.get). + /// + /// **Panics** for ranges `..=usize::MAX` and `0..=usize::MAX`. + /// + /// It's a generalisation of [`Iterator::take`] and [`Iterator::skip`], + /// and uses these under the hood. + /// Therefore, the resulting iterator is: + /// - [`ExactSizeIterator`] if the adapted iterator is + /// [`ExactSizeIterator`]. + /// - [`DoubleEndedIterator`] if the adapted iterator is + /// [`DoubleEndedIterator`] and [`ExactSizeIterator`]. + /// + /// # Unspecified Behavior + /// The result of indexing with an exhausted [`core::ops::RangeInclusive`] + /// is unspecified. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// + /// let vec = vec![3, 1, 4, 1, 5]; + /// + /// let mut range: Vec<_> = + /// vec.iter().get(1..=3).copied().collect(); + /// assert_eq!(&range, &[1, 4, 1]); + /// + /// // It works with other types of ranges, too + /// range = vec.iter().get(..2).copied().collect(); + /// assert_eq!(&range, &[3, 1]); + /// + /// range = vec.iter().get(0..1).copied().collect(); + /// assert_eq!(&range, &[3]); + /// + /// range = vec.iter().get(2..).copied().collect(); + /// assert_eq!(&range, &[4, 1, 5]); + /// + /// range = vec.iter().get(..=2).copied().collect(); + /// assert_eq!(&range, &[3, 1, 4]); + /// + /// range = vec.iter().get(..).copied().collect(); + /// assert_eq!(range, vec); + /// ``` + fn get<R>(self, index: R) -> R::Output + where + Self: Sized, + R: traits::IteratorIndex<Self>, + { + iter_index::get(self, index) + } + /// Create an iterator which iterates over both this and the specified /// iterator simultaneously, yielding pairs of two optional elements. /// @@ -518,8 +584,8 @@ /// As long as neither input iterator is exhausted yet, it yields two values /// via `EitherOrBoth::Both`. /// - /// When the parameter iterator is exhausted, it only yields a value from the - /// `self` iterator via `EitherOrBoth::Left`. + /// When the parameter iterator is exhausted, it only yields a value from + /// the `self` iterator via `EitherOrBoth::Left`. /// /// When the `self` iterator is exhausted, it only yields a value from the /// parameter iterator via `EitherOrBoth::Right`. @@ -538,8 +604,9 @@ /// ``` #[inline] fn zip_longest<J>(self, other: J) -> ZipLongest<Self, J::IntoIter> - where J: IntoIterator, - Self: Sized + where + J: IntoIterator, + Self: Sized, { zip_longest::zip_longest(self, other.into_iter()) } @@ -551,8 +618,9 @@ /// lengths. #[inline] fn zip_eq<J>(self, other: J) -> ZipEq<Self, J::IntoIter> - where J: IntoIterator, - Self: Sized + where + J: IntoIterator, + Self: Sized, { zip_eq(self, other) } @@ -579,10 +647,10 @@ /// /// itertools::assert_equal(pit, vec![(0, 1), (2, 3)]); /// ``` - /// fn batching<B, F>(self, f: F) -> Batching<Self, F> - where F: FnMut(&mut Self) -> Option<B>, - Self: Sized + where + F: FnMut(&mut Self) -> Option<B>, + Self: Sized, { adaptors::batching(self, f) } @@ -591,10 +659,10 @@ /// Consecutive elements that map to the same key (“runs”), are assigned /// to the same group. /// - /// `GroupBy` is the storage for the lazy grouping operation. + /// `ChunkBy` is the storage for the lazy grouping operation. /// /// If the groups are consumed in order, or if each group's iterator is - /// dropped without keeping it around, then `GroupBy` uses no + /// dropped without keeping it around, then `ChunkBy` uses no /// allocations. It needs allocations only if several group iterators /// are alive at the same time. /// @@ -609,34 +677,47 @@ /// ``` /// use itertools::Itertools; /// - /// // group data into runs of larger than zero or not. + /// // chunk data into runs of larger than zero or not. /// let data = vec![1, 3, -2, -2, 1, 0, 1, 2]; - /// // groups: |---->|------>|--------->| + /// // chunks: |---->|------>|--------->| /// - /// // Note: The `&` is significant here, `GroupBy` is iterable + /// // Note: The `&` is significant here, `ChunkBy` is iterable /// // only by reference. You can also call `.into_iter()` explicitly. /// let mut data_grouped = Vec::new(); - /// for (key, group) in &data.into_iter().group_by(|elt| *elt >= 0) { - /// data_grouped.push((key, group.collect())); + /// for (key, chunk) in &data.into_iter().chunk_by(|elt| *elt >= 0) { + /// data_grouped.push((key, chunk.collect())); /// } /// assert_eq!(data_grouped, vec![(true, vec![1, 3]), (false, vec![-2, -2]), (true, vec![1, 0, 1, 2])]); /// ``` #[cfg(feature = "use_alloc")] - fn group_by<K, F>(self, key: F) -> GroupBy<K, Self, F> - where Self: Sized, - F: FnMut(&Self::Item) -> K, - K: PartialEq, + fn chunk_by<K, F>(self, key: F) -> ChunkBy<K, Self, F> + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: PartialEq, { groupbylazy::new(self, key) } + /// See [`.chunk_by()`](Itertools::chunk_by). + #[deprecated(note = "Use .chunk_by() instead", since = "0.13.0")] + #[cfg(feature = "use_alloc")] + fn group_by<K, F>(self, key: F) -> ChunkBy<K, Self, F> + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: PartialEq, + { + self.chunk_by(key) + } + /// Return an *iterable* that can chunk the iterator. /// /// Yield subiterators (chunks) that each yield a fixed number elements, /// determined by `size`. The last chunk will be shorter if there aren't /// enough elements. /// - /// `IntoChunks` is based on `GroupBy`: it is iterable (implements + /// `IntoChunks` is based on `ChunkBy`: it is iterable (implements /// `IntoIterator`, **not** `Iterator`), and it only buffers if several /// chunk iterators are alive at the same time. /// @@ -659,7 +740,8 @@ /// ``` #[cfg(feature = "use_alloc")] fn chunks(self, size: usize) -> IntoChunks<Self> - where Self: Sized, + where + Self: Sized, { assert!(size != 0); groupbylazy::new_chunks(self, size) @@ -699,9 +781,10 @@ /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4)]); /// ``` fn tuple_windows<T>(self) -> TupleWindows<Self, T> - where Self: Sized + Iterator<Item = T::Item>, - T: traits::HomogeneousTuple, - T::Item: Clone + where + Self: Sized + Iterator<Item = T::Item>, + T: traits::HomogeneousTuple, + T::Item: Clone, { tuple_impl::tuple_windows(self) } @@ -710,9 +793,9 @@ /// elements when the window would otherwise exceed the length of the /// iterator, producing tuples of a specific size (up to 12). /// - /// `circular_tuple_windows` clones the iterator elements so that they can be - /// part of successive windows, this makes it most suited for iterators - /// of references and other values that are cheap to copy. + /// `circular_tuple_windows` clones the iterator elements so that they can + /// be part of successive windows, this makes it most suited for + /// iterators of references and other values that are cheap to copy. /// /// ``` /// use itertools::Itertools; @@ -734,9 +817,10 @@ /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4), (3, 4, 1), (4, 1, 2)]); /// ``` fn circular_tuple_windows<T>(self) -> CircularTupleWindows<Self, T> - where Self: Sized + Clone + Iterator<Item = T::Item> + ExactSizeIterator, - T: tuple_impl::TupleCollect + Clone, - T::Item: Clone + where + Self: Sized + Clone + Iterator<Item = T::Item> + ExactSizeIterator, + T: tuple_impl::TupleCollect + Clone, + T::Item: Clone, { tuple_impl::circular_tuple_windows(self) } @@ -772,8 +856,9 @@ /// /// See also [`Tuples::into_buffer`]. fn tuples<T>(self) -> Tuples<Self, T> - where Self: Sized + Iterator<Item = T::Item>, - T: traits::HomogeneousTuple + where + Self: Sized + Iterator<Item = T::Item>, + T: traits::HomogeneousTuple, { tuple_impl::tuples(self) } @@ -797,36 +882,13 @@ /// ``` #[cfg(feature = "use_alloc")] fn tee(self) -> (Tee<Self>, Tee<Self>) - where Self: Sized, - Self::Item: Clone + where + Self: Sized, + Self::Item: Clone, { tee::new(self) } - /// Return an iterator adaptor that steps `n` elements in the base iterator - /// for each iteration. - /// - /// The iterator steps by yielding the next element from the base iterator, - /// then skipping forward `n - 1` elements. - /// - /// Iterator element type is `Self::Item`. - /// - /// **Panics** if the step is 0. - /// - /// ``` - /// use itertools::Itertools; - /// - /// let it = (0..8).step(3); - /// itertools::assert_equal(it, vec![0, 3, 6]); - /// ``` - #[deprecated(note="Use std .step_by() instead", since="0.8.0")] - #[allow(deprecated)] - fn step(self, n: usize) -> Step<Self> - where Self: Sized - { - adaptors::step(self, n) - } - /// Convert each item of the iterator using the [`Into`] trait. /// /// ```rust @@ -835,21 +897,13 @@ /// (1i32..42i32).map_into::<f64>().collect_vec(); /// ``` fn map_into<R>(self) -> MapInto<Self, R> - where Self: Sized, - Self::Item: Into<R>, + where + Self: Sized, + Self::Item: Into<R>, { adaptors::map_into(self) } - /// See [`.map_ok()`](Itertools::map_ok). - #[deprecated(note="Use .map_ok() instead", since="0.10.0")] - fn map_results<F, T, U, E>(self, f: F) -> MapOk<Self, F> - where Self: Iterator<Item = Result<T, E>> + Sized, - F: FnMut(T) -> U, - { - self.map_ok(f) - } - /// Return an iterator adaptor that applies the provided closure /// to every `Result::Ok` value. `Result::Err` values are /// unchanged. @@ -862,8 +916,9 @@ /// itertools::assert_equal(it, vec![Ok(42), Err(false), Ok(12)]); /// ``` fn map_ok<F, T, U, E>(self, f: F) -> MapOk<Self, F> - where Self: Iterator<Item = Result<T, E>> + Sized, - F: FnMut(T) -> U, + where + Self: Iterator<Item = Result<T, E>> + Sized, + F: FnMut(T) -> U, { adaptors::map_ok(self, f) } @@ -880,8 +935,9 @@ /// itertools::assert_equal(it, vec![Ok(22), Err(false)]); /// ``` fn filter_ok<F, T, E>(self, f: F) -> FilterOk<Self, F> - where Self: Iterator<Item = Result<T, E>> + Sized, - F: FnMut(&T) -> bool, + where + Self: Iterator<Item = Result<T, E>> + Sized, + F: FnMut(&T) -> bool, { adaptors::filter_ok(self, f) } @@ -898,8 +954,9 @@ /// itertools::assert_equal(it, vec![Ok(44), Err(false)]); /// ``` fn filter_map_ok<F, T, U, E>(self, f: F) -> FilterMapOk<Self, F> - where Self: Iterator<Item = Result<T, E>> + Sized, - F: FnMut(T) -> Option<U>, + where + Self: Iterator<Item = Result<T, E>> + Sized, + F: FnMut(T) -> Option<U>, { adaptors::filter_map_ok(self, f) } @@ -908,7 +965,8 @@ /// a series of `Result::Ok` values. `Result::Err` values are unchanged. /// /// This is useful when you have some common error type for your crate and - /// need to propagate it upwards, but the `Result::Ok` case needs to be flattened. + /// need to propagate it upwards, but the `Result::Ok` case needs to be + /// flattened. /// /// ``` /// use itertools::Itertools; @@ -922,8 +980,9 @@ /// assert_eq!(output_result, Err(false)); /// ``` fn flatten_ok<T, E>(self) -> FlattenOk<Self, T, E> - where Self: Iterator<Item = Result<T, E>> + Sized, - T: IntoIterator + where + Self: Iterator<Item = Result<T, E>> + Sized, + T: IntoIterator, { flatten_ok::flatten_ok(self) } @@ -931,9 +990,10 @@ /// “Lift” a function of the values of the current iterator so as to process /// an iterator of `Result` values instead. /// - /// `processor` is a closure that receives an adapted version of the iterator - /// as the only argument — the adapted iterator produces elements of type `T`, - /// as long as the original iterator produces `Ok` values. + /// `processor` is a closure that receives an adapted version of the + /// iterator as the only argument — the adapted iterator produces + /// elements of type `T`, as long as the original iterator produces `Ok` + /// values. /// /// If the original iterable produces an error at any point, the adapted /// iterator ends and it will return the error iself. @@ -959,8 +1019,9 @@ /// assert!(second_max.is_err()); /// ``` fn process_results<F, T, E, R>(self, processor: F) -> Result<R, E> - where Self: Iterator<Item = Result<T, E>> + Sized, - F: FnOnce(ProcessResults<Self, E>) -> R + where + Self: Iterator<Item = Result<T, E>> + Sized, + F: FnOnce(ProcessResults<Self, E>) -> R, { process_results(self, processor) } @@ -980,15 +1041,17 @@ /// itertools::assert_equal(it, vec![0, 0, 3, 5, 6, 9, 10]); /// ``` fn merge<J>(self, other: J) -> Merge<Self, J::IntoIter> - where Self: Sized, - Self::Item: PartialOrd, - J: IntoIterator<Item = Self::Item> + where + Self: Sized, + Self::Item: PartialOrd, + J: IntoIterator<Item = Self::Item>, { merge(self, other) } /// Return an iterator adaptor that merges the two base iterators in order. - /// This is much like [`.merge()`](Itertools::merge) but allows for a custom ordering. + /// This is much like [`.merge()`](Itertools::merge) but allows for a custom + /// ordering. /// /// This can be especially useful for sequences of tuples. /// @@ -1002,13 +1065,13 @@ /// let it = a.merge_by(b, |x, y| x.1 <= y.1); /// itertools::assert_equal(it, vec![(0, 'a'), (0, 'b'), (1, 'c'), (1, 'd')]); /// ``` - fn merge_by<J, F>(self, other: J, is_first: F) -> MergeBy<Self, J::IntoIter, F> - where Self: Sized, - J: IntoIterator<Item = Self::Item>, - F: FnMut(&Self::Item, &Self::Item) -> bool + where + Self: Sized, + J: IntoIterator<Item = Self::Item>, + F: FnMut(&Self::Item, &Self::Item) -> bool, { - adaptors::merge_by_new(self, other.into_iter(), is_first) + merge_join::merge_by_new(self, other, is_first) } /// Create an iterator that merges items from both this and the specified @@ -1017,17 +1080,17 @@ /// The function can either return an `Ordering` variant or a boolean. /// /// If `cmp_fn` returns `Ordering`, - /// it chooses whether to pair elements based on the `Ordering` returned by the - /// specified compare function. At any point, inspecting the tip of the - /// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type - /// `J::Item` respectively, the resulting iterator will: + /// it chooses whether to pair elements based on the `Ordering` returned by + /// the specified compare function. At any point, inspecting the tip of + /// the iterators `I` and `J` as items `i` of type `I::Item` and `j` of + /// type `J::Item` respectively, the resulting iterator will: /// - /// - Emit `EitherOrBoth::Left(i)` when `i < j`, - /// and remove `i` from its source iterator - /// - Emit `EitherOrBoth::Right(j)` when `i > j`, - /// and remove `j` from its source iterator - /// - Emit `EitherOrBoth::Both(i, j)` when `i == j`, - /// and remove both `i` and `j` from their respective source iterators + /// - Emit `EitherOrBoth::Left(i)` when `i < j`, and remove `i` from its + /// source iterator + /// - Emit `EitherOrBoth::Right(j)` when `i > j`, and remove `j` from its + /// source iterator + /// - Emit `EitherOrBoth::Both(i, j)` when `i == j`, and remove both `i` + /// and `j` from their respective source iterators /// /// ``` /// use itertools::Itertools; @@ -1037,7 +1100,9 @@ /// let b = (0..10).step_by(3); /// /// itertools::assert_equal( - /// a.merge_join_by(b, |i, j| i.cmp(j)), + /// // This performs a diff in the style of the Unix command comm(1), + /// // generalized to arbitrary types rather than text. + /// a.merge_join_by(b, Ord::cmp), /// vec![Both(0, 0), Left(2), Right(3), Left(4), Both(6, 6), Left(1), Right(9)] /// ); /// ``` @@ -1048,10 +1113,10 @@ /// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type /// `J::Item` respectively, the resulting iterator will: /// - /// - Emit `Either::Left(i)` when `true`, - /// and remove `i` from its source iterator - /// - Emit `Either::Right(j)` when `false`, - /// and remove `j` from its source iterator + /// - Emit `Either::Left(i)` when `true`, and remove `i` from its source + /// iterator + /// - Emit `Either::Right(j)` when `false`, and remove `j` from its source + /// iterator /// /// It is similar to the `Ordering` case if the first argument is considered /// "less" than the second argument. @@ -1069,11 +1134,12 @@ /// ); /// ``` #[inline] + #[doc(alias = "comm")] fn merge_join_by<J, F, T>(self, other: J, cmp_fn: F) -> MergeJoinBy<Self, J::IntoIter, F> - where J: IntoIterator, - F: FnMut(&Self::Item, &J::Item) -> T, - T: merge_join::OrderingOrBool<Self::Item, J::Item>, - Self: Sized + where + J: IntoIterator, + F: FnMut(&Self::Item, &J::Item) -> T, + Self: Sized, { merge_join_by(self, other, cmp_fn) } @@ -1096,9 +1162,10 @@ /// ``` #[cfg(feature = "use_alloc")] fn kmerge(self) -> KMerge<<Self::Item as IntoIterator>::IntoIter> - where Self: Sized, - Self::Item: IntoIterator, - <Self::Item as IntoIterator>::Item: PartialOrd, + where + Self: Sized, + Self::Item: IntoIterator, + <Self::Item as IntoIterator>::Item: PartialOrd, { kmerge(self) } @@ -1124,12 +1191,11 @@ /// assert_eq!(it.last(), Some(-7.)); /// ``` #[cfg(feature = "use_alloc")] - fn kmerge_by<F>(self, first: F) - -> KMergeBy<<Self::Item as IntoIterator>::IntoIter, F> - where Self: Sized, - Self::Item: IntoIterator, - F: FnMut(&<Self::Item as IntoIterator>::Item, - &<Self::Item as IntoIterator>::Item) -> bool + fn kmerge_by<F>(self, first: F) -> KMergeBy<<Self::Item as IntoIterator>::IntoIter, F> + where + Self: Sized, + Self::Item: IntoIterator, + F: FnMut(&<Self::Item as IntoIterator>::Item, &<Self::Item as IntoIterator>::Item) -> bool, { kmerge_by(self, first) } @@ -1146,10 +1212,11 @@ /// itertools::assert_equal(it, vec![(0, 'α'), (0, 'β'), (1, 'α'), (1, 'β')]); /// ``` fn cartesian_product<J>(self, other: J) -> Product<Self, J::IntoIter> - where Self: Sized, - Self::Item: Clone, - J: IntoIterator, - J::IntoIter: Clone + where + Self: Sized, + Self::Item: Clone, + J: IntoIterator, + J::IntoIter: Clone, { adaptors::cartesian_product(self, other.into_iter()) } @@ -1161,10 +1228,11 @@ /// the product of iterators yielding multiple types, use the /// [`iproduct`] macro instead. /// - /// /// The iterator element type is `Vec<T>`, where `T` is the iterator element /// of the subiterators. /// + /// Note that the iterator is fused. + /// /// ``` /// use itertools::Itertools; /// let mut multi_prod = (0..3).map(|i| (i * 2)..(i * 2 + 2)) @@ -1179,12 +1247,23 @@ /// assert_eq!(multi_prod.next(), Some(vec![1, 3, 5])); /// assert_eq!(multi_prod.next(), None); /// ``` + /// + /// If the adapted iterator is empty, the result is an iterator yielding a + /// single empty vector. This is known as the [nullary cartesian product](https://en.wikipedia.org/wiki/Empty_product#Nullary_Cartesian_product). + /// + /// ``` + /// use itertools::Itertools; + /// let mut nullary_cartesian_product = (0..0).map(|i| (i * 2)..(i * 2 + 2)).multi_cartesian_product(); + /// assert_eq!(nullary_cartesian_product.next(), Some(vec![])); + /// assert_eq!(nullary_cartesian_product.next(), None); + /// ``` #[cfg(feature = "use_alloc")] fn multi_cartesian_product(self) -> MultiProduct<<Self::Item as IntoIterator>::IntoIter> - where Self: Sized, - Self::Item: IntoIterator, - <Self::Item as IntoIterator>::IntoIter: Clone, - <Self::Item as IntoIterator>::Item: Clone + where + Self: Sized, + Self::Item: IntoIterator, + <Self::Item as IntoIterator>::IntoIter: Clone, + <Self::Item as IntoIterator>::Item: Clone, { adaptors::multi_cartesian_product(self) } @@ -1218,9 +1297,9 @@ /// vec![-6., 4., -1.]); /// ``` fn coalesce<F>(self, f: F) -> Coalesce<Self, F> - where Self: Sized, - F: FnMut(Self::Item, Self::Item) - -> Result<Self::Item, (Self::Item, Self::Item)> + where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> Result<Self::Item, (Self::Item, Self::Item)>, { adaptors::coalesce(self, f) } @@ -1240,8 +1319,9 @@ /// vec![1., 2., 3., 2.]); /// ``` fn dedup(self) -> Dedup<Self> - where Self: Sized, - Self::Item: PartialEq, + where + Self: Sized, + Self::Item: PartialEq, { adaptors::dedup(self) } @@ -1262,14 +1342,15 @@ /// vec![(0, 1.), (0, 2.), (0, 3.), (1, 2.)]); /// ``` fn dedup_by<Cmp>(self, cmp: Cmp) -> DedupBy<Self, Cmp> - where Self: Sized, - Cmp: FnMut(&Self::Item, &Self::Item)->bool, + where + Self: Sized, + Cmp: FnMut(&Self::Item, &Self::Item) -> bool, { adaptors::dedup_by(self, cmp) } - /// Remove duplicates from sections of consecutive identical elements, while keeping a count of - /// how many repeated elements were present. + /// Remove duplicates from sections of consecutive identical elements, while + /// keeping a count of how many repeated elements were present. /// If the iterator is sorted, all elements will be unique. /// /// Iterator element type is `(usize, Self::Item)`. @@ -1290,8 +1371,8 @@ adaptors::dedup_with_count(self) } - /// Remove duplicates from sections of consecutive identical elements, while keeping a count of - /// how many repeated elements were present. + /// Remove duplicates from sections of consecutive identical elements, while + /// keeping a count of how many repeated elements were present. /// This will determine equality using a comparison function. /// If the iterator is sorted, all elements will be unique. /// @@ -1314,12 +1395,14 @@ adaptors::dedup_by_with_count(self, cmp) } - /// Return an iterator adaptor that produces elements that appear more than once during the - /// iteration. Duplicates are detected using hash and equality. + /// Return an iterator adaptor that produces elements that appear more than + /// once during the iteration. Duplicates are detected using hash and + /// equality. /// - /// The iterator is stable, returning the duplicate items in the order in which they occur in - /// the adapted iterator. Each duplicate item is returned exactly once. If an item appears more - /// than twice, the second item is the item retained and the rest are discarded. + /// The iterator is stable, returning the duplicate items in the order in + /// which they occur in the adapted iterator. Each duplicate item is + /// returned exactly once. If an item appears more than twice, the + /// second item is the item retained and the rest are discarded. /// /// ``` /// use itertools::Itertools; @@ -1330,21 +1413,25 @@ /// ``` #[cfg(feature = "use_std")] fn duplicates(self) -> Duplicates<Self> - where Self: Sized, - Self::Item: Eq + Hash + where + Self: Sized, + Self::Item: Eq + Hash, { duplicates_impl::duplicates(self) } - /// Return an iterator adaptor that produces elements that appear more than once during the - /// iteration. Duplicates are detected using hash and equality. + /// Return an iterator adaptor that produces elements that appear more than + /// once during the iteration. Duplicates are detected using hash and + /// equality. /// - /// Duplicates are detected by comparing the key they map to with the keying function `f` by - /// hash and equality. The keys are stored in a hash map in the iterator. + /// Duplicates are detected by comparing the key they map to with the keying + /// function `f` by hash and equality. The keys are stored in a hash map + /// in the iterator. /// - /// The iterator is stable, returning the duplicate items in the order in which they occur in - /// the adapted iterator. Each duplicate item is returned exactly once. If an item appears more - /// than twice, the second item is the item retained and the rest are discarded. + /// The iterator is stable, returning the duplicate items in the order in + /// which they occur in the adapted iterator. Each duplicate item is + /// returned exactly once. If an item appears more than twice, the + /// second item is the item retained and the rest are discarded. /// /// ``` /// use itertools::Itertools; @@ -1355,9 +1442,10 @@ /// ``` #[cfg(feature = "use_std")] fn duplicates_by<V, F>(self, f: F) -> DuplicatesBy<Self, V, F> - where Self: Sized, - V: Eq + Hash, - F: FnMut(&Self::Item) -> V + where + Self: Sized, + V: Eq + Hash, + F: FnMut(&Self::Item) -> V, { duplicates_impl::duplicates_by(self, f) } @@ -1382,8 +1470,9 @@ /// ``` #[cfg(feature = "use_std")] fn unique(self) -> Unique<Self> - where Self: Sized, - Self::Item: Clone + Eq + Hash + where + Self: Sized, + Self::Item: Clone + Eq + Hash, { unique_impl::unique(self) } @@ -1408,9 +1497,10 @@ /// ``` #[cfg(feature = "use_std")] fn unique_by<V, F>(self, f: F) -> UniqueBy<Self, V, F> - where Self: Sized, - V: Eq + Hash, - F: FnMut(&Self::Item) -> V + where + Self: Sized, + V: Eq + Hash, + F: FnMut(&Self::Item) -> V, { unique_impl::unique_by(self, f) } @@ -1428,8 +1518,9 @@ /// See also [`.take_while_ref()`](Itertools::take_while_ref) /// which is a similar adaptor. fn peeking_take_while<F>(&mut self, accept: F) -> PeekingTakeWhile<Self, F> - where Self: Sized + PeekingNext, - F: FnMut(&Self::Item) -> bool, + where + Self: Sized + PeekingNext, + F: FnMut(&Self::Item) -> bool, { peeking_take_while::peeking_take_while(self, accept) } @@ -1450,11 +1541,11 @@ /// .collect::<String>(); /// assert_eq!(decimals, "0123456789"); /// assert_eq!(hexadecimals.next(), Some('a')); - /// /// ``` fn take_while_ref<F>(&mut self, accept: F) -> TakeWhileRef<Self, F> - where Self: Clone, - F: FnMut(&Self::Item) -> bool + where + Self: Clone, + F: FnMut(&Self::Item) -> bool, { adaptors::take_while_ref(self, accept) } @@ -1519,7 +1610,8 @@ /// .collect(); /// let expected: Vec<_> = vec![1, 2, 3].into_iter().map(NoCloneImpl).collect(); /// assert_eq!(filtered, expected); - fn take_while_inclusive<F>(&mut self, accept: F) -> TakeWhileInclusive<Self, F> + #[doc(alias = "take_until")] + fn take_while_inclusive<F>(self, accept: F) -> TakeWhileInclusive<Self, F> where Self: Sized, F: FnMut(&Self::Item) -> bool, @@ -1539,10 +1631,10 @@ /// itertools::assert_equal( /// (0..).map(|i| std::char::from_digit(i, 16)).while_some(), /// "0123456789abcdef".chars()); - /// /// ``` fn while_some<A>(self) -> WhileSome<Self> - where Self: Sized + Iterator<Item = Option<A>> + where + Self: Sized + Iterator<Item = Option<A>>, { adaptors::while_some(self) } @@ -1553,6 +1645,11 @@ /// Iterator element can be any homogeneous tuple of type `Self::Item` with /// size up to 12. /// + /// # Guarantees + /// + /// If the adapted iterator is deterministic, + /// this iterator adapter yields items in a reliable order. + /// /// ``` /// use itertools::Itertools; /// @@ -1581,18 +1678,71 @@ /// itertools::assert_equal(it, vec![(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]); /// ``` fn tuple_combinations<T>(self) -> TupleCombinations<Self, T> - where Self: Sized + Clone, - Self::Item: Clone, - T: adaptors::HasCombination<Self>, + where + Self: Sized + Clone, + Self::Item: Clone, + T: adaptors::HasCombination<Self>, { adaptors::tuple_combinations(self) } - /// Return an iterator adaptor that iterates over the `k`-length combinations of - /// the elements from an iterator. + /// Return an iterator adaptor that iterates over the combinations of the + /// elements from an iterator. /// - /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new Vec per iteration, - /// and clones the iterator elements. + /// Iterator element type is [Self::Item; K]. The iterator produces a new + /// array per iteration, and clones the iterator elements. + /// + /// # Guarantees + /// + /// If the adapted iterator is deterministic, + /// this iterator adapter yields items in a reliable order. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut v = Vec::new(); + /// for [a, b] in (1..5).array_combinations() { + /// v.push([a, b]); + /// } + /// assert_eq!(v, vec![[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]); + /// + /// let mut it = (1..5).array_combinations(); + /// assert_eq!(Some([1, 2, 3]), it.next()); + /// assert_eq!(Some([1, 2, 4]), it.next()); + /// assert_eq!(Some([1, 3, 4]), it.next()); + /// assert_eq!(Some([2, 3, 4]), it.next()); + /// assert_eq!(None, it.next()); + /// + /// // this requires a type hint + /// let it = (1..5).array_combinations::<3>(); + /// itertools::assert_equal(it, vec![[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]); + /// + /// // you can also specify the complete type + /// use itertools::ArrayCombinations; + /// use std::ops::Range; + /// + /// let it: ArrayCombinations<Range<u32>, 3> = (1..5).array_combinations(); + /// itertools::assert_equal(it, vec![[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]); + /// ``` + #[cfg(feature = "use_alloc")] + fn array_combinations<const K: usize>(self) -> ArrayCombinations<Self, K> + where + Self: Sized + Clone, + Self::Item: Clone, + { + combinations::array_combinations(self) + } + + /// Return an iterator adaptor that iterates over the `k`-length + /// combinations of the elements from an iterator. + /// + /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new + /// `Vec` per iteration, and clones the iterator elements. + /// + /// # Guarantees + /// + /// If the adapted iterator is deterministic, + /// this iterator adapter yields items in a reliable order. /// /// ``` /// use itertools::Itertools; @@ -1606,7 +1756,8 @@ /// ]); /// ``` /// - /// Note: Combinations does not take into account the equality of the iterated values. + /// Note: Combinations does not take into account the equality of the + /// iterated values. /// ``` /// use itertools::Itertools; /// @@ -1619,8 +1770,9 @@ /// ``` #[cfg(feature = "use_alloc")] fn combinations(self, k: usize) -> Combinations<Self> - where Self: Sized, - Self::Item: Clone + where + Self: Sized, + Self::Item: Clone, { combinations::combinations(self, k) } @@ -1628,8 +1780,8 @@ /// Return an iterator that iterates over the `k`-length combinations of /// the elements from an iterator, with replacement. /// - /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new Vec per iteration, - /// and clones the iterator elements. + /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new + /// `Vec` per iteration, and clones the iterator elements. /// /// ``` /// use itertools::Itertools; @@ -1657,11 +1809,14 @@ /// elements from an iterator. /// /// Iterator element type is `Vec<Self::Item>` with length `k`. The iterator - /// produces a new Vec per iteration, and clones the iterator elements. + /// produces a new `Vec` per iteration, and clones the iterator elements. /// /// If `k` is greater than the length of the input iterator, the resultant /// iterator adaptor will be empty. /// + /// If you are looking for permutations with replacements, + /// use `repeat_n(iter, k).multi_cartesian_product()` instead. + /// /// ``` /// use itertools::Itertools; /// @@ -1676,7 +1831,8 @@ /// ]); /// ``` /// - /// Note: Permutations does not take into account the equality of the iterated values. + /// Note: Permutations does not take into account the equality of the + /// iterated values. /// /// ``` /// use itertools::Itertools; @@ -1692,21 +1848,22 @@ /// re-iterated if the permutations adaptor is completed and re-iterated. #[cfg(feature = "use_alloc")] fn permutations(self, k: usize) -> Permutations<Self> - where Self: Sized, - Self::Item: Clone + where + Self: Sized, + Self::Item: Clone, { permutations::permutations(self, k) } - /// Return an iterator that iterates through the powerset of the elements from an - /// iterator. + /// Return an iterator that iterates through the powerset of the elements + /// from an iterator. /// - /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new `Vec` - /// per iteration, and clones the iterator elements. + /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new + /// `Vec` per iteration, and clones the iterator elements. /// - /// The powerset of a set contains all subsets including the empty set and the full - /// input set. A powerset has length _2^n_ where _n_ is the length of the input - /// set. + /// The powerset of a set contains all subsets including the empty set and + /// the full input set. A powerset has length _2^n_ where _n_ is the + /// length of the input set. /// /// Each `Vec` produced by this iterator represents a subset of the elements /// produced by the source iterator. @@ -1728,8 +1885,9 @@ /// ``` #[cfg(feature = "use_alloc")] fn powerset(self) -> Powerset<Self> - where Self: Sized, - Self::Item: Clone, + where + Self: Sized, + Self::Item: Clone, { powerset::powerset(self) } @@ -1752,14 +1910,15 @@ /// itertools::assert_equal(it, vec![18, 16, 14, 12, 10, 4, 3, 2, 1, 0]); /// ``` fn pad_using<F>(self, min: usize, f: F) -> PadUsing<Self, F> - where Self: Sized, - F: FnMut(usize) -> Self::Item + where + Self: Sized, + F: FnMut(usize) -> Self::Item, { pad_tail::pad_using(self, min, f) } - /// Return an iterator adaptor that combines each element with a `Position` to - /// ease special-case handling of the first or last elements. + /// Return an iterator adaptor that combines each element with a `Position` + /// to ease special-case handling of the first or last elements. /// /// Iterator element type is /// [`(Position, Self::Item)`](Position) @@ -1778,7 +1937,8 @@ /// itertools::assert_equal(it, vec![(Position::Only, 0)]); /// ``` fn with_position(self) -> WithPosition<Self> - where Self: Sized, + where + Self: Sized, { with_position::with_position(self) } @@ -1786,7 +1946,8 @@ /// Return an iterator adaptor that yields the indices of all elements /// satisfying a predicate, counted from the start of the iterator. /// - /// Equivalent to `iter.enumerate().filter(|(_, v)| predicate(v)).map(|(i, _)| i)`. + /// Equivalent to `iter.enumerate().filter(|(_, v)| predicate(*v)).map(|(i, + /// _)| i)`. /// /// ``` /// use itertools::Itertools; @@ -1797,8 +1958,9 @@ /// itertools::assert_equal(data.iter().positions(|v| v % 2 == 1).rev(), vec![7, 6, 3, 2, 0]); /// ``` fn positions<P>(self, predicate: P) -> Positions<Self, P> - where Self: Sized, - P: FnMut(Self::Item) -> bool, + where + Self: Sized, + P: FnMut(Self::Item) -> bool, { adaptors::positions(self, predicate) } @@ -1810,17 +1972,62 @@ /// use itertools::Itertools; /// /// let input = vec![vec![1], vec![3, 2, 1]]; - /// let it = input.into_iter().update(|mut v| v.push(0)); + /// let it = input.into_iter().update(|v| v.push(0)); /// itertools::assert_equal(it, vec![vec![1, 0], vec![3, 2, 1, 0]]); /// ``` fn update<F>(self, updater: F) -> Update<Self, F> - where Self: Sized, - F: FnMut(&mut Self::Item), + where + Self: Sized, + F: FnMut(&mut Self::Item), { adaptors::update(self, updater) } // non-adaptor methods + /// Advances the iterator and returns the next items grouped in an array of + /// a specific size. + /// + /// If there are enough elements to be grouped in an array, then the array + /// is returned inside `Some`, otherwise `None` is returned. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut iter = 1..5; + /// + /// assert_eq!(Some([1, 2]), iter.next_array()); + /// ``` + fn next_array<const N: usize>(&mut self) -> Option<[Self::Item; N]> + where + Self: Sized, + { + next_array::next_array(self) + } + + /// Collects all items from the iterator into an array of a specific size. + /// + /// If the number of elements inside the iterator is **exactly** equal to + /// the array size, then the array is returned inside `Some`, otherwise + /// `None` is returned. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let iter = 1..3; + /// + /// if let Some([x, y]) = iter.collect_array() { + /// assert_eq!([x, y], [1, 2]) + /// } else { + /// panic!("Expected two elements") + /// } + /// ``` + fn collect_array<const N: usize>(mut self) -> Option<[Self::Item; N]> + where + Self: Sized, + { + self.next_array().filter(|_| self.next().is_none()) + } + /// Advances the iterator and returns the next items grouped in a tuple of /// a specific size (up to 12). /// @@ -1835,8 +2042,9 @@ /// assert_eq!(Some((1, 2)), iter.next_tuple()); /// ``` fn next_tuple<T>(&mut self) -> Option<T> - where Self: Sized + Iterator<Item = T::Item>, - T: traits::HomogeneousTuple + where + Self: Sized + Iterator<Item = T::Item>, + T: traits::HomogeneousTuple, { T::collect_from_iter_no_buf(self) } @@ -1860,19 +2068,19 @@ /// } /// ``` fn collect_tuple<T>(mut self) -> Option<T> - where Self: Sized + Iterator<Item = T::Item>, - T: traits::HomogeneousTuple + where + Self: Sized + Iterator<Item = T::Item>, + T: traits::HomogeneousTuple, { match self.next_tuple() { elt @ Some(_) => match self.next() { Some(_) => None, None => elt, }, - _ => None + _ => None, } } - /// Find the position and value of the first element satisfying a predicate. /// /// The iterator is not advanced past the first element found. @@ -1884,16 +2092,13 @@ /// assert_eq!(text.chars().find_position(|ch| ch.is_lowercase()), Some((1, 'α'))); /// ``` fn find_position<P>(&mut self, mut pred: P) -> Option<(usize, Self::Item)> - where P: FnMut(&Self::Item) -> bool + where + P: FnMut(&Self::Item) -> bool, { - for (index, elt) in self.enumerate() { - if pred(&elt) { - return Some((index, elt)); - } - } - None + self.enumerate().find(|(_, elt)| pred(elt)) } - /// Find the value of the first element satisfying a predicate or return the last element, if any. + /// Find the value of the first element satisfying a predicate or return the + /// last element, if any. /// /// The iterator is not advanced past the first element found. /// @@ -1904,16 +2109,34 @@ /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 5), Some(&4)); /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 2), Some(&3)); /// assert_eq!(std::iter::empty::<i32>().find_or_last(|&x| x > 5), None); + /// + /// // An iterator of Results can return the first Ok or the last Err: + /// let input = vec![Err(()), Ok(11), Err(()), Ok(22)]; + /// assert_eq!(input.into_iter().find_or_last(Result::is_ok), Some(Ok(11))); + /// + /// let input: Vec<Result<(), i32>> = vec![Err(11), Err(22)]; + /// assert_eq!(input.into_iter().find_or_last(Result::is_ok), Some(Err(22))); + /// + /// assert_eq!(std::iter::empty::<Result<(), i32>>().find_or_last(Result::is_ok), None); /// ``` fn find_or_last<P>(mut self, mut predicate: P) -> Option<Self::Item> - where Self: Sized, - P: FnMut(&Self::Item) -> bool, + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, { let mut prev = None; - self.find_map(|x| if predicate(&x) { Some(x) } else { prev = Some(x); None }) - .or(prev) + self.find_map(|x| { + if predicate(&x) { + Some(x) + } else { + prev = Some(x); + None + } + }) + .or(prev) } - /// Find the value of the first element satisfying a predicate or return the first element, if any. + /// Find the value of the first element satisfying a predicate or return the + /// first element, if any. /// /// The iterator is not advanced past the first element found. /// @@ -1924,17 +2147,23 @@ /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 5), Some(&1)); /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 2), Some(&3)); /// assert_eq!(std::iter::empty::<i32>().find_or_first(|&x| x > 5), None); + /// + /// // An iterator of Results can return the first Ok or the first Err: + /// let input = vec![Err(()), Ok(11), Err(()), Ok(22)]; + /// assert_eq!(input.into_iter().find_or_first(Result::is_ok), Some(Ok(11))); + /// + /// let input: Vec<Result<(), i32>> = vec![Err(11), Err(22)]; + /// assert_eq!(input.into_iter().find_or_first(Result::is_ok), Some(Err(11))); + /// + /// assert_eq!(std::iter::empty::<Result<(), i32>>().find_or_first(Result::is_ok), None); /// ``` fn find_or_first<P>(mut self, mut predicate: P) -> Option<Self::Item> - where Self: Sized, - P: FnMut(&Self::Item) -> bool, + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, { let first = self.next()?; - Some(if predicate(&first) { - first - } else { - self.find(|x| predicate(x)).unwrap_or(first) - }) + Some(if predicate(&first) { first } else { self.find(|x| predicate(x)).unwrap_or(first) }) } /// Returns `true` if the given item is present in this iterator. /// @@ -1965,7 +2194,7 @@ where Self: Sized, Self::Item: Borrow<Q>, - Q: PartialEq, + Q: PartialEq + ?Sized, { self.any(|x| x.borrow() == query) } @@ -1987,8 +2216,9 @@ /// assert!(data.into_iter().all_equal()); /// ``` fn all_equal(&mut self) -> bool - where Self: Sized, - Self::Item: PartialEq, + where + Self: Sized, + Self::Item: PartialEq, { match self.next() { None => true, @@ -1996,10 +2226,10 @@ } } - /// If there are elements and they are all equal, return a single copy of that element. - /// If there are no elements, return an Error containing None. - /// If there are elements and they are not all equal, return a tuple containing the first - /// two non-equal elements found. + /// If there are elements and they are all equal, return a single copy of + /// that element. If there are no elements, return an Error containing + /// None. If there are elements and they are not all equal, return a + /// tuple containing the first two non-equal elements found. /// /// ``` /// use itertools::Itertools; @@ -2013,10 +2243,11 @@ /// let data : Option<usize> = None; /// assert_eq!(data.into_iter().all_equal_value(), Err(None)); /// ``` + #[allow(clippy::type_complexity)] fn all_equal_value(&mut self) -> Result<Self::Item, Option<(Self::Item, Self::Item)>> - where - Self: Sized, - Self::Item: PartialEq + where + Self: Sized, + Self::Item: PartialEq, { let first = self.next().ok_or(None)?; let other = self.find(|x| x != &first); @@ -2044,8 +2275,9 @@ /// ``` #[cfg(feature = "use_std")] fn all_unique(&mut self) -> bool - where Self: Sized, - Self::Item: Eq + Hash + where + Self: Sized, + Self::Item: Eq + Hash, { let mut used = HashSet::new(); self.all(move |elt| used.insert(elt)) @@ -2054,20 +2286,22 @@ /// Consume the first `n` elements from the iterator eagerly, /// and return the same iterator again. /// - /// It works similarly to *.skip(* `n` *)* except it is eager and + /// It works similarly to `.skip(n)` except it is eager and /// preserves the iterator type. /// /// ``` /// use itertools::Itertools; /// - /// let mut iter = "αβγ".chars().dropping(2); + /// let iter = "αβγ".chars().dropping(2); /// itertools::assert_equal(iter, "γ".chars()); /// ``` /// /// *Fusing notes: if the iterator is exhausted by dropping, - /// the result of calling `.next()` again depends on the iterator implementation.* + /// the result of calling `.next()` again depends on the iterator + /// implementation.* fn dropping(mut self, n: usize) -> Self - where Self: Sized + where + Self: Sized, { if n > 0 { self.nth(n - 1); @@ -2091,8 +2325,8 @@ /// itertools::assert_equal(init, vec![0, 3, 6]); /// ``` fn dropping_back(mut self, n: usize) -> Self - where Self: Sized, - Self: DoubleEndedIterator + where + Self: Sized + DoubleEndedIterator, { if n > 0 { (&mut self).rev().nth(n - 1); @@ -2100,31 +2334,6 @@ self } - /// Run the closure `f` eagerly on each element of the iterator. - /// - /// Consumes the iterator until its end. - /// - /// ``` - /// use std::sync::mpsc::channel; - /// use itertools::Itertools; - /// - /// let (tx, rx) = channel(); - /// - /// // use .foreach() to apply a function to each value -- sending it - /// (0..5).map(|x| x * 2 + 1).foreach(|x| { tx.send(x).unwrap(); } ); - /// - /// drop(tx); - /// - /// itertools::assert_equal(rx.iter(), vec![1, 3, 5, 7, 9]); - /// ``` - #[deprecated(note="Use .for_each() instead", since="0.8.0")] - fn foreach<F>(self, f: F) - where F: FnMut(Self::Item), - Self: Sized, - { - self.for_each(f); - } - /// Combine all an iterator's elements into one element by using [`Extend`]. /// /// This combinator will extend the first item with each of the rest of the @@ -2139,17 +2348,20 @@ /// vec![1, 2, 3, 4, 5, 6]); /// ``` fn concat(self) -> Self::Item - where Self: Sized, - Self::Item: Extend<<<Self as Iterator>::Item as IntoIterator>::Item> + IntoIterator + Default + where + Self: Sized, + Self::Item: + Extend<<<Self as Iterator>::Item as IntoIterator>::Item> + IntoIterator + Default, { concat(self) } - /// `.collect_vec()` is simply a type specialization of [`Iterator::collect`], - /// for convenience. + /// `.collect_vec()` is simply a type specialization of + /// [`Iterator::collect`], for convenience. #[cfg(feature = "use_alloc")] fn collect_vec(self) -> Vec<Self::Item> - where Self: Sized + where + Self: Sized, { self.collect() } @@ -2165,16 +2377,18 @@ /// /// fn process_dir_entries(entries: &[fs::DirEntry]) { /// // ... + /// # let _ = entries; /// } /// - /// fn do_stuff() -> std::io::Result<()> { + /// fn do_stuff() -> io::Result<()> { /// let entries: Vec<_> = fs::read_dir(".")?.try_collect()?; /// process_dir_entries(&entries); /// /// Ok(()) /// } + /// + /// # let _ = do_stuff; /// ``` - #[cfg(feature = "use_alloc")] fn try_collect<T, U, E>(self) -> Result<U, E> where Self: Sized + Iterator<Item = Result<T, E>>, @@ -2200,21 +2414,14 @@ /// ``` #[inline] fn set_from<'a, A: 'a, J>(&mut self, from: J) -> usize - where Self: Iterator<Item = &'a mut A>, - J: IntoIterator<Item = A> + where + Self: Iterator<Item = &'a mut A>, + J: IntoIterator<Item = A>, { - let mut count = 0; - for elt in from { - match self.next() { - None => break, - Some(ptr) => *ptr = elt, - } - count += 1; - } - count + from.into_iter().zip(self).map(|(new, old)| *old = new).count() } - /// Combine all iterator elements into one String, separated by `sep`. + /// Combine all iterator elements into one `String`, separated by `sep`. /// /// Use the `Display` implementation of each element. /// @@ -2226,7 +2433,8 @@ /// ``` #[cfg(feature = "use_alloc")] fn join(&mut self, sep: &str) -> String - where Self::Item: std::fmt::Display + where + Self::Item: std::fmt::Display, { match self.next() { None => String::new(), @@ -2260,7 +2468,8 @@ /// "1.10, 2.72, -3.00"); /// ``` fn format(self, sep: &str) -> Format<Self> - where Self: Sized, + where + Self: Sized, { format::new_format_default(self, sep) } @@ -2294,44 +2503,38 @@ /// }); /// assert_eq!(format!("{}", matrix_formatter), /// "1, 2, 3\n4, 5, 6"); - /// - /// /// ``` fn format_with<F>(self, sep: &str, format: F) -> FormatWith<Self, F> - where Self: Sized, - F: FnMut(Self::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result, + where + Self: Sized, + F: FnMut(Self::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result, { format::new_format(self, sep, format) } - /// See [`.fold_ok()`](Itertools::fold_ok). - #[deprecated(note="Use .fold_ok() instead", since="0.10.0")] - fn fold_results<A, E, B, F>(&mut self, start: B, f: F) -> Result<B, E> - where Self: Iterator<Item = Result<A, E>>, - F: FnMut(B, A) -> B - { - self.fold_ok(start, f) - } - /// Fold `Result` values from an iterator. /// /// Only `Ok` values are folded. If no error is encountered, the folded /// value is returned inside `Ok`. Otherwise, the operation terminates - /// and returns the first `Err` value it encounters. No iterator elements are - /// consumed after the first error. + /// and returns the first `Err` value it encounters. No iterator elements + /// are consumed after the first error. /// /// The first accumulator value is the `start` parameter. - /// Each iteration passes the accumulator value and the next value inside `Ok` - /// to the fold function `f` and its return value becomes the new accumulator value. + /// Each iteration passes the accumulator value and the next value inside + /// `Ok` to the fold function `f` and its return value becomes the new + /// accumulator value. /// /// For example the sequence *Ok(1), Ok(2), Ok(3)* will result in a /// computation like this: /// - /// ```ignore + /// ```no_run + /// # let start = 0; + /// # let f = |x, y| x + y; /// let mut accum = start; /// accum = f(accum, 1); /// accum = f(accum, 2); /// accum = f(accum, 3); + /// # let _ = accum; /// ``` /// /// With a `start` value of 0 and an addition as folding function, @@ -2356,8 +2559,9 @@ /// ); /// ``` fn fold_ok<A, E, B, F>(&mut self, mut start: B, mut f: F) -> Result<B, E> - where Self: Iterator<Item = Result<A, E>>, - F: FnMut(B, A) -> B + where + Self: Iterator<Item = Result<A, E>>, + F: FnMut(B, A) -> B, { for elt in self { match elt { @@ -2388,8 +2592,9 @@ /// assert_eq!(more_values.next().unwrap(), Some(0)); /// ``` fn fold_options<A, B, F>(&mut self, mut start: B, mut f: F) -> Option<B> - where Self: Iterator<Item = Option<A>>, - F: FnMut(B, A) -> B + where + Self: Iterator<Item = Option<A>>, + F: FnMut(B, A) -> B, { for elt in self { match elt { @@ -2412,10 +2617,14 @@ /// assert_eq!((0..10).fold1(|x, y| x + y).unwrap_or(0), 45); /// assert_eq!((0..0).fold1(|x, y| x * y), None); /// ``` - #[deprecated(since = "0.10.2", note = "Use `Iterator::reduce` instead")] + #[deprecated( + note = "Use [`Iterator::reduce`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.reduce) instead", + since = "0.10.2" + )] fn fold1<F>(mut self, f: F) -> Option<Self::Item> - where F: FnMut(Self::Item, Self::Item) -> Self::Item, - Self: Sized, + where + F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized, { self.next().map(move |x| self.fold(x, f)) } @@ -2448,65 +2657,110 @@ /// └─f─f─f─f─f─f /// ``` /// - /// If `f` is associative, prefer the normal [`Iterator::reduce`] instead. + /// If `f` is associative you should also decide carefully: + /// + /// For an iterator producing `n` elements, both [`Iterator::reduce`] and + /// `tree_reduce` will call `f` `n - 1` times. However, `tree_reduce` + /// will call `f` on earlier intermediate results, which is beneficial + /// for `f` that allocate and produce longer results for longer + /// arguments. For example if `f` combines arguments using `format!`, then + /// `tree_reduce` will operate on average on shorter arguments resulting + /// in less bytes being allocated overall. + /// + /// Moreover, the output of `tree_reduce` is preferable to that of + /// [`Iterator::reduce`] in certain cases. For example, building a + /// binary search tree using `tree_reduce` will result in a balanced + /// tree with height `O(ln(n))`, while [`Iterator::reduce`] will output a + /// tree with height `O(n)`, essentially a linked list. + /// + /// If `f` does not benefit from such a reordering, like + /// `u32::wrapping_add`, prefer the normal [`Iterator::reduce`] instead + /// since it will most likely result in the generation of simpler code + /// because the compiler is able to optimize it. /// /// ``` /// use itertools::Itertools; /// + /// let f = |a: String, b: String| { + /// format!("f({a}, {b})") + /// }; + /// /// // The same tree as above - /// let num_strings = (1..8).map(|x| x.to_string()); - /// assert_eq!(num_strings.tree_fold1(|x, y| format!("f({}, {})", x, y)), - /// Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))"))); + /// assert_eq!((1..8).map(|x| x.to_string()).tree_reduce(f), + /// Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))"))); /// - /// // Like fold1, an empty iterator produces None - /// assert_eq!((0..0).tree_fold1(|x, y| x * y), None); + /// // Like reduce, an empty iterator produces None + /// assert_eq!((0..0).tree_reduce(|x, y| x * y), None); /// - /// // tree_fold1 matches fold1 for associative operations... - /// assert_eq!((0..10).tree_fold1(|x, y| x + y), - /// (0..10).fold1(|x, y| x + y)); + /// // tree_reduce matches reduce for associative operations... + /// assert_eq!((0..10).tree_reduce(|x, y| x + y), + /// (0..10).reduce(|x, y| x + y)); + /// /// // ...but not for non-associative ones - /// assert_ne!((0..10).tree_fold1(|x, y| x - y), - /// (0..10).fold1(|x, y| x - y)); + /// assert_ne!((0..10).tree_reduce(|x, y| x - y), + /// (0..10).reduce(|x, y| x - y)); + /// + /// let mut total_len_reduce = 0; + /// let reduce_res = (1..100).map(|x| x.to_string()) + /// .reduce(|a, b| { + /// let r = f(a, b); + /// total_len_reduce += r.len(); + /// r + /// }) + /// .unwrap(); + /// + /// let mut total_len_tree_reduce = 0; + /// let tree_reduce_res = (1..100).map(|x| x.to_string()) + /// .tree_reduce(|a, b| { + /// let r = f(a, b); + /// total_len_tree_reduce += r.len(); + /// r + /// }) + /// .unwrap(); + /// + /// assert_eq!(total_len_reduce, 33299); + /// assert_eq!(total_len_tree_reduce, 4228); + /// assert_eq!(reduce_res.len(), tree_reduce_res.len()); /// ``` - fn tree_fold1<F>(mut self, mut f: F) -> Option<Self::Item> - where F: FnMut(Self::Item, Self::Item) -> Self::Item, - Self: Sized, + fn tree_reduce<F>(mut self, mut f: F) -> Option<Self::Item> + where + F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized, { type State<T> = Result<T, Option<T>>; fn inner0<T, II, FF>(it: &mut II, f: &mut FF) -> State<T> - where - II: Iterator<Item = T>, - FF: FnMut(T, T) -> T + where + II: Iterator<Item = T>, + FF: FnMut(T, T) -> T, { // This function could be replaced with `it.next().ok_or(None)`, - // but half the useful tree_fold1 work is combining adjacent items, + // but half the useful tree_reduce work is combining adjacent items, // so put that in a form that LLVM is more likely to optimize well. - let a = - if let Some(v) = it.next() { v } - else { return Err(None) }; - let b = - if let Some(v) = it.next() { v } - else { return Err(Some(a)) }; + let a = if let Some(v) = it.next() { + v + } else { + return Err(None); + }; + let b = if let Some(v) = it.next() { + v + } else { + return Err(Some(a)); + }; Ok(f(a, b)) } fn inner<T, II, FF>(stop: usize, it: &mut II, f: &mut FF) -> State<T> - where - II: Iterator<Item = T>, - FF: FnMut(T, T) -> T + where + II: Iterator<Item = T>, + FF: FnMut(T, T) -> T, { let mut x = inner0(it, f)?; for height in 0..stop { // Try to get another tree the same size with which to combine it, // creating a new tree that's twice as big for next time around. - let next = - if height == 0 { - inner0(it, f) - } else { - inner(height, it, f) - }; + let next = if height == 0 { inner0(it, f) } else { inner(height, it, f) }; match next { Ok(y) => x = f(x, y), @@ -2521,16 +2775,27 @@ Ok(x) } - match inner(usize::max_value(), &mut self, &mut f) { + match inner(usize::MAX, &mut self, &mut f) { Err(x) => x, _ => unreachable!(), } } - /// An iterator method that applies a function, producing a single, final value. + /// See [`.tree_reduce()`](Itertools::tree_reduce). + #[deprecated(note = "Use .tree_reduce() instead", since = "0.13.0")] + fn tree_fold1<F>(self, f: F) -> Option<Self::Item> + where + F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized, + { + self.tree_reduce(f) + } + + /// An iterator method that applies a function, producing a single, final + /// value. /// - /// `fold_while()` is basically equivalent to [`Iterator::fold`] but with additional support for - /// early exit via short-circuiting. + /// `fold_while()` is basically equivalent to [`Iterator::fold`] but with + /// additional support for early exit via short-circuiting. /// /// ``` /// use itertools::Itertools; @@ -2563,23 +2828,24 @@ /// assert_eq!(result2, result3); /// ``` /// - /// The big difference between the computations of `result2` and `result3` is that while - /// `fold()` called the provided closure for every item of the callee iterator, - /// `fold_while()` actually stopped iterating as soon as it encountered `Fold::Done(_)`. + /// The big difference between the computations of `result2` and `result3` + /// is that while `fold()` called the provided closure for every item of + /// the callee iterator, `fold_while()` actually stopped iterating as + /// soon as it encountered `Fold::Done(_)`. fn fold_while<B, F>(&mut self, init: B, mut f: F) -> FoldWhile<B> - where Self: Sized, - F: FnMut(B, Self::Item) -> FoldWhile<B> + where + Self: Sized, + F: FnMut(B, Self::Item) -> FoldWhile<B>, { - use Result::{ - Ok as Continue, - Err as Break, - }; + use Result::{Err as Break, Ok as Continue}; - let result = self.try_fold(init, #[inline(always)] |acc, v| - match f(acc, v) { - FoldWhile::Continue(acc) => Continue(acc), - FoldWhile::Done(acc) => Break(acc), - } + let result = self.try_fold( + init, + #[inline(always)] + |acc, v| match f(acc, v) { + FoldWhile::Continue(acc) => Continue(acc), + FoldWhile::Done(acc) => Break(acc), + }, ); match result { @@ -2594,9 +2860,9 @@ /// /// # Panics /// - /// When calling `sum1()` and a primitive integer type is being returned, this - /// method will panic if the computation overflows and debug assertions are - /// enabled. + /// When calling `sum1()` and a primitive integer type is being returned, + /// this method will panic if the computation overflows and debug + /// assertions are enabled. /// /// # Examples /// @@ -2610,11 +2876,11 @@ /// assert_eq!(nonempty_sum, Some(55)); /// ``` fn sum1<S>(mut self) -> Option<S> - where Self: Sized, - S: std::iter::Sum<Self::Item>, + where + Self: Sized, + S: std::iter::Sum<Self::Item>, { - self.next() - .map(|first| once(first).chain(self).sum()) + self.next().map(|first| once(first).chain(self).sum()) } /// Iterate over the entire iterator and multiply all the elements. @@ -2623,9 +2889,9 @@ /// /// # Panics /// - /// When calling `product1()` and a primitive integer type is being returned, - /// method will panic if the computation overflows and debug assertions are - /// enabled. + /// When calling `product1()` and a primitive integer type is being + /// returned, method will panic if the computation overflows and debug + /// assertions are enabled. /// /// # Examples /// ``` @@ -2638,11 +2904,11 @@ /// assert_eq!(nonempty_product, Some(3628800)); /// ``` fn product1<P>(mut self) -> Option<P> - where Self: Sized, - P: std::iter::Product<Self::Item>, + where + Self: Sized, + P: std::iter::Product<Self::Item>, { - self.next() - .map(|first| once(first).chain(self).product()) + self.next().map(|first| once(first).chain(self).product()) } /// Sort all iterator elements into a new iterator in ascending order. @@ -2650,7 +2916,7 @@ /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_unstable`] method and returns the result as a new /// iterator that owns its elements. - /// + /// /// This sort is unstable (i.e., may reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted @@ -2666,8 +2932,9 @@ /// ``` #[cfg(feature = "use_alloc")] fn sorted_unstable(self) -> VecIntoIter<Self::Item> - where Self: Sized, - Self::Item: Ord + where + Self: Sized, + Self::Item: Ord, { // Use .sort_unstable() directly since it is not quite identical with // .sort_by(Ord::cmp) @@ -2681,7 +2948,7 @@ /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_unstable_by`] method and returns the result as a new /// iterator that owns its elements. - /// + /// /// This sort is unstable (i.e., may reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted @@ -2703,8 +2970,9 @@ /// ``` #[cfg(feature = "use_alloc")] fn sorted_unstable_by<F>(self, cmp: F) -> VecIntoIter<Self::Item> - where Self: Sized, - F: FnMut(&Self::Item, &Self::Item) -> Ordering, + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, { let mut v = Vec::from_iter(self); v.sort_unstable_by(cmp); @@ -2716,7 +2984,7 @@ /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_unstable_by_key`] method and returns the result as a new /// iterator that owns its elements. - /// + /// /// This sort is unstable (i.e., may reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted @@ -2738,9 +3006,10 @@ /// ``` #[cfg(feature = "use_alloc")] fn sorted_unstable_by_key<K, F>(self, f: F) -> VecIntoIter<Self::Item> - where Self: Sized, - K: Ord, - F: FnMut(&Self::Item) -> K, + where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K, { let mut v = Vec::from_iter(self); v.sort_unstable_by_key(f); @@ -2752,7 +3021,7 @@ /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort`] method and returns the result as a new /// iterator that owns its elements. - /// + /// /// This sort is stable (i.e., does not reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted @@ -2768,8 +3037,9 @@ /// ``` #[cfg(feature = "use_alloc")] fn sorted(self) -> VecIntoIter<Self::Item> - where Self: Sized, - Self::Item: Ord + where + Self: Sized, + Self::Item: Ord, { // Use .sort() directly since it is not quite identical with // .sort_by(Ord::cmp) @@ -2783,7 +3053,7 @@ /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_by`] method and returns the result as a new /// iterator that owns its elements. - /// + /// /// This sort is stable (i.e., does not reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted @@ -2805,8 +3075,9 @@ /// ``` #[cfg(feature = "use_alloc")] fn sorted_by<F>(self, cmp: F) -> VecIntoIter<Self::Item> - where Self: Sized, - F: FnMut(&Self::Item, &Self::Item) -> Ordering, + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, { let mut v = Vec::from_iter(self); v.sort_by(cmp); @@ -2818,7 +3089,7 @@ /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_by_key`] method and returns the result as a new /// iterator that owns its elements. - /// + /// /// This sort is stable (i.e., does not reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted @@ -2840,22 +3111,23 @@ /// ``` #[cfg(feature = "use_alloc")] fn sorted_by_key<K, F>(self, f: F) -> VecIntoIter<Self::Item> - where Self: Sized, - K: Ord, - F: FnMut(&Self::Item) -> K, + where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K, { let mut v = Vec::from_iter(self); v.sort_by_key(f); v.into_iter() } - /// Sort all iterator elements into a new iterator in ascending order. The key function is - /// called exactly once per key. + /// Sort all iterator elements into a new iterator in ascending order. The + /// key function is called exactly once per key. /// /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_by_cached_key`] method and returns the result as a new /// iterator that owns its elements. - /// + /// /// This sort is stable (i.e., does not reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted @@ -2916,12 +3188,460 @@ /// ``` #[cfg(feature = "use_alloc")] fn k_smallest(self, k: usize) -> VecIntoIter<Self::Item> - where Self: Sized, - Self::Item: Ord + where + Self: Sized, + Self::Item: Ord, { - crate::k_smallest::k_smallest(self, k) - .into_sorted_vec() - .into_iter() + // The stdlib heap has optimised handling of "holes", which is not included in + // our heap implementation in k_smallest_general. While the difference + // is unlikely to have practical impact unless `Self::Item` is very large, this + // method uses the stdlib structure to maintain performance compared to + // previous versions of the crate. + use alloc::collections::BinaryHeap; + + if k == 0 { + self.last(); + return Vec::new().into_iter(); + } + if k == 1 { + return self.min().into_iter().collect_vec().into_iter(); + } + + let mut iter = self.fuse(); + let mut heap: BinaryHeap<_> = iter.by_ref().take(k).collect(); + + iter.for_each(|i| { + debug_assert_eq!(heap.len(), k); + // Equivalent to heap.push(min(i, heap.pop())) but more efficient. + // This should be done with a single `.peek_mut().unwrap()` but + // `PeekMut` sifts-down unconditionally on Rust 1.46.0 and prior. + if *heap.peek().unwrap() > i { + *heap.peek_mut().unwrap() = i; + } + }); + + heap.into_sorted_vec().into_iter() + } + + /// Sort the k smallest elements into a new iterator using the provided + /// comparison. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// This corresponds to `self.sorted_by(cmp).take(k)` in the same way that + /// [`k_smallest`](Itertools::k_smallest) corresponds to + /// `self.sorted().take(k)`, in both semantics and complexity. + /// + /// Particularly, a custom heap implementation ensures the comparison is not + /// cloned. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_smallest = numbers + /// .into_iter() + /// .k_smallest_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b))); + /// + /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_smallest_by<F>(self, k: usize, cmp: F) -> VecIntoIter<Self::Item> + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + k_smallest::k_smallest_general(self, k, cmp).into_iter() + } + + /// Return the elements producing the k smallest outputs of the provided + /// function. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// This corresponds to `self.sorted_by_key(key).take(k)` in the same way + /// that [`k_smallest`](Itertools::k_smallest) corresponds to + /// `self.sorted().take(k)`, in both semantics and complexity. + /// + /// Particularly, a custom heap implementation ensures the comparison is not + /// cloned. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_smallest = numbers + /// .into_iter() + /// .k_smallest_by_key(5, |n| (n % 7, *n)); + /// + /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_smallest_by_key<F, K>(self, k: usize, key: F) -> VecIntoIter<Self::Item> + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: Ord, + { + self.k_smallest_by(k, k_smallest::key_to_cmp(key)) + } + + /// Sort the k smallest elements into a new iterator, in ascending order, + /// relaxing the amount of memory required. + /// + /// **Note:** This consumes the entire iterator, and returns the result + /// as a new iterator that owns its elements. If the input contains + /// less than k elements, the result is equivalent to `self.sorted()`. + /// + /// This is guaranteed to use `2 * k * sizeof(Self::Item) + O(1)` memory + /// and `O(n + k log k)` time, with `n` the number of elements in the input, + /// meaning it uses more memory than the minimum obtained by + /// [`k_smallest`](Itertools::k_smallest) but achieves linear time in + /// the number of elements. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// **Note:** This is functionally-equivalent to `self.sorted().take(k)` + /// but much more efficient. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_smallest = numbers + /// .into_iter() + /// .k_smallest_relaxed(5); + /// + /// itertools::assert_equal(five_smallest, 0..5); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_smallest_relaxed(self, k: usize) -> VecIntoIter<Self::Item> + where + Self: Sized, + Self::Item: Ord, + { + self.k_smallest_relaxed_by(k, Ord::cmp) + } + + /// Sort the k smallest elements into a new iterator using the provided + /// comparison, relaxing the amount of memory required. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// This corresponds to `self.sorted_by(cmp).take(k)` in the same way that + /// [`k_smallest_relaxed`](Itertools::k_smallest_relaxed) corresponds to + /// `self.sorted().take(k)`, in both semantics and complexity. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_smallest = numbers + /// .into_iter() + /// .k_smallest_relaxed_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b))); + /// + /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_smallest_relaxed_by<F>(self, k: usize, cmp: F) -> VecIntoIter<Self::Item> + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + k_smallest::k_smallest_relaxed_general(self, k, cmp).into_iter() + } + + /// Return the elements producing the k smallest outputs of the provided + /// function, relaxing the amount of memory required. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// This corresponds to `self.sorted_by_key(key).take(k)` in the same way + /// that [`k_smallest_relaxed`](Itertools::k_smallest_relaxed) + /// corresponds to `self.sorted().take(k)`, in both semantics and + /// complexity. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_smallest = numbers + /// .into_iter() + /// .k_smallest_relaxed_by_key(5, |n| (n % 7, *n)); + /// + /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_smallest_relaxed_by_key<F, K>(self, k: usize, key: F) -> VecIntoIter<Self::Item> + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: Ord, + { + self.k_smallest_relaxed_by(k, k_smallest::key_to_cmp(key)) + } + + /// Sort the k largest elements into a new iterator, in descending order. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// It is semantically equivalent to [`k_smallest`](Itertools::k_smallest) + /// with a reversed `Ord`. + /// However, this is implemented with a custom binary heap which does not + /// have the same performance characteristics for very large `Self::Item`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_largest = numbers + /// .into_iter() + /// .k_largest(5); + /// + /// itertools::assert_equal(five_largest, vec![14, 13, 12, 11, 10]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_largest(self, k: usize) -> VecIntoIter<Self::Item> + where + Self: Sized, + Self::Item: Ord, + { + self.k_largest_by(k, Self::Item::cmp) + } + + /// Sort the k largest elements into a new iterator using the provided + /// comparison. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// Functionally equivalent to [`k_smallest_by`](Itertools::k_smallest_by) + /// with a reversed `Ord`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_largest = numbers + /// .into_iter() + /// .k_largest_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b))); + /// + /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_largest_by<F>(self, k: usize, mut cmp: F) -> VecIntoIter<Self::Item> + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + self.k_smallest_by(k, move |a, b| cmp(b, a)) + } + + /// Return the elements producing the k largest outputs of the provided + /// function. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// Functionally equivalent to + /// [`k_smallest_by_key`](Itertools::k_smallest_by_key) with a reversed + /// `Ord`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_largest = numbers + /// .into_iter() + /// .k_largest_by_key(5, |n| (n % 7, *n)); + /// + /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_largest_by_key<F, K>(self, k: usize, key: F) -> VecIntoIter<Self::Item> + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: Ord, + { + self.k_largest_by(k, k_smallest::key_to_cmp(key)) + } + + /// Sort the k largest elements into a new iterator, in descending order, + /// relaxing the amount of memory required. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// It is semantically equivalent to + /// [`k_smallest_relaxed`](Itertools::k_smallest_relaxed) + /// with a reversed `Ord`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_largest = numbers + /// .into_iter() + /// .k_largest_relaxed(5); + /// + /// itertools::assert_equal(five_largest, vec![14, 13, 12, 11, 10]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_largest_relaxed(self, k: usize) -> VecIntoIter<Self::Item> + where + Self: Sized, + Self::Item: Ord, + { + self.k_largest_relaxed_by(k, Self::Item::cmp) + } + + /// Sort the k largest elements into a new iterator using the provided + /// comparison, relaxing the amount of memory required. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// Functionally equivalent to + /// [`k_smallest_relaxed_by`](Itertools::k_smallest_relaxed_by) + /// with a reversed `Ord`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_largest = numbers + /// .into_iter() + /// .k_largest_relaxed_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b))); + /// + /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_largest_relaxed_by<F>(self, k: usize, mut cmp: F) -> VecIntoIter<Self::Item> + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + self.k_smallest_relaxed_by(k, move |a, b| cmp(b, a)) + } + + /// Return the elements producing the k largest outputs of the provided + /// function, relaxing the amount of memory required. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// Functionally equivalent to + /// [`k_smallest_relaxed_by_key`](Itertools::k_smallest_relaxed_by_key) + /// with a reversed `Ord`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // A random permutation of 0..15 + /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; + /// + /// let five_largest = numbers + /// .into_iter() + /// .k_largest_relaxed_by_key(5, |n| (n % 7, *n)); + /// + /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_largest_relaxed_by_key<F, K>(self, k: usize, key: F) -> VecIntoIter<Self::Item> + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: Ord, + { + self.k_largest_relaxed_by(k, k_smallest::key_to_cmp(key)) + } + + /// Consumes the iterator and return an iterator of the last `n` elements. + /// + /// The iterator, if directly collected to a `VecDeque`, is converted + /// without any extra copying or allocation cost. + /// If directly collected to a `Vec`, it may need some data movement + /// but no re-allocation. + /// + /// ``` + /// use itertools::{assert_equal, Itertools}; + /// + /// let v = vec![5, 9, 8, 4, 2, 12, 0]; + /// assert_equal(v.iter().tail(3), &[2, 12, 0]); + /// assert_equal(v.iter().tail(10), &v); + /// + /// assert_equal(v.iter().tail(1), v.iter().last()); + /// + /// assert_equal((0..100).tail(10), 90..100); + /// + /// assert_equal((0..100).filter(|x| x % 3 == 0).tail(10), (72..100).step_by(3)); + /// ``` + /// + /// For double ended iterators without side-effects, you might prefer + /// `.rev().take(n).rev()` to have a similar result (lazy and + /// non-allocating) without consuming the entire iterator. + #[cfg(feature = "use_alloc")] + fn tail(self, n: usize) -> VecDequeIntoIter<Self::Item> + where + Self: Sized, + { + match n { + 0 => { + self.last(); + VecDeque::new() + } + 1 => self.last().into_iter().collect(), + _ => { + // Skip the starting part of the iterator if possible. + let (low, _) = self.size_hint(); + let mut iter = self.fuse().skip(low.saturating_sub(n)); + // TODO: If VecDeque has a more efficient method than + // `.pop_front();.push_back(val)` in the future then maybe revisit this. + let mut data: Vec<_> = iter.by_ref().take(n).collect(); + // Update `data` cyclically. + let idx = iter.fold(0, |i, val| { + debug_assert_eq!(data.len(), n); + data[i] = val; + if i + 1 == n { + 0 + } else { + i + 1 + } + }); + // Respect the insertion order, efficiently. + let mut data = VecDeque::from(data); + data.rotate_left(idx); + data + } + } + .into_iter() } /// Collect all iterator elements into one of two @@ -2946,10 +3666,11 @@ /// assert_eq!(failures, [false, true]); /// ``` fn partition_map<A, B, F, L, R>(self, mut predicate: F) -> (A, B) - where Self: Sized, - F: FnMut(Self::Item) -> Either<L, R>, - A: Default + Extend<L>, - B: Default + Extend<R>, + where + Self: Sized, + F: FnMut(Self::Item) -> Either<L, R>, + A: Default + Extend<L>, + B: Default + Extend<R>, { let mut left = A::default(); let mut right = B::default(); @@ -2978,10 +3699,10 @@ /// assert_eq!(failures, [false, true]); /// ``` fn partition_result<A, B, T, E>(self) -> (A, B) - where - Self: Iterator<Item = Result<T, E>> + Sized, - A: Default + Extend<T>, - B: Default + Extend<E>, + where + Self: Iterator<Item = Result<T, E>> + Sized, + A: Default + Extend<T>, + B: Default + Extend<E>, { self.partition_map(|r| match r { Ok(v) => Either::Left(v), @@ -3007,16 +3728,19 @@ /// ``` #[cfg(feature = "use_std")] fn into_group_map<K, V>(self) -> HashMap<K, Vec<V>> - where Self: Iterator<Item=(K, V)> + Sized, - K: Hash + Eq, + where + Self: Iterator<Item = (K, V)> + Sized, + K: Hash + Eq, { group_map::into_group_map(self) } - /// Return an `Iterator` on a `HashMap`. Keys mapped to `Vec`s of values. The key is specified - /// in the closure. + /// Return a `HashMap` of keys mapped to `Vec`s of values. The key is + /// specified in the closure. The values are taken from the input + /// iterator. /// - /// Essentially a shorthand for `.into_grouping_map_by(f).collect::<Vec<_>>()`. + /// Essentially a shorthand for + /// `.into_grouping_map_by(f).collect::<Vec<_>>()`. /// /// ``` /// use itertools::Itertools; @@ -3026,7 +3750,7 @@ /// let lookup: HashMap<u32,Vec<(u32, u32)>> = /// data.clone().into_iter().into_group_map_by(|a| a.0); /// - /// assert_eq!(lookup[&0], vec![(0,10),(0,20)]); + /// assert_eq!(lookup[&0], vec![(0,10), (0,20)]); /// assert_eq!(lookup.get(&1), None); /// assert_eq!(lookup[&2], vec![(2,12), (2,42)]); /// assert_eq!(lookup[&3], vec![(3,13), (3,33)]); @@ -3042,10 +3766,10 @@ /// ``` #[cfg(feature = "use_std")] fn into_group_map_by<K, V, F>(self, f: F) -> HashMap<K, Vec<V>> - where - Self: Iterator<Item=V> + Sized, - K: Hash + Eq, - F: Fn(&V) -> K, + where + Self: Iterator<Item = V> + Sized, + K: Hash + Eq, + F: FnMut(&V) -> K, { group_map::into_group_map_by(self, f) } @@ -3061,8 +3785,9 @@ /// on what operations are available. #[cfg(feature = "use_std")] fn into_grouping_map<K, V>(self) -> GroupingMap<Self> - where Self: Iterator<Item=(K, V)> + Sized, - K: Hash + Eq, + where + Self: Iterator<Item = (K, V)> + Sized, + K: Hash + Eq, { grouping_map::new(self) } @@ -3070,18 +3795,20 @@ /// Constructs a `GroupingMap` to be used later with one of the efficient /// group-and-fold operations it allows to perform. /// - /// The values from this iterator will be used as values for the folding operation - /// while the keys will be obtained from the values by calling `key_mapper`. + /// The values from this iterator will be used as values for the folding + /// operation while the keys will be obtained from the values by calling + /// `key_mapper`. /// /// See [`GroupingMap`] for more informations /// on what operations are available. #[cfg(feature = "use_std")] fn into_grouping_map_by<K, V, F>(self, key_mapper: F) -> GroupingMapBy<Self, F> - where Self: Iterator<Item=V> + Sized, - K: Hash + Eq, - F: FnMut(&V) -> K + where + Self: Iterator<Item = V> + Sized, + K: Hash + Eq, + F: FnMut(&V) -> K, { - grouping_map::new(grouping_map::MapForGrouping::new(self, key_mapper)) + grouping_map::new(grouping_map::new_map_for_grouping(self, key_mapper)) } /// Return all minimum elements of an iterator. @@ -3106,9 +3833,11 @@ /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. - #[cfg(feature = "use_std")] + #[cfg(feature = "use_alloc")] fn min_set(self) -> Vec<Self::Item> - where Self: Sized, Self::Item: Ord + where + Self: Sized, + Self::Item: Ord, { extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x.cmp(y)) } @@ -3137,15 +3866,13 @@ /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. - #[cfg(feature = "use_std")] + #[cfg(feature = "use_alloc")] fn min_set_by<F>(self, mut compare: F) -> Vec<Self::Item> - where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, { - extrema_set::min_set_impl( - self, - |_| (), - |x, y, _, _| compare(x, y) - ) + extrema_set::min_set_impl(self, |_| (), |x, y, _, _| compare(x, y)) } /// Return all minimum elements of an iterator, as determined by @@ -3171,9 +3898,12 @@ /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. - #[cfg(feature = "use_std")] + #[cfg(feature = "use_alloc")] fn min_set_by_key<K, F>(self, key: F) -> Vec<Self::Item> - where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K + where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K, { extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky)) } @@ -3200,9 +3930,11 @@ /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. - #[cfg(feature = "use_std")] + #[cfg(feature = "use_alloc")] fn max_set(self) -> Vec<Self::Item> - where Self: Sized, Self::Item: Ord + where + Self: Sized, + Self::Item: Ord, { extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x.cmp(y)) } @@ -3231,15 +3963,13 @@ /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. - #[cfg(feature = "use_std")] + #[cfg(feature = "use_alloc")] fn max_set_by<F>(self, mut compare: F) -> Vec<Self::Item> - where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, { - extrema_set::max_set_impl( - self, - |_| (), - |x, y, _, _| compare(x, y) - ) + extrema_set::max_set_impl(self, |_| (), |x, y, _, _| compare(x, y)) } /// Return all maximum elements of an iterator, as determined by @@ -3265,9 +3995,12 @@ /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. - #[cfg(feature = "use_std")] + #[cfg(feature = "use_alloc")] fn max_set_by_key<K, F>(self, key: F) -> Vec<Self::Item> - where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K + where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K, { extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky)) } @@ -3278,9 +4011,9 @@ /// /// - `NoElements` if the iterator is empty. /// - `OneElement(x)` if the iterator has exactly one element. - /// - `MinMax(x, y)` is returned otherwise, where `x <= y`. Two - /// values are equal if and only if there is more than one - /// element in the iterator and all elements are equal. + /// - `MinMax(x, y)` is returned otherwise, where `x <= y`. Two values are + /// equal if and only if there is more than one element in the iterator + /// and all elements are equal. /// /// On an iterator of length `n`, `minmax` does `1.5 * n` comparisons, /// and so is faster than calling `min` and `max` separately which does @@ -3308,7 +4041,9 @@ /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. fn minmax(self) -> MinMaxResult<Self::Item> - where Self: Sized, Self::Item: PartialOrd + where + Self: Sized, + Self::Item: PartialOrd, { minmax::minmax_impl(self, |_| (), |x, y, _, _| x < y) } @@ -3316,16 +4051,20 @@ /// Return the minimum and maximum element of an iterator, as determined by /// the specified function. /// - /// The return value is a variant of [`MinMaxResult`] like for [`.minmax()`](Itertools::minmax). + /// The return value is a variant of [`MinMaxResult`] like for + /// [`.minmax()`](Itertools::minmax). /// - /// For the minimum, the first minimal element is returned. For the maximum, - /// the last maximal element wins. This matches the behavior of the standard - /// [`Iterator::min`] and [`Iterator::max`] methods. + /// For the minimum, the first minimal element is returned. For the + /// maximum, the last maximal element wins. This matches the behavior + /// of the standard [`Iterator::min`] and [`Iterator::max`] methods. /// /// The keys can be floats but no particular result is guaranteed /// if a key is NaN. fn minmax_by_key<K, F>(self, key: F) -> MinMaxResult<Self::Item> - where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K + where + Self: Sized, + K: PartialOrd, + F: FnMut(&Self::Item) -> K, { minmax::minmax_impl(self, key, |_, _, xk, yk| xk < yk) } @@ -3333,19 +4072,18 @@ /// Return the minimum and maximum element of an iterator, as determined by /// the specified comparison function. /// - /// The return value is a variant of [`MinMaxResult`] like for [`.minmax()`](Itertools::minmax). + /// The return value is a variant of [`MinMaxResult`] like for + /// [`.minmax()`](Itertools::minmax). /// - /// For the minimum, the first minimal element is returned. For the maximum, - /// the last maximal element wins. This matches the behavior of the standard - /// [`Iterator::min`] and [`Iterator::max`] methods. + /// For the minimum, the first minimal element is returned. For the + /// maximum, the last maximal element wins. This matches the behavior + /// of the standard [`Iterator::min`] and [`Iterator::max`] methods. fn minmax_by<F>(self, mut compare: F) -> MinMaxResult<Self::Item> - where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, { - minmax::minmax_impl( - self, - |_| (), - |x, y, _, _| Ordering::Less == compare(x, y) - ) + minmax::minmax_impl(self, |_| (), |x, y, _, _| Ordering::Less == compare(x, y)) } /// Return the position of the maximum element in the iterator. @@ -3368,11 +4106,11 @@ /// assert_eq!(a.iter().position_max(), Some(1)); /// ``` fn position_max(self) -> Option<usize> - where Self: Sized, Self::Item: Ord + where + Self: Sized, + Self::Item: Ord, { - self.enumerate() - .max_by(|x, y| Ord::cmp(&x.1, &y.1)) - .map(|x| x.0) + self.enumerate().max_by(|x, y| Ord::cmp(&x.1, &y.1)).map(|x| x.0) } /// Return the position of the maximum element in the iterator, as @@ -3396,11 +4134,12 @@ /// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), Some(3)); /// ``` fn position_max_by_key<K, F>(self, mut key: F) -> Option<usize> - where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K + where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K, { - self.enumerate() - .max_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1))) - .map(|x| x.0) + self.enumerate().max_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1))).map(|x| x.0) } /// Return the position of the maximum element in the iterator, as @@ -3424,11 +4163,11 @@ /// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), Some(1)); /// ``` fn position_max_by<F>(self, mut compare: F) -> Option<usize> - where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, { - self.enumerate() - .max_by(|x, y| compare(&x.1, &y.1)) - .map(|x| x.0) + self.enumerate().max_by(|x, y| compare(&x.1, &y.1)).map(|x| x.0) } /// Return the position of the minimum element in the iterator. @@ -3451,11 +4190,11 @@ /// assert_eq!(a.iter().position_min(), Some(2)); /// ``` fn position_min(self) -> Option<usize> - where Self: Sized, Self::Item: Ord + where + Self: Sized, + Self::Item: Ord, { - self.enumerate() - .min_by(|x, y| Ord::cmp(&x.1, &y.1)) - .map(|x| x.0) + self.enumerate().min_by(|x, y| Ord::cmp(&x.1, &y.1)).map(|x| x.0) } /// Return the position of the minimum element in the iterator, as @@ -3479,11 +4218,12 @@ /// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), Some(0)); /// ``` fn position_min_by_key<K, F>(self, mut key: F) -> Option<usize> - where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K + where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K, { - self.enumerate() - .min_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1))) - .map(|x| x.0) + self.enumerate().min_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1))).map(|x| x.0) } /// Return the position of the minimum element in the iterator, as @@ -3507,11 +4247,11 @@ /// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), Some(2)); /// ``` fn position_min_by<F>(self, mut compare: F) -> Option<usize> - where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, { - self.enumerate() - .min_by(|x, y| compare(&x.1, &y.1)) - .map(|x| x.0) + self.enumerate().min_by(|x, y| compare(&x.1, &y.1)).map(|x| x.0) } /// Return the positions of the minimum and maximum elements in @@ -3521,10 +4261,9 @@ /// /// - `NoElements` if the iterator is empty. /// - `OneElement(xpos)` if the iterator has exactly one element. - /// - `MinMax(xpos, ypos)` is returned otherwise, where the - /// element at `xpos` ≤ the element at `ypos`. While the - /// referenced elements themselves may be equal, `xpos` cannot - /// be equal to `ypos`. + /// - `MinMax(xpos, ypos)` is returned otherwise, where the element at + /// `xpos` ≤ the element at `ypos`. While the referenced elements + /// themselves may be equal, `xpos` cannot be equal to `ypos`. /// /// On an iterator of length `n`, `position_minmax` does `1.5 * n` /// comparisons, and so is faster than calling `position_min` and @@ -3557,9 +4296,11 @@ /// assert_eq!(a.iter().position_minmax(), MinMax(2, 1)); /// ``` fn position_minmax(self) -> MinMaxResult<usize> - where Self: Sized, Self::Item: PartialOrd + where + Self: Sized, + Self::Item: PartialOrd, { - use crate::MinMaxResult::{NoElements, OneElement, MinMax}; + use crate::MinMaxResult::{MinMax, NoElements, OneElement}; match minmax::minmax_impl(self.enumerate(), |_| (), |x, y, _, _| x.1 < y.1) { NoElements => NoElements, OneElement(x) => OneElement(x.0), @@ -3602,9 +4343,12 @@ /// /// [`position_minmax`]: Self::position_minmax fn position_minmax_by_key<K, F>(self, mut key: F) -> MinMaxResult<usize> - where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K + where + Self: Sized, + K: PartialOrd, + F: FnMut(&Self::Item) -> K, { - use crate::MinMaxResult::{NoElements, OneElement, MinMax}; + use crate::MinMaxResult::{MinMax, NoElements, OneElement}; match self.enumerate().minmax_by_key(|e| key(&e.1)) { NoElements => NoElements, OneElement(x) => OneElement(x.0), @@ -3644,9 +4388,11 @@ /// /// [`position_minmax`]: Self::position_minmax fn position_minmax_by<F>(self, mut compare: F) -> MinMaxResult<usize> - where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, { - use crate::MinMaxResult::{NoElements, OneElement, MinMax}; + use crate::MinMaxResult::{MinMax, NoElements, OneElement}; match self.enumerate().minmax_by(|x, y| compare(&x.1, &y.1)) { NoElements => NoElements, OneElement(x) => OneElement(x.0), @@ -3654,13 +4400,14 @@ } } - /// If the iterator yields exactly one element, that element will be returned, otherwise - /// an error will be returned containing an iterator that has the same output as the input - /// iterator. + /// If the iterator yields exactly one element, that element will be + /// returned, otherwise an error will be returned containing an iterator + /// that has the same output as the input iterator. /// - /// This provides an additional layer of validation over just calling `Iterator::next()`. - /// If your assumption that there should only be one element yielded is false this provides - /// the opportunity to detect and handle that, preventing errors at a distance. + /// This provides an additional layer of validation over just calling + /// `Iterator::next()`. If your assumption that there should only be one + /// element yielded is false this provides the opportunity to detect and + /// handle that, preventing errors at a distance. /// /// # Examples /// ``` @@ -3676,27 +4423,25 @@ Self: Sized, { match self.next() { - Some(first) => { - match self.next() { - Some(second) => { - Err(ExactlyOneError::new(Some(Either::Left([first, second])), self)) - } - None => { - Ok(first) - } + Some(first) => match self.next() { + Some(second) => { + Err(ExactlyOneError::new(Some(Either::Left([first, second])), self)) } - } + None => Ok(first), + }, None => Err(ExactlyOneError::new(None, self)), } } - /// If the iterator yields no elements, Ok(None) will be returned. If the iterator yields - /// exactly one element, that element will be returned, otherwise an error will be returned - /// containing an iterator that has the same output as the input iterator. + /// If the iterator yields no elements, `Ok(None)` will be returned. If the + /// iterator yields exactly one element, that element will be returned, + /// otherwise an error will be returned containing an iterator that has + /// the same output as the input iterator. /// - /// This provides an additional layer of validation over just calling `Iterator::next()`. - /// If your assumption that there should be at most one element yielded is false this provides - /// the opportunity to detect and handle that, preventing errors at a distance. + /// This provides an additional layer of validation over just calling + /// `Iterator::next()`. If your assumption that there should be at most + /// one element yielded is false this provides the opportunity to detect + /// and handle that, preventing errors at a distance. /// /// # Examples /// ``` @@ -3712,16 +4457,12 @@ Self: Sized, { match self.next() { - Some(first) => { - match self.next() { - Some(second) => { - Err(ExactlyOneError::new(Some(Either::Left([first, second])), self)) - } - None => { - Ok(Some(first)) - } + Some(first) => match self.next() { + Some(second) => { + Err(ExactlyOneError::new(Some(Either::Left([first, second])), self)) } - } + None => Ok(Some(first)), + }, None => Ok(None), } } @@ -3755,7 +4496,7 @@ /// # Examples /// ``` /// # use itertools::Itertools; - /// let counts = [1, 1, 1, 3, 3, 5].into_iter().counts(); + /// let counts = [1, 1, 1, 3, 3, 5].iter().counts(); /// assert_eq!(counts[&1], 3); /// assert_eq!(counts[&3], 2); /// assert_eq!(counts[&5], 1); @@ -3781,6 +4522,7 @@ /// # use itertools::Itertools; /// struct Character { /// first_name: &'static str, + /// # #[allow(dead_code)] /// last_name: &'static str, /// } /// @@ -3816,8 +4558,8 @@ /// Converts an iterator of tuples into a tuple of containers. /// - /// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each - /// column. + /// It consumes an entire iterator of n-ary tuples, producing `n` + /// collections, one for each column. /// /// This function is, in some sense, the opposite of [`multizip`]. /// @@ -3840,9 +4582,33 @@ { MultiUnzip::multiunzip(self) } + + /// Returns the length of the iterator if one exists. + /// Otherwise return `self.size_hint()`. + /// + /// Fallible [`ExactSizeIterator::len`]. + /// + /// Inherits guarantees and restrictions from [`Iterator::size_hint`]. + /// + /// ``` + /// use itertools::Itertools; + /// + /// assert_eq!([0; 10].iter().try_len(), Ok(10)); + /// assert_eq!((10..15).try_len(), Ok(5)); + /// assert_eq!((15..10).try_len(), Ok(0)); + /// assert_eq!((10..).try_len(), Err((usize::MAX, None))); + /// assert_eq!((10..15).filter(|x| x % 2 == 0).try_len(), Err((0, Some(5)))); + /// ``` + fn try_len(&self) -> Result<usize, size_hint::SizeHint> { + let sh = self.size_hint(); + match sh { + (lo, Some(hi)) if lo == hi => Ok(lo), + _ => Err(sh), + } + } } -impl<T: ?Sized> Itertools for T where T: Iterator { } +impl<T> Itertools for T where T: Iterator + ?Sized {} /// Return `true` if both iterables produce equal sequences /// (elements pairwise equal and sequences of the same length), @@ -3855,9 +4621,10 @@ /// assert!(!itertools::equal(&[0, 0], &[0, 0, 0])); /// ``` pub fn equal<I, J>(a: I, b: J) -> bool - where I: IntoIterator, - J: IntoIterator, - I::Item: PartialEq<J::Item> +where + I: IntoIterator, + J: IntoIterator, + I::Item: PartialEq<J::Item>, { a.into_iter().eq(b) } @@ -3866,31 +4633,39 @@ /// semantics as [`equal(a, b)`](equal). /// /// **Panics** on assertion failure with a message that shows the -/// two iteration elements. +/// two different elements and the iteration index. /// -/// ```ignore +/// ```should_panic +/// # use itertools::assert_equal; /// assert_equal("exceed".split('c'), "excess".split('c')); -/// // ^PANIC: panicked at 'Failed assertion Some("eed") == Some("ess") for iteration 1', +/// // ^PANIC: panicked at 'Failed assertion Some("eed") == Some("ess") for iteration 1'. /// ``` +#[track_caller] pub fn assert_equal<I, J>(a: I, b: J) - where I: IntoIterator, - J: IntoIterator, - I::Item: fmt::Debug + PartialEq<J::Item>, - J::Item: fmt::Debug, +where + I: IntoIterator, + J: IntoIterator, + I::Item: fmt::Debug + PartialEq<J::Item>, + J::Item: fmt::Debug, { let mut ia = a.into_iter(); let mut ib = b.into_iter(); - let mut i = 0; + let mut i: usize = 0; loop { match (ia.next(), ib.next()) { (None, None) => return, (a, b) => { let equal = match (&a, &b) { - (&Some(ref a), &Some(ref b)) => a == b, + (Some(a), Some(b)) => a == b, _ => false, }; - assert!(equal, "Failed assertion {a:?} == {b:?} for iteration {i}", - i=i, a=a, b=b); + assert!( + equal, + "Failed assertion {a:?} == {b:?} for iteration {i}", + i = i, + a = a, + b = b + ); i += 1; } } @@ -3915,22 +4690,18 @@ /// assert_eq!(split_index, 3); /// ``` pub fn partition<'a, A: 'a, I, F>(iter: I, mut pred: F) -> usize - where I: IntoIterator<Item = &'a mut A>, - I::IntoIter: DoubleEndedIterator, - F: FnMut(&A) -> bool +where + I: IntoIterator<Item = &'a mut A>, + I::IntoIter: DoubleEndedIterator, + F: FnMut(&A) -> bool, { let mut split_index = 0; let mut iter = iter.into_iter(); - 'main: while let Some(front) = iter.next() { + while let Some(front) = iter.next() { if !pred(front) { - loop { - match iter.next_back() { - Some(back) => if pred(back) { - std::mem::swap(front, back); - break; - }, - None => break 'main, - } + match iter.rfind(|back| pred(back)) { + Some(back) => std::mem::swap(front, back), + None => break, } } split_index += 1; @@ -3953,15 +4724,15 @@ /// Return the value in the continue or done. pub fn into_inner(self) -> T { match self { - FoldWhile::Continue(x) | FoldWhile::Done(x) => x, + Self::Continue(x) | Self::Done(x) => x, } } /// Return true if `self` is `Done`, false if it is `Continue`. pub fn is_done(&self) -> bool { match *self { - FoldWhile::Continue(_) => false, - FoldWhile::Done(_) => true, + Self::Continue(_) => false, + Self::Done(_) => true, } } }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/merge_join.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/merge_join.rs new file mode 100644 index 0000000..1d5dca56 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/merge_join.rs
@@ -0,0 +1,353 @@ +use std::cmp::Ordering; +use std::fmt; +use std::iter::{Fuse, FusedIterator}; +use std::marker::PhantomData; + +use either::Either; + +use super::adaptors::{put_back, PutBack}; +use crate::either_or_both::EitherOrBoth; +use crate::size_hint::{self, SizeHint}; +#[cfg(doc)] +use crate::Itertools; + +#[derive(Clone, Debug)] +pub struct MergeLte; + +/// An iterator adaptor that merges the two base iterators in ascending order. +/// If both base iterators are sorted (ascending), the result is sorted. +/// +/// Iterator element type is `I::Item`. +/// +/// See [`.merge()`](crate::Itertools::merge_by) for more information. +pub type Merge<I, J> = MergeBy<I, J, MergeLte>; + +/// Create an iterator that merges elements in `i` and `j`. +/// +/// [`IntoIterator`] enabled version of +/// [`Itertools::merge`](crate::Itertools::merge). +/// +/// ``` +/// use itertools::merge; +/// +/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) { +/// /* loop body */ +/// # let _ = elt; +/// } +/// ``` +pub fn merge<I, J>( + i: I, + j: J, +) -> Merge<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter> +where + I: IntoIterator, + J: IntoIterator<Item = I::Item>, + I::Item: PartialOrd, +{ + merge_by_new(i, j, MergeLte) +} + +/// An iterator adaptor that merges the two base iterators in ascending order. +/// If both base iterators are sorted (ascending), the result is sorted. +/// +/// Iterator element type is `I::Item`. +/// +/// See [`.merge_by()`](crate::Itertools::merge_by) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct MergeBy<I: Iterator, J: Iterator, F> { + left: PutBack<Fuse<I>>, + right: PutBack<Fuse<J>>, + cmp_fn: F, +} + +/// Create a `MergeBy` iterator. +pub fn merge_by_new<I, J, F>(a: I, b: J, cmp: F) -> MergeBy<I::IntoIter, J::IntoIter, F> +where + I: IntoIterator, + J: IntoIterator<Item = I::Item>, +{ + MergeBy { + left: put_back(a.into_iter().fuse()), + right: put_back(b.into_iter().fuse()), + cmp_fn: cmp, + } +} + +/// Return an iterator adaptor that merge-joins items from the two base +/// iterators in ascending order. +/// +/// [`IntoIterator`] enabled version of [`Itertools::merge_join_by`]. +pub fn merge_join_by<I, J, F, T>( + left: I, + right: J, + cmp_fn: F, +) -> MergeJoinBy<I::IntoIter, J::IntoIter, F> +where + I: IntoIterator, + J: IntoIterator, + F: FnMut(&I::Item, &J::Item) -> T, +{ + MergeBy { + left: put_back(left.into_iter().fuse()), + right: put_back(right.into_iter().fuse()), + cmp_fn: MergeFuncLR(cmp_fn, PhantomData), + } +} + +/// An iterator adaptor that merge-joins items from the two base iterators in +/// ascending order. +/// +/// See [`.merge_join_by()`](crate::Itertools::merge_join_by) for more +/// information. +pub type MergeJoinBy<I, J, F> = + MergeBy<I, J, MergeFuncLR<F, <F as FuncLR<<I as Iterator>::Item, <J as Iterator>::Item>>::T>>; + +#[derive(Clone, Debug)] +pub struct MergeFuncLR<F, T>(F, PhantomData<T>); + +pub trait FuncLR<L, R> { + type T; +} + +impl<L, R, T, F: FnMut(&L, &R) -> T> FuncLR<L, R> for F { + type T = T; +} + +pub trait OrderingOrBool<L, R> { + type MergeResult; + fn left(left: L) -> Self::MergeResult; + fn right(right: R) -> Self::MergeResult; + // "merge" never returns (Some(...), Some(...), ...) so Option<Either<I::Item, + // J::Item>> is appealing but it is always followed by two put_backs, so we + // think the compiler is smart enough to optimize it. Or we could move + // put_backs into "merge". + fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult); + fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint; +} + +impl<L, R, F: FnMut(&L, &R) -> Ordering> OrderingOrBool<L, R> for MergeFuncLR<F, Ordering> { + type MergeResult = EitherOrBoth<L, R>; + fn left(left: L) -> Self::MergeResult { + EitherOrBoth::Left(left) + } + fn right(right: R) -> Self::MergeResult { + EitherOrBoth::Right(right) + } + fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult) { + match self.0(&left, &right) { + Ordering::Equal => (None, EitherOrBoth::Both(left, right)), + Ordering::Less => (Some(Either::Right(right)), EitherOrBoth::Left(left)), + Ordering::Greater => (Some(Either::Left(left)), EitherOrBoth::Right(right)), + } + } + fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { + let (a_lower, a_upper) = left; + let (b_lower, b_upper) = right; + let lower = ::std::cmp::max(a_lower, b_lower); + let upper = match (a_upper, b_upper) { + (Some(x), Some(y)) => x.checked_add(y), + _ => None, + }; + (lower, upper) + } +} + +impl<L, R, F: FnMut(&L, &R) -> bool> OrderingOrBool<L, R> for MergeFuncLR<F, bool> { + type MergeResult = Either<L, R>; + fn left(left: L) -> Self::MergeResult { + Either::Left(left) + } + fn right(right: R) -> Self::MergeResult { + Either::Right(right) + } + fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult) { + if self.0(&left, &right) { + (Some(Either::Right(right)), Either::Left(left)) + } else { + (Some(Either::Left(left)), Either::Right(right)) + } + } + fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { + // Not ExactSizeIterator because size may be larger than usize + size_hint::add(left, right) + } +} + +impl<T, F: FnMut(&T, &T) -> bool> OrderingOrBool<T, T> for F { + type MergeResult = T; + fn left(left: T) -> Self::MergeResult { + left + } + fn right(right: T) -> Self::MergeResult { + right + } + fn merge(&mut self, left: T, right: T) -> (Option<Either<T, T>>, Self::MergeResult) { + if self(&left, &right) { + (Some(Either::Right(right)), left) + } else { + (Some(Either::Left(left)), right) + } + } + fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { + // Not ExactSizeIterator because size may be larger than usize + size_hint::add(left, right) + } +} + +impl<T: PartialOrd> OrderingOrBool<T, T> for MergeLte { + type MergeResult = T; + fn left(left: T) -> Self::MergeResult { + left + } + fn right(right: T) -> Self::MergeResult { + right + } + fn merge(&mut self, left: T, right: T) -> (Option<Either<T, T>>, Self::MergeResult) { + if left <= right { + (Some(Either::Right(right)), left) + } else { + (Some(Either::Left(left)), right) + } + } + fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { + // Not ExactSizeIterator because size may be larger than usize + size_hint::add(left, right) + } +} + +impl<I, J, F> Clone for MergeBy<I, J, F> +where + I: Iterator, + J: Iterator, + PutBack<Fuse<I>>: Clone, + PutBack<Fuse<J>>: Clone, + F: Clone, +{ + clone_fields!(left, right, cmp_fn); +} + +impl<I, J, F> fmt::Debug for MergeBy<I, J, F> +where + I: Iterator + fmt::Debug, + I::Item: fmt::Debug, + J: Iterator + fmt::Debug, + J::Item: fmt::Debug, +{ + debug_fmt_fields!(MergeBy, left, right); +} + +impl<I, J, F> Iterator for MergeBy<I, J, F> +where + I: Iterator, + J: Iterator, + F: OrderingOrBool<I::Item, J::Item>, +{ + type Item = F::MergeResult; + + fn next(&mut self) -> Option<Self::Item> { + match (self.left.next(), self.right.next()) { + (None, None) => None, + (Some(left), None) => Some(F::left(left)), + (None, Some(right)) => Some(F::right(right)), + (Some(left), Some(right)) => { + let (not_next, next) = self.cmp_fn.merge(left, right); + match not_next { + Some(Either::Left(l)) => { + self.left.put_back(l); + } + Some(Either::Right(r)) => { + self.right.put_back(r); + } + None => (), + } + + Some(next) + } + } + } + + fn fold<B, G>(mut self, init: B, mut f: G) -> B + where + Self: Sized, + G: FnMut(B, Self::Item) -> B, + { + let mut acc = init; + let mut left = self.left.next(); + let mut right = self.right.next(); + + loop { + match (left, right) { + (Some(l), Some(r)) => match self.cmp_fn.merge(l, r) { + (Some(Either::Right(r)), x) => { + acc = f(acc, x); + left = self.left.next(); + right = Some(r); + } + (Some(Either::Left(l)), x) => { + acc = f(acc, x); + left = Some(l); + right = self.right.next(); + } + (None, x) => { + acc = f(acc, x); + left = self.left.next(); + right = self.right.next(); + } + }, + (Some(l), None) => { + self.left.put_back(l); + acc = self.left.fold(acc, |acc, x| f(acc, F::left(x))); + break; + } + (None, Some(r)) => { + self.right.put_back(r); + acc = self.right.fold(acc, |acc, x| f(acc, F::right(x))); + break; + } + (None, None) => { + break; + } + } + } + + acc + } + + fn size_hint(&self) -> SizeHint { + F::size_hint(self.left.size_hint(), self.right.size_hint()) + } + + fn nth(&mut self, mut n: usize) -> Option<Self::Item> { + loop { + if n == 0 { + break self.next(); + } + n -= 1; + match (self.left.next(), self.right.next()) { + (None, None) => break None, + (Some(_left), None) => break self.left.nth(n).map(F::left), + (None, Some(_right)) => break self.right.nth(n).map(F::right), + (Some(left), Some(right)) => { + let (not_next, _) = self.cmp_fn.merge(left, right); + match not_next { + Some(Either::Left(l)) => { + self.left.put_back(l); + } + Some(Either::Right(r)) => { + self.right.put_back(r); + } + None => (), + } + } + } + } + } +} + +impl<I, J, F> FusedIterator for MergeBy<I, J, F> +where + I: Iterator, + J: Iterator, + F: OrderingOrBool<I::Item, J::Item>, +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/minmax.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/minmax.rs similarity index 69% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/minmax.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/minmax.rs index 52b2f11..a4205044 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/minmax.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/minmax.rs
@@ -1,8 +1,7 @@ - /// `MinMaxResult` is an enum returned by `minmax`. /// /// See [`.minmax()`](crate::Itertools::minmax) for more detail. -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum MinMaxResult<T> { /// Empty iterator NoElements, @@ -12,15 +11,15 @@ /// More than one element in the iterator, the first element is not larger /// than the second - MinMax(T, T) + MinMax(T, T), } impl<T: Clone> MinMaxResult<T> { - /// `into_option` creates an `Option` of type `(T, T)`. The returned `Option` - /// has variant `None` if and only if the `MinMaxResult` has variant - /// `NoElements`. Otherwise `Some((x, y))` is returned where `x <= y`. - /// If the `MinMaxResult` has variant `OneElement(x)`, performing this - /// operation will make one clone of `x`. + /// `into_option` creates an `Option` of type `(T, T)`. The returned + /// `Option` has variant `None` if and only if the `MinMaxResult` has + /// variant `NoElements`. Otherwise `Some((x, y))` is returned where `x + /// <= y`. If the `MinMaxResult` has variant `OneElement(x)`, performing + /// this operation will make one clone of `x`. /// /// # Examples /// @@ -36,34 +35,36 @@ /// let r = MinMax(1, 2); /// assert_eq!(r.into_option(), Some((1, 2))); /// ``` - pub fn into_option(self) -> Option<(T,T)> { + pub fn into_option(self) -> Option<(T, T)> { match self { - MinMaxResult::NoElements => None, - MinMaxResult::OneElement(x) => Some((x.clone(), x)), - MinMaxResult::MinMax(x, y) => Some((x, y)) + Self::NoElements => None, + Self::OneElement(x) => Some((x.clone(), x)), + Self::MinMax(x, y) => Some((x, y)), } } } /// Implementation guts for `minmax` and `minmax_by_key`. -pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F, - mut lt: L) -> MinMaxResult<I::Item> - where I: Iterator, - F: FnMut(&I::Item) -> K, - L: FnMut(&I::Item, &I::Item, &K, &K) -> bool, +pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F, mut lt: L) -> MinMaxResult<I::Item> +where + I: Iterator, + F: FnMut(&I::Item) -> K, + L: FnMut(&I::Item, &I::Item, &K, &K) -> bool, { let (mut min, mut max, mut min_key, mut max_key) = match it.next() { None => return MinMaxResult::NoElements, - Some(x) => { - match it.next() { - None => return MinMaxResult::OneElement(x), - Some(y) => { - let xk = key_for(&x); - let yk = key_for(&y); - if !lt(&y, &x, &yk, &xk) {(x, y, xk, yk)} else {(y, x, yk, xk)} + Some(x) => match it.next() { + None => return MinMaxResult::OneElement(x), + Some(y) => { + let xk = key_for(&x); + let yk = key_for(&y); + if !lt(&y, &x, &yk, &xk) { + (x, y, xk, yk) + } else { + (y, x, yk, xk) } } - } + }, }; loop { @@ -74,7 +75,7 @@ // for 2 elements. let first = match it.next() { None => break, - Some(x) => x + Some(x) => x, }; let second = match it.next() { None => { @@ -86,7 +87,7 @@ } break; } - Some(x) => x + Some(x) => x, }; let first_key = key_for(&first); let second_key = key_for(&second);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/multipeek_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/multipeek_impl.rs similarity index 71% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/multipeek_impl.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/multipeek_impl.rs index 8b49c69..fff1bc2 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/multipeek_impl.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/multipeek_impl.rs
@@ -1,14 +1,16 @@ -use std::iter::Fuse; -use alloc::collections::VecDeque; use crate::size_hint; -use crate::PeekingNext; #[cfg(doc)] use crate::Itertools; +use crate::PeekingNext; +use alloc::collections::VecDeque; +use std::iter::Fuse; /// See [`multipeek()`] for more information. #[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct MultiPeek<I> - where I: Iterator +where + I: Iterator, { iter: Fuse<I>, buf: VecDeque<I::Item>, @@ -20,17 +22,15 @@ /// /// [`IntoIterator`] enabled version of [`Itertools::multipeek`]. pub fn multipeek<I>(iterable: I) -> MultiPeek<I::IntoIter> - where I: IntoIterator +where + I: IntoIterator, { - MultiPeek { - iter: iterable.into_iter().fuse(), - buf: VecDeque::new(), - index: 0, - } + MultiPeek { iter: iterable.into_iter().fuse(), buf: VecDeque::new(), index: 0 } } impl<I> MultiPeek<I> - where I: Iterator +where + I: Iterator, { /// Reset the peeking “cursor” pub fn reset_peek(&mut self) { @@ -62,24 +62,31 @@ } impl<I> PeekingNext for MultiPeek<I> - where I: Iterator, +where + I: Iterator, { fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item> - where F: FnOnce(&Self::Item) -> bool + where + F: FnOnce(&Self::Item) -> bool, { if self.buf.is_empty() { if let Some(r) = self.peek() { - if !accept(r) { return None } + if !accept(r) { + return None; + } } - } else if let Some(r) = self.buf.get(0) { - if !accept(r) { return None } + } else if let Some(r) = self.buf.front() { + if !accept(r) { + return None; + } } self.next() } } impl<I> Iterator for MultiPeek<I> - where I: Iterator +where + I: Iterator, { type Item = I::Item; @@ -91,11 +98,15 @@ fn size_hint(&self) -> (usize, Option<usize>) { size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) } + + fn fold<B, F>(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + init = self.buf.into_iter().fold(init, &mut f); + self.iter.fold(init, f) + } } // Same size -impl<I> ExactSizeIterator for MultiPeek<I> - where I: ExactSizeIterator -{} - - +impl<I> ExactSizeIterator for MultiPeek<I> where I: ExactSizeIterator {}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/next_array.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/next_array.rs new file mode 100644 index 0000000..e6085b10 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/next_array.rs
@@ -0,0 +1,264 @@ +use core::mem::{self, MaybeUninit}; + +/// An array of at most `N` elements. +struct ArrayBuilder<T, const N: usize> { + /// The (possibly uninitialized) elements of the `ArrayBuilder`. + /// + /// # Safety + /// + /// The elements of `arr[..len]` are valid `T`s. + arr: [MaybeUninit<T>; N], + + /// The number of leading elements of `arr` that are valid `T`s, len <= N. + len: usize, +} + +impl<T, const N: usize> ArrayBuilder<T, N> { + /// Initializes a new, empty `ArrayBuilder`. + pub fn new() -> Self { + // SAFETY: The safety invariant of `arr` trivially holds for `len = 0`. + Self { arr: [(); N].map(|_| MaybeUninit::uninit()), len: 0 } + } + + /// Pushes `value` onto the end of the array. + /// + /// # Panics + /// + /// This panics if `self.len >= N`. + #[inline(always)] + pub fn push(&mut self, value: T) { + // PANICS: This will panic if `self.len >= N`. + let place = &mut self.arr[self.len]; + // SAFETY: The safety invariant of `self.arr` applies to elements at + // indices `0..self.len` — not to the element at `self.len`. Writing to + // the element at index `self.len` therefore does not violate the safety + // invariant of `self.arr`. Even if this line panics, we have not + // created any intermediate invalid state. + *place = MaybeUninit::new(value); + // Lemma: `self.len < N`. By invariant, `self.len <= N`. Above, we index + // into `self.arr`, which has size `N`, at index `self.len`. If `self.len == N` + // at that point, that index would be out-of-bounds, and the index + // operation would panic. Thus, `self.len != N`, and since `self.len <= N`, + // that means that `self.len < N`. + // + // PANICS: Since `self.len < N`, and since `N <= usize::MAX`, + // `self.len + 1 <= usize::MAX`, and so `self.len += 1` will not + // overflow. Overflow is the only panic condition of `+=`. + // + // SAFETY: + // - We are required to uphold the invariant that `self.len <= N`. Since, by the + // preceding lemma, `self.len < N` at this point in the code, `self.len += 1` + // results in `self.len <= N`. + // - We are required to uphold the invariant that `self.arr[..self.len]` are + // valid instances of `T`. Since this invariant already held when this method + // was called, and since we only increment `self.len` by 1 here, we only need + // to prove that the element at `self.arr[self.len]` (using the value of + // `self.len` before incrementing) is valid. Above, we construct `place` to + // point to `self.arr[self.len]`, and then initialize `*place` to + // `MaybeUninit::new(value)`, which is a valid `T` by construction. + self.len += 1; + } + + /// Consumes the elements in the `ArrayBuilder` and returns them as an array + /// `[T; N]`. + /// + /// If `self.len() < N`, this returns `None`. + pub fn take(&mut self) -> Option<[T; N]> { + if self.len == N { + // SAFETY: Decreasing the value of `self.len` cannot violate the + // safety invariant on `self.arr`. + self.len = 0; + + // SAFETY: Since `self.len` is 0, `self.arr` may safely contain + // uninitialized elements. + let arr = mem::replace(&mut self.arr, [(); N].map(|_| MaybeUninit::uninit())); + + Some(arr.map(|v| { + // SAFETY: We know that all elements of `arr` are valid because + // we checked that `len == N`. + unsafe { v.assume_init() } + })) + } else { + None + } + } +} + +impl<T, const N: usize> AsMut<[T]> for ArrayBuilder<T, N> { + fn as_mut(&mut self) -> &mut [T] { + let valid = &mut self.arr[..self.len]; + // SAFETY: By invariant on `self.arr`, the elements of `self.arr` at + // indices `0..self.len` are in a valid state. Since `valid` references + // only these elements, the safety precondition of + // `slice_assume_init_mut` is satisfied. + unsafe { slice_assume_init_mut(valid) } + } +} + +impl<T, const N: usize> Drop for ArrayBuilder<T, N> { + // We provide a non-trivial `Drop` impl, because the trivial impl would be a + // no-op; `MaybeUninit<T>` has no innate awareness of its own validity, and + // so it can only forget its contents. By leveraging the safety invariant of + // `self.arr`, we do know which elements of `self.arr` are valid, and can + // selectively run their destructors. + fn drop(&mut self) { + // SAFETY: + // - by invariant on `&mut [T]`, `self.as_mut()` is: + // - valid for reads and writes + // - properly aligned + // - non-null + // - the dropped `T` are valid for dropping; they do not have any additional + // library invariants that we've violated + // - no other pointers to `valid` exist (since we're in the context of `drop`) + unsafe { core::ptr::drop_in_place(self.as_mut()) } + } +} + +/// Assuming all the elements are initialized, get a mutable slice to them. +/// +/// # Safety +/// +/// The caller guarantees that the elements `T` referenced by `slice` are in a +/// valid state. +unsafe fn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] { + // SAFETY: Casting `&mut [MaybeUninit<T>]` to `&mut [T]` is sound, because + // `MaybeUninit<T>` is guaranteed to have the same size, alignment and ABI + // as `T`, and because the caller has guaranteed that `slice` is in the + // valid state. + unsafe { &mut *(slice as *mut [MaybeUninit<T>] as *mut [T]) } +} + +/// Equivalent to `it.next_array()`. +pub(crate) fn next_array<I, const N: usize>(it: &mut I) -> Option<[I::Item; N]> +where + I: Iterator, +{ + let mut builder = ArrayBuilder::new(); + for _ in 0..N { + builder.push(it.next()?); + } + builder.take() +} + +#[cfg(test)] +mod test { + use super::ArrayBuilder; + + #[test] + fn zero_len_take() { + let mut builder = ArrayBuilder::<(), 0>::new(); + let taken = builder.take(); + assert_eq!(taken, Some([(); 0])); + } + + #[test] + #[should_panic] + fn zero_len_push() { + let mut builder = ArrayBuilder::<(), 0>::new(); + builder.push(()); + } + + #[test] + fn push_4() { + let mut builder = ArrayBuilder::<(), 4>::new(); + assert_eq!(builder.take(), None); + + builder.push(()); + assert_eq!(builder.take(), None); + + builder.push(()); + assert_eq!(builder.take(), None); + + builder.push(()); + assert_eq!(builder.take(), None); + + builder.push(()); + assert_eq!(builder.take(), Some([(); 4])); + } + + #[test] + fn tracked_drop() { + use std::panic::{catch_unwind, AssertUnwindSafe}; + use std::sync::atomic::{AtomicU16, Ordering}; + + static DROPPED: AtomicU16 = AtomicU16::new(0); + + #[derive(Debug, PartialEq)] + struct TrackedDrop; + + impl Drop for TrackedDrop { + fn drop(&mut self) { + DROPPED.fetch_add(1, Ordering::Relaxed); + } + } + + { + let builder = ArrayBuilder::<TrackedDrop, 0>::new(); + assert_eq!(DROPPED.load(Ordering::Relaxed), 0); + drop(builder); + assert_eq!(DROPPED.load(Ordering::Relaxed), 0); + } + + { + let mut builder = ArrayBuilder::<TrackedDrop, 2>::new(); + builder.push(TrackedDrop); + assert_eq!(builder.take(), None); + assert_eq!(DROPPED.load(Ordering::Relaxed), 0); + drop(builder); + assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 1); + } + + { + let mut builder = ArrayBuilder::<TrackedDrop, 2>::new(); + builder.push(TrackedDrop); + builder.push(TrackedDrop); + assert!(matches!(builder.take(), Some(_))); + assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 2); + drop(builder); + assert_eq!(DROPPED.load(Ordering::Relaxed), 0); + } + + { + let mut builder = ArrayBuilder::<TrackedDrop, 2>::new(); + + builder.push(TrackedDrop); + builder.push(TrackedDrop); + + assert!(catch_unwind(AssertUnwindSafe(|| { + builder.push(TrackedDrop); + })) + .is_err()); + + assert_eq!(DROPPED.load(Ordering::Relaxed), 1); + + drop(builder); + + assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 3); + } + + { + let mut builder = ArrayBuilder::<TrackedDrop, 2>::new(); + + builder.push(TrackedDrop); + builder.push(TrackedDrop); + + assert!(catch_unwind(AssertUnwindSafe(|| { + builder.push(TrackedDrop); + })) + .is_err()); + + assert_eq!(DROPPED.load(Ordering::Relaxed), 1); + + assert!(matches!(builder.take(), Some(_))); + + assert_eq!(DROPPED.load(Ordering::Relaxed), 3); + + builder.push(TrackedDrop); + builder.push(TrackedDrop); + + assert!(matches!(builder.take(), Some(_))); + + assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 5); + } + } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/pad_tail.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/pad_tail.rs similarity index 67% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/pad_tail.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/pad_tail.rs index 248a432..326cfafcb 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/pad_tail.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/pad_tail.rs
@@ -1,5 +1,5 @@ -use std::iter::{Fuse, FusedIterator}; use crate::size_hint; +use std::iter::{Fuse, FusedIterator}; /// An iterator adaptor that pads a sequence to a minimum length by filling /// missing elements using a closure. @@ -25,20 +25,17 @@ /// Create a new `PadUsing` iterator. pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F> - where I: Iterator, - F: FnMut(usize) -> I::Item +where + I: Iterator, + F: FnMut(usize) -> I::Item, { - PadUsing { - iter: iter.fuse(), - min, - pos: 0, - filler, - } + PadUsing { iter: iter.fuse(), min, pos: 0, filler } } impl<I, F> Iterator for PadUsing<I, F> - where I: Iterator, - F: FnMut(usize) -> I::Item +where + I: Iterator, + F: FnMut(usize) -> I::Item, { type Item = I::Item; @@ -53,7 +50,7 @@ } else { None } - }, + } e => { self.pos += 1; e @@ -65,11 +62,24 @@ let tail = self.min.saturating_sub(self.pos); size_hint::max(self.iter.size_hint(), (tail, Some(tail))) } + + fn fold<B, G>(self, mut init: B, mut f: G) -> B + where + G: FnMut(B, Self::Item) -> B, + { + let mut pos = self.pos; + init = self.iter.fold(init, |acc, item| { + pos += 1; + f(acc, item) + }); + (pos..self.min).map(self.filler).fold(init, f) + } } impl<I, F> DoubleEndedIterator for PadUsing<I, F> - where I: DoubleEndedIterator + ExactSizeIterator, - F: FnMut(usize) -> I::Item +where + I: DoubleEndedIterator + ExactSizeIterator, + F: FnMut(usize) -> I::Item, { fn next_back(&mut self) -> Option<Self::Item> { if self.min == 0 { @@ -82,15 +92,26 @@ Some((self.filler)(self.min)) } } + + fn rfold<B, G>(self, mut init: B, mut f: G) -> B + where + G: FnMut(B, Self::Item) -> B, + { + init = (self.iter.len()..self.min).map(self.filler).rfold(init, &mut f); + self.iter.rfold(init, f) + } } impl<I, F> ExactSizeIterator for PadUsing<I, F> - where I: ExactSizeIterator, - F: FnMut(usize) -> I::Item -{} - +where + I: ExactSizeIterator, + F: FnMut(usize) -> I::Item, +{ +} impl<I, F> FusedIterator for PadUsing<I, F> - where I: FusedIterator, - F: FnMut(usize) -> I::Item -{} +where + I: FusedIterator, + F: FnMut(usize) -> I::Item, +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peek_nth.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peek_nth.rs new file mode 100644 index 0000000..e3f3f23 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peek_nth.rs
@@ -0,0 +1,176 @@ +use crate::size_hint; +use crate::PeekingNext; +use alloc::collections::VecDeque; +use std::iter::Fuse; + +/// See [`peek_nth()`] for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct PeekNth<I> +where + I: Iterator, +{ + iter: Fuse<I>, + buf: VecDeque<I::Item>, +} + +/// A drop-in replacement for [`std::iter::Peekable`] which adds a `peek_nth` +/// method allowing the user to `peek` at a value several iterations forward +/// without advancing the base iterator. +/// +/// This differs from `multipeek` in that subsequent calls to `peek` or +/// `peek_nth` will always return the same value until `next` is called +/// (making `reset_peek` unnecessary). +pub fn peek_nth<I>(iterable: I) -> PeekNth<I::IntoIter> +where + I: IntoIterator, +{ + PeekNth { iter: iterable.into_iter().fuse(), buf: VecDeque::new() } +} + +impl<I> PeekNth<I> +where + I: Iterator, +{ + /// Works exactly like the `peek` method in [`std::iter::Peekable`]. + pub fn peek(&mut self) -> Option<&I::Item> { + self.peek_nth(0) + } + + /// Works exactly like the `peek_mut` method in [`std::iter::Peekable`]. + pub fn peek_mut(&mut self) -> Option<&mut I::Item> { + self.peek_nth_mut(0) + } + + /// Returns a reference to the `nth` value without advancing the iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use itertools::peek_nth; + /// + /// let xs = vec![1, 2, 3]; + /// let mut iter = peek_nth(xs.into_iter()); + /// + /// assert_eq!(iter.peek_nth(0), Some(&1)); + /// assert_eq!(iter.next(), Some(1)); + /// + /// // The iterator does not advance even if we call `peek_nth` multiple times + /// assert_eq!(iter.peek_nth(0), Some(&2)); + /// assert_eq!(iter.peek_nth(1), Some(&3)); + /// assert_eq!(iter.next(), Some(2)); + /// + /// // Calling `peek_nth` past the end of the iterator will return `None` + /// assert_eq!(iter.peek_nth(1), None); + /// ``` + pub fn peek_nth(&mut self, n: usize) -> Option<&I::Item> { + let unbuffered_items = (n + 1).saturating_sub(self.buf.len()); + + self.buf.extend(self.iter.by_ref().take(unbuffered_items)); + + self.buf.get(n) + } + + /// Returns a mutable reference to the `nth` value without advancing the + /// iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use itertools::peek_nth; + /// + /// let xs = vec![1, 2, 3, 4, 5]; + /// let mut iter = peek_nth(xs.into_iter()); + /// + /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 1)); + /// assert_eq!(iter.next(), Some(1)); + /// + /// // The iterator does not advance even if we call `peek_nth_mut` multiple times + /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 2)); + /// assert_eq!(iter.peek_nth_mut(1), Some(&mut 3)); + /// assert_eq!(iter.next(), Some(2)); + /// + /// // Peek into the iterator and set the value behind the mutable reference. + /// if let Some(p) = iter.peek_nth_mut(1) { + /// assert_eq!(*p, 4); + /// *p = 9; + /// } + /// + /// // The value we put in reappears as the iterator continues. + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), Some(9)); + /// + /// // Calling `peek_nth_mut` past the end of the iterator will return `None` + /// assert_eq!(iter.peek_nth_mut(1), None); + /// ``` + pub fn peek_nth_mut(&mut self, n: usize) -> Option<&mut I::Item> { + let unbuffered_items = (n + 1).saturating_sub(self.buf.len()); + + self.buf.extend(self.iter.by_ref().take(unbuffered_items)); + + self.buf.get_mut(n) + } + + /// Works exactly like the `next_if` method in [`std::iter::Peekable`]. + pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> { + match self.next() { + Some(item) if func(&item) => Some(item), + Some(item) => { + self.buf.push_front(item); + None + } + _ => None, + } + } + + /// Works exactly like the `next_if_eq` method in [`std::iter::Peekable`]. + pub fn next_if_eq<T>(&mut self, expected: &T) -> Option<I::Item> + where + T: ?Sized, + I::Item: PartialEq<T>, + { + self.next_if(|next| next == expected) + } +} + +impl<I> Iterator for PeekNth<I> +where + I: Iterator, +{ + type Item = I::Item; + + fn next(&mut self) -> Option<Self::Item> { + self.buf.pop_front().or_else(|| self.iter.next()) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) + } + + fn fold<B, F>(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + init = self.buf.into_iter().fold(init, &mut f); + self.iter.fold(init, f) + } +} + +impl<I> ExactSizeIterator for PeekNth<I> where I: ExactSizeIterator {} + +impl<I> PeekingNext for PeekNth<I> +where + I: Iterator, +{ + fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item> + where + F: FnOnce(&Self::Item) -> bool, + { + self.peek().filter(|item| accept(item))?; + self.next() + } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peeking_take_while.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peeking_take_while.rs similarity index 74% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peeking_take_while.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peeking_take_while.rs index 3a37228..f3259a9 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peeking_take_while.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peeking_take_while.rs
@@ -1,7 +1,8 @@ -use std::iter::Peekable; use crate::PutBack; #[cfg(feature = "use_alloc")] use crate::PutBackN; +use crate::RepeatN; +use std::iter::Peekable; /// An iterator that allows peeking at an element before deciding to accept it. /// @@ -10,31 +11,36 @@ /// /// This is implemented by peeking adaptors like peekable and put back, /// but also by a few iterators that can be peeked natively, like the slice’s -/// by reference iterator (`std::slice::Iter`). -pub trait PeekingNext : Iterator { +/// by reference iterator ([`std::slice::Iter`]). +pub trait PeekingNext: Iterator { /// Pass a reference to the next iterator element to the closure `accept`; - /// if `accept` returns true, return it as the next element, - /// else None. + /// if `accept` returns `true`, return it as the next element, + /// else `None`. fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item> - where Self: Sized, - F: FnOnce(&Self::Item) -> bool; + where + Self: Sized, + F: FnOnce(&Self::Item) -> bool; } -impl<'a, I> PeekingNext for &'a mut I - where I: PeekingNext, +impl<I> PeekingNext for &mut I +where + I: PeekingNext, { fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item> - where F: FnOnce(&Self::Item) -> bool + where + F: FnOnce(&Self::Item) -> bool, { (*self).peeking_next(accept) } } impl<I> PeekingNext for Peekable<I> - where I: Iterator, +where + I: Iterator, { fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item> - where F: FnOnce(&Self::Item) -> bool + where + F: FnOnce(&Self::Item) -> bool, { if let Some(r) = self.peek() { if !accept(r) { @@ -46,10 +52,12 @@ } impl<I> PeekingNext for PutBack<I> - where I: Iterator, +where + I: Iterator, { fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item> - where F: FnOnce(&Self::Item) -> bool + where + F: FnOnce(&Self::Item) -> bool, { if let Some(r) = self.next() { if !accept(&r) { @@ -65,10 +73,12 @@ #[cfg(feature = "use_alloc")] impl<I> PeekingNext for PutBackN<I> - where I: Iterator, +where + I: Iterator, { fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item> - where F: FnOnce(&Self::Item) -> bool + where + F: FnOnce(&Self::Item) -> bool, { if let Some(r) = self.next() { if !accept(&r) { @@ -82,39 +92,51 @@ } } +impl<T: Clone> PeekingNext for RepeatN<T> { + fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item> + where + F: FnOnce(&Self::Item) -> bool, + { + let r = self.elt.as_ref()?; + if !accept(r) { + return None; + } + self.next() + } +} + /// An iterator adaptor that takes items while a closure returns `true`. /// /// See [`.peeking_take_while()`](crate::Itertools::peeking_take_while) /// for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct PeekingTakeWhile<'a, I: 'a, F> - where I: Iterator, +pub struct PeekingTakeWhile<'a, I, F> +where + I: Iterator + 'a, { iter: &'a mut I, f: F, } -impl<'a, I: 'a, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F> +impl<'a, I, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F> where - I: Iterator + std::fmt::Debug, + I: Iterator + std::fmt::Debug + 'a, { debug_fmt_fields!(PeekingTakeWhile, iter); } /// Create a `PeekingTakeWhile` pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F> - where I: Iterator, +where + I: Iterator, { - PeekingTakeWhile { - iter, - f, - } + PeekingTakeWhile { iter, f } } -impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F> - where I: PeekingNext, - F: FnMut(&I::Item) -> bool, - +impl<I, F> Iterator for PeekingTakeWhile<'_, I, F> +where + I: PeekingNext, + F: FnMut(&I::Item) -> bool, { type Item = I::Item; fn next(&mut self) -> Option<Self::Item> { @@ -126,12 +148,14 @@ } } -impl<'a, I, F> PeekingNext for PeekingTakeWhile<'a, I, F> - where I: PeekingNext, - F: FnMut(&I::Item) -> bool, +impl<I, F> PeekingNext for PeekingTakeWhile<'_, I, F> +where + I: PeekingNext, + F: FnMut(&I::Item) -> bool, { fn peeking_next<G>(&mut self, g: G) -> Option<Self::Item> - where G: FnOnce(&Self::Item) -> bool, + where + G: FnOnce(&Self::Item) -> bool, { let f = &mut self.f; self.iter.peeking_next(|r| f(r) && g(r)) @@ -174,4 +198,4 @@ // cloning a Rev has no extra overhead; peekable and put backs are never DEI. peeking_next_by_clone! { [I: Clone + PeekingNext + DoubleEndedIterator] - ::std::iter::Rev<I> } +::std::iter::Rev<I> }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/permutations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/permutations.rs new file mode 100644 index 0000000..dffe829 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/permutations.rs
@@ -0,0 +1,174 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use std::fmt; +use std::iter::once; +use std::iter::FusedIterator; + +use super::lazy_buffer::LazyBuffer; +use crate::size_hint::{self, SizeHint}; + +/// An iterator adaptor that iterates through all the `k`-permutations of the +/// elements from an iterator. +/// +/// See [`.permutations()`](crate::Itertools::permutations) for +/// more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Permutations<I: Iterator> { + vals: LazyBuffer<I>, + state: PermutationState, +} + +impl<I> Clone for Permutations<I> +where + I: Clone + Iterator, + I::Item: Clone, +{ + clone_fields!(vals, state); +} + +#[derive(Clone, Debug)] +enum PermutationState { + /// No permutation generated yet. + Start { k: usize }, + /// Values from the iterator are not fully loaded yet so `n` is still + /// unknown. + Buffered { k: usize, min_n: usize }, + /// All values from the iterator are known so `n` is known. + Loaded { indices: Box<[usize]>, cycles: Box<[usize]> }, + /// No permutation left to generate. + End, +} + +impl<I> fmt::Debug for Permutations<I> +where + I: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(Permutations, vals, state); +} + +pub fn permutations<I: Iterator>(iter: I, k: usize) -> Permutations<I> { + Permutations { vals: LazyBuffer::new(iter), state: PermutationState::Start { k } } +} + +impl<I> Iterator for Permutations<I> +where + I: Iterator, + I::Item: Clone, +{ + type Item = Vec<I::Item>; + + fn next(&mut self) -> Option<Self::Item> { + let Self { vals, state } = self; + match state { + PermutationState::Start { k: 0 } => { + *state = PermutationState::End; + Some(Vec::new()) + } + &mut PermutationState::Start { k } => { + vals.prefill(k); + if vals.len() != k { + *state = PermutationState::End; + return None; + } + *state = PermutationState::Buffered { k, min_n: k }; + Some(vals[0..k].to_vec()) + } + PermutationState::Buffered { ref k, min_n } => { + if vals.get_next() { + let item = (0..*k - 1).chain(once(*min_n)).map(|i| vals[i].clone()).collect(); + *min_n += 1; + Some(item) + } else { + let n = *min_n; + let prev_iteration_count = n - *k + 1; + let mut indices: Box<[_]> = (0..n).collect(); + let mut cycles: Box<[_]> = (n - k..n).rev().collect(); + // Advance the state to the correct point. + for _ in 0..prev_iteration_count { + if advance(&mut indices, &mut cycles) { + *state = PermutationState::End; + return None; + } + } + let item = vals.get_at(&indices[0..*k]); + *state = PermutationState::Loaded { indices, cycles }; + Some(item) + } + } + PermutationState::Loaded { indices, cycles } => { + if advance(indices, cycles) { + *state = PermutationState::End; + return None; + } + let k = cycles.len(); + Some(vals.get_at(&indices[0..k])) + } + PermutationState::End => None, + } + } + + fn count(self) -> usize { + let Self { vals, state } = self; + let n = vals.count(); + state.size_hint_for(n).1.unwrap() + } + + fn size_hint(&self) -> SizeHint { + let (mut low, mut upp) = self.vals.size_hint(); + low = self.state.size_hint_for(low).0; + upp = upp.and_then(|n| self.state.size_hint_for(n).1); + (low, upp) + } +} + +impl<I> FusedIterator for Permutations<I> +where + I: Iterator, + I::Item: Clone, +{ +} + +fn advance(indices: &mut [usize], cycles: &mut [usize]) -> bool { + let n = indices.len(); + let k = cycles.len(); + // NOTE: if `cycles` are only zeros, then we reached the last permutation. + for i in (0..k).rev() { + if cycles[i] == 0 { + cycles[i] = n - i - 1; + indices[i..].rotate_left(1); + } else { + let swap_index = n - cycles[i]; + indices.swap(i, swap_index); + cycles[i] -= 1; + return false; + } + } + true +} + +impl PermutationState { + fn size_hint_for(&self, n: usize) -> SizeHint { + // At the beginning, there are `n!/(n-k)!` items to come. + let at_start = |n, k| { + debug_assert!(n >= k); + let total = (n - k + 1..=n).try_fold(1usize, |acc, i| acc.checked_mul(i)); + (total.unwrap_or(usize::MAX), total) + }; + match *self { + Self::Start { k } if n < k => (0, Some(0)), + Self::Start { k } => at_start(n, k), + Self::Buffered { k, min_n } => { + // Same as `Start` minus the previously generated items. + size_hint::sub_scalar(at_start(n, k), min_n - k + 1) + } + Self::Loaded { ref indices, ref cycles } => { + let count = cycles.iter().enumerate().try_fold(0usize, |acc, (i, &c)| { + acc.checked_mul(indices.len() - i).and_then(|count| count.checked_add(c)) + }); + (count.unwrap_or(usize::MAX), count) + } + Self::End => (0, Some(0)), + } + } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/powerset.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/powerset.rs new file mode 100644 index 0000000..f9c75e95 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/powerset.rs
@@ -0,0 +1,131 @@ +use alloc::vec::Vec; +use std::fmt; +use std::iter::FusedIterator; + +use super::combinations::{combinations, Combinations}; +use crate::adaptors::checked_binomial; +use crate::size_hint::{self, SizeHint}; + +/// An iterator to iterate through the powerset of the elements from an +/// iterator. +/// +/// See [`.powerset()`](crate::Itertools::powerset) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Powerset<I: Iterator> { + combs: Combinations<I>, +} + +impl<I> Clone for Powerset<I> +where + I: Clone + Iterator, + I::Item: Clone, +{ + clone_fields!(combs); +} + +impl<I> fmt::Debug for Powerset<I> +where + I: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(Powerset, combs); +} + +/// Create a new `Powerset` from a clonable iterator. +pub fn powerset<I>(src: I) -> Powerset<I> +where + I: Iterator, + I::Item: Clone, +{ + Powerset { combs: combinations(src, 0) } +} + +impl<I: Iterator> Powerset<I> { + /// Returns true if `k` has been incremented, false otherwise. + fn increment_k(&mut self) -> bool { + if self.combs.k() < self.combs.n() || self.combs.k() == 0 { + self.combs.reset(self.combs.k() + 1); + true + } else { + false + } + } +} + +impl<I> Iterator for Powerset<I> +where + I: Iterator, + I::Item: Clone, +{ + type Item = Vec<I::Item>; + + fn next(&mut self) -> Option<Self::Item> { + if let Some(elt) = self.combs.next() { + Some(elt) + } else if self.increment_k() { + self.combs.next() + } else { + None + } + } + + fn nth(&mut self, mut n: usize) -> Option<Self::Item> { + loop { + match self.combs.try_nth(n) { + Ok(item) => return Some(item), + Err(steps) => { + if !self.increment_k() { + return None; + } + n -= steps; + } + } + } + } + + fn size_hint(&self) -> SizeHint { + let k = self.combs.k(); + // Total bounds for source iterator. + let (n_min, n_max) = self.combs.src().size_hint(); + let low = remaining_for(n_min, k).unwrap_or(usize::MAX); + let upp = n_max.and_then(|n| remaining_for(n, k)); + size_hint::add(self.combs.size_hint(), (low, upp)) + } + + fn count(self) -> usize { + let k = self.combs.k(); + let (n, combs_count) = self.combs.n_and_count(); + combs_count + remaining_for(n, k).unwrap() + } + + fn fold<B, F>(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + let mut it = self.combs; + if it.k() == 0 { + init = it.by_ref().fold(init, &mut f); + it.reset(1); + } + init = it.by_ref().fold(init, &mut f); + // n is now known for sure because k >= 1 and all k-combinations have been + // generated. + for k in it.k() + 1..=it.n() { + it.reset(k); + init = it.by_ref().fold(init, &mut f); + } + init + } +} + +impl<I> FusedIterator for Powerset<I> +where + I: Iterator, + I::Item: Clone, +{ +} + +fn remaining_for(n: usize, k: usize) -> Option<usize> { + (k + 1..=n).try_fold(0usize, |sum, i| sum.checked_add(checked_binomial(n, i)?)) +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/process_results_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/process_results_impl.rs new file mode 100644 index 0000000..4478f217 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/process_results_impl.rs
@@ -0,0 +1,105 @@ +#[cfg(doc)] +use crate::Itertools; + +/// An iterator that produces only the `T` values as long as the +/// inner iterator produces `Ok(T)`. +/// +/// Used by [`process_results`](crate::process_results), see its docs +/// for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Debug)] +pub struct ProcessResults<'a, I, E: 'a> { + error: &'a mut Result<(), E>, + iter: I, +} + +impl<I, E> ProcessResults<'_, I, E> { + #[inline(always)] + fn next_body<T>(&mut self, item: Option<Result<T, E>>) -> Option<T> { + match item { + Some(Ok(x)) => Some(x), + Some(Err(e)) => { + *self.error = Err(e); + None + } + None => None, + } + } +} + +impl<I, T, E> Iterator for ProcessResults<'_, I, E> +where + I: Iterator<Item = Result<T, E>>, +{ + type Item = T; + + fn next(&mut self) -> Option<Self::Item> { + let item = self.iter.next(); + self.next_body(item) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (0, self.iter.size_hint().1) + } + + fn fold<B, F>(mut self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + let error = self.error; + self.iter + .try_fold(init, |acc, opt| match opt { + Ok(x) => Ok(f(acc, x)), + Err(e) => { + *error = Err(e); + Err(acc) + } + }) + .unwrap_or_else(|e| e) + } +} + +impl<I, T, E> DoubleEndedIterator for ProcessResults<'_, I, E> +where + I: Iterator<Item = Result<T, E>>, + I: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option<Self::Item> { + let item = self.iter.next_back(); + self.next_body(item) + } + + fn rfold<B, F>(mut self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + let error = self.error; + self.iter + .try_rfold(init, |acc, opt| match opt { + Ok(x) => Ok(f(acc, x)), + Err(e) => { + *error = Err(e); + Err(acc) + } + }) + .unwrap_or_else(|e| e) + } +} + +/// “Lift” a function of the values of an iterator so that it can process +/// an iterator of `Result` values instead. +/// +/// [`IntoIterator`] enabled version of [`Itertools::process_results`]. +pub fn process_results<I, F, T, E, R>(iterable: I, processor: F) -> Result<R, E> +where + I: IntoIterator<Item = Result<T, E>>, + F: FnOnce(ProcessResults<I::IntoIter, E>) -> R, +{ + let iter = iterable.into_iter(); + let mut error = Ok(()); + + let result = processor(ProcessResults { error: &mut error, iter }); + + error.map(|_| result) +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/put_back_n_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/put_back_n_impl.rs similarity index 75% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/put_back_n_impl.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/put_back_n_impl.rs index 60ea8e6..f508f40 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/put_back_n_impl.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/put_back_n_impl.rs
@@ -7,6 +7,7 @@ /// /// Iterator element type is `I::Item`. #[derive(Debug, Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct PutBackN<I: Iterator> { top: Vec<I::Item>, iter: I, @@ -17,16 +18,15 @@ /// /// Iterator element type is `I::Item`. pub fn put_back_n<I>(iterable: I) -> PutBackN<I::IntoIter> - where I: IntoIterator +where + I: IntoIterator, { - PutBackN { - top: Vec::new(), - iter: iterable.into_iter(), - } + PutBackN { top: Vec::new(), iter: iterable.into_iter() } } impl<I: Iterator> PutBackN<I> { - /// Puts x in front of the iterator. + /// Puts `x` in front of the iterator. + /// /// The values are yielded in order of the most recently put back /// values first. /// @@ -57,5 +57,12 @@ fn size_hint(&self) -> (usize, Option<usize>) { size_hint::add_scalar(self.iter.size_hint(), self.top.len()) } -} + fn fold<B, F>(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + init = self.top.into_iter().rfold(init, &mut f); + self.iter.fold(init, f) + } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/rciter_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/rciter_impl.rs similarity index 85% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/rciter_impl.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/rciter_impl.rs index 7298350a..e709dbf0 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/rciter_impl.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/rciter_impl.rs
@@ -1,10 +1,10 @@ - -use std::iter::{FusedIterator, IntoIterator}; use alloc::rc::Rc; use std::cell::RefCell; +use std::iter::{FusedIterator, IntoIterator}; /// A wrapper for `Rc<RefCell<I>>`, that implements the `Iterator` trait. #[derive(Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct RcIter<I> { /// The boxed iterator. pub rciter: Rc<RefCell<I>>, @@ -15,9 +15,9 @@ /// The returned `RcIter` can be cloned, and each clone will refer back to the /// same original iterator. /// -/// `RcIter` allows doing interesting things like using `.zip()` on an iterator with -/// itself, at the cost of runtime borrow checking which may have a performance -/// penalty. +/// `RcIter` allows doing interesting things like using `.zip()` on an iterator +/// with itself, at the cost of runtime borrow checking which may have a +/// performance penalty. /// /// Iterator element type is `Self::Item`. /// @@ -45,7 +45,8 @@ /// `.next()`, i.e. if it somehow participates in an “iterator knot” /// where it is an adaptor of itself. pub fn rciter<I>(iterable: I) -> RcIter<I::IntoIter> - where I: IntoIterator +where + I: IntoIterator, { RcIter { rciter: Rc::new(RefCell::new(iterable.into_iter())) } } @@ -55,7 +56,8 @@ } impl<A, I> Iterator for RcIter<I> - where I: Iterator<Item = A> +where + I: Iterator<Item = A>, { type Item = A; #[inline] @@ -73,7 +75,8 @@ } impl<I> DoubleEndedIterator for RcIter<I> - where I: DoubleEndedIterator +where + I: DoubleEndedIterator, { #[inline] fn next_back(&mut self) -> Option<Self::Item> { @@ -82,8 +85,9 @@ } /// Return an iterator from `&RcIter<I>` (by simply cloning it). -impl<'a, I> IntoIterator for &'a RcIter<I> - where I: Iterator +impl<I> IntoIterator for &RcIter<I> +where + I: Iterator, { type Item = I::Item; type IntoIter = RcIter<I>; @@ -93,7 +97,4 @@ } } - -impl<A, I> FusedIterator for RcIter<I> - where I: FusedIterator<Item = A> -{} +impl<A, I> FusedIterator for RcIter<I> where I: FusedIterator<Item = A> {}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/repeatn.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/repeatn.rs new file mode 100644 index 0000000..b2037e15 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/repeatn.rs
@@ -0,0 +1,80 @@ +use std::iter::FusedIterator; + +/// An iterator that produces *n* repetitions of an element. +/// +/// See [`repeat_n()`](crate::repeat_n) for more information. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[derive(Clone, Debug)] +pub struct RepeatN<A> { + pub(crate) elt: Option<A>, + n: usize, +} + +/// Create an iterator that produces `n` repetitions of `element`. +pub fn repeat_n<A>(element: A, n: usize) -> RepeatN<A> +where + A: Clone, +{ + if n == 0 { + RepeatN { elt: None, n } + } else { + RepeatN { elt: Some(element), n } + } +} + +impl<A> Iterator for RepeatN<A> +where + A: Clone, +{ + type Item = A; + + fn next(&mut self) -> Option<Self::Item> { + if self.n > 1 { + self.n -= 1; + self.elt.as_ref().cloned() + } else { + self.n = 0; + self.elt.take() + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (self.n, Some(self.n)) + } + + fn fold<B, F>(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + match self { + Self { elt: Some(elt), n } => { + debug_assert!(n > 0); + init = (1..n).map(|_| elt.clone()).fold(init, &mut f); + f(init, elt) + } + _ => init, + } + } +} + +impl<A> DoubleEndedIterator for RepeatN<A> +where + A: Clone, +{ + #[inline] + fn next_back(&mut self) -> Option<Self::Item> { + self.next() + } + + #[inline] + fn rfold<B, F>(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.fold(init, f) + } +} + +impl<A> ExactSizeIterator for RepeatN<A> where A: Clone {} + +impl<A> FusedIterator for RepeatN<A> where A: Clone {}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/size_hint.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/size_hint.rs similarity index 72% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/size_hint.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/size_hint.rs index 71ea1412..dbaaa77e 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/size_hint.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/size_hint.rs
@@ -1,9 +1,6 @@ //! Arithmetic on `Iterator.size_hint()` values. -//! -use std::usize; use std::cmp; -use std::u32; /// `SizeHint` is the return type of `Iterator::size_hint()`. pub type SizeHint = (usize, Option<usize>); @@ -31,7 +28,6 @@ /// Subtract `x` correctly from a `SizeHint`. #[inline] -#[allow(dead_code)] pub fn sub_scalar(sh: SizeHint, x: usize) -> SizeHint { let (mut low, mut hi) = sh; low = low.saturating_sub(x); @@ -39,22 +35,7 @@ (low, hi) } - /// Multiply `SizeHint` correctly -/// -/// ```ignore -/// use std::usize; -/// use itertools::size_hint; -/// -/// assert_eq!(size_hint::mul((3, Some(4)), (3, Some(4))), -/// (9, Some(16))); -/// -/// assert_eq!(size_hint::mul((3, Some(4)), (usize::MAX, None)), -/// (usize::MAX, None)); -/// -/// assert_eq!(size_hint::mul((3, None), (0, Some(0))), -/// (0, Some(0))); -/// ``` #[inline] pub fn mul(a: SizeHint, b: SizeHint) -> SizeHint { let low = a.0.saturating_mul(b.0); @@ -75,20 +56,6 @@ (low, hi) } -/// Raise `base` correctly by a `SizeHint` exponent. -#[inline] -pub fn pow_scalar_base(base: usize, exp: SizeHint) -> SizeHint { - let exp_low = cmp::min(exp.0, u32::MAX as usize) as u32; - let low = base.saturating_pow(exp_low); - - let hi = exp.1.and_then(|exp| { - let exp_hi = cmp::min(exp, u32::MAX as usize) as u32; - base.checked_pow(exp_hi) - }); - - (low, hi) -} - /// Return the maximum #[inline] pub fn max(a: SizeHint, b: SizeHint) -> SizeHint { @@ -117,3 +84,10 @@ }; (lower, upper) } + +#[test] +fn mul_size_hints() { + assert_eq!(mul((3, Some(4)), (3, Some(4))), (9, Some(16))); + assert_eq!(mul((3, Some(4)), (usize::MAX, None)), (usize::MAX, None)); + assert_eq!(mul((3, None), (0, Some(0))), (0, Some(0))); +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/sources.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/sources.rs similarity index 62% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/sources.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/sources.rs index 3877ce3c..936458d 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/sources.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/sources.rs
@@ -5,62 +5,6 @@ use std::fmt; use std::mem; -/// See [`repeat_call`](crate::repeat_call) for more information. -#[derive(Clone)] -#[deprecated(note="Use std repeat_with() instead", since="0.8.0")] -pub struct RepeatCall<F> { - f: F, -} - -impl<F> fmt::Debug for RepeatCall<F> -{ - debug_fmt_fields!(RepeatCall, ); -} - -/// An iterator source that produces elements indefinitely by calling -/// a given closure. -/// -/// Iterator element type is the return type of the closure. -/// -/// ``` -/// use itertools::repeat_call; -/// use itertools::Itertools; -/// use std::collections::BinaryHeap; -/// -/// let mut heap = BinaryHeap::from(vec![2, 5, 3, 7, 8]); -/// -/// // extract each element in sorted order -/// for element in repeat_call(|| heap.pop()).while_some() { -/// print!("{}", element); -/// } -/// -/// itertools::assert_equal( -/// repeat_call(|| 1).take(5), -/// vec![1, 1, 1, 1, 1] -/// ); -/// ``` -#[deprecated(note="Use std repeat_with() instead", since="0.8.0")] -pub fn repeat_call<F, A>(function: F) -> RepeatCall<F> - where F: FnMut() -> A -{ - RepeatCall { f: function } -} - -impl<A, F> Iterator for RepeatCall<F> - where F: FnMut() -> A -{ - type Item = A; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - Some((self.f)()) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - (usize::max_value(), None) - } -} - /// Creates a new unfold source with the specified closure as the "iterator /// function" and an initial state to eventually pass to the closure /// @@ -97,17 +41,20 @@ /// vec![1, 1, 2, 3, 5, 8, 13, 21]); /// assert_eq!(fibonacci.last(), Some(2_971_215_073)) /// ``` +#[deprecated( + note = "Use [std::iter::from_fn](https://doc.rust-lang.org/std/iter/fn.from_fn.html) instead", + since = "0.13.0" +)] pub fn unfold<A, St, F>(initial_state: St, f: F) -> Unfold<St, F> - where F: FnMut(&mut St) -> Option<A> +where + F: FnMut(&mut St) -> Option<A>, { - Unfold { - f, - state: initial_state, - } + Unfold { f, state: initial_state } } impl<St, F> fmt::Debug for Unfold<St, F> - where St: fmt::Debug, +where + St: fmt::Debug, { debug_fmt_fields!(Unfold, state); } @@ -115,6 +62,10 @@ /// See [`unfold`](crate::unfold) for more information. #[derive(Clone)] #[must_use = "iterators are lazy and do nothing unless consumed"] +#[deprecated( + note = "Use [std::iter::FromFn](https://doc.rust-lang.org/std/iter/struct.FromFn.html) instead", + since = "0.13.0" +)] pub struct Unfold<St, F> { f: F, /// Internal state that will be passed to the closure on the next iteration @@ -122,7 +73,8 @@ } impl<A, St, F> Iterator for Unfold<St, F> - where F: FnMut(&mut St) -> Option<A> +where + F: FnMut(&mut St) -> Option<A>, { type Item = A; @@ -144,13 +96,15 @@ } impl<St, F> fmt::Debug for Iterate<St, F> - where St: fmt::Debug, +where + St: fmt::Debug, { debug_fmt_fields!(Iterate, state); } impl<St, F> Iterator for Iterate<St, F> - where F: FnMut(&St) -> St +where + F: FnMut(&St) -> St, { type Item = St; @@ -162,22 +116,34 @@ #[inline] fn size_hint(&self) -> (usize, Option<usize>) { - (usize::max_value(), None) + (usize::MAX, None) } } -/// Creates a new iterator that infinitely applies function to value and yields results. +/// Creates a new iterator that infinitely applies function to value and yields +/// results. /// /// ``` /// use itertools::iterate; /// -/// itertools::assert_equal(iterate(1, |&i| i * 3).take(5), vec![1, 3, 9, 27, 81]); +/// itertools::assert_equal(iterate(1, |i| i % 3 + 1).take(5), vec![1, 2, 3, 1, 2]); /// ``` +/// +/// **Panics** if compute the next value does. +/// +/// ```should_panic +/// # use itertools::iterate; +/// let mut it = iterate(25u32, |x| x - 10).take_while(|&x| x > 10); +/// assert_eq!(it.next(), Some(25)); // `Iterate` holds 15. +/// assert_eq!(it.next(), Some(15)); // `Iterate` holds 5. +/// it.next(); // `5 - 10` overflows. +/// ``` +/// +/// You can alternatively use [`core::iter::successors`] as it better describes +/// a finite iterator. pub fn iterate<St, F>(initial_value: St, f: F) -> Iterate<St, F> - where F: FnMut(&St) -> St +where + F: FnMut(&St) -> St, { - Iterate { - state: initial_value, - f, - } + Iterate { state: initial_value, f } }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/take_while_inclusive.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/take_while_inclusive.rs new file mode 100644 index 0000000..abd5c1d --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/take_while_inclusive.rs
@@ -0,0 +1,92 @@ +use core::iter::FusedIterator; +use std::fmt; + +/// An iterator adaptor that consumes elements while the given predicate is +/// `true`, including the element for which the predicate first returned +/// `false`. +/// +/// See [`.take_while_inclusive()`](crate::Itertools::take_while_inclusive) +/// for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Clone)] +pub struct TakeWhileInclusive<I, F> { + iter: I, + predicate: F, + done: bool, +} + +impl<I, F> TakeWhileInclusive<I, F> +where + I: Iterator, + F: FnMut(&I::Item) -> bool, +{ + /// Create a new [`TakeWhileInclusive`] from an iterator and a predicate. + pub(crate) fn new(iter: I, predicate: F) -> Self { + Self { iter, predicate, done: false } + } +} + +impl<I, F> fmt::Debug for TakeWhileInclusive<I, F> +where + I: Iterator + fmt::Debug, +{ + debug_fmt_fields!(TakeWhileInclusive, iter, done); +} + +impl<I, F> Iterator for TakeWhileInclusive<I, F> +where + I: Iterator, + F: FnMut(&I::Item) -> bool, +{ + type Item = I::Item; + + fn next(&mut self) -> Option<Self::Item> { + if self.done { + None + } else { + self.iter.next().map(|item| { + if !(self.predicate)(&item) { + self.done = true; + } + item + }) + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + if self.done { + (0, Some(0)) + } else { + (0, self.iter.size_hint().1) + } + } + + fn fold<B, Fold>(mut self, init: B, mut f: Fold) -> B + where + Fold: FnMut(B, Self::Item) -> B, + { + if self.done { + init + } else { + let predicate = &mut self.predicate; + self.iter + .try_fold(init, |mut acc, item| { + let is_ok = predicate(&item); + acc = f(acc, item); + if is_ok { + Ok(acc) + } else { + Err(acc) + } + }) + .unwrap_or_else(|err| err) + } + } +} + +impl<I, F> FusedIterator for TakeWhileInclusive<I, F> +where + I: Iterator, + F: FnMut(&I::Item) -> bool, +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tee.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tee.rs similarity index 83% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tee.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tee.rs index ea47529..f2659104 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tee.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tee.rs
@@ -1,8 +1,8 @@ use super::size_hint; -use std::cell::RefCell; use alloc::collections::VecDeque; use alloc::rc::Rc; +use std::cell::RefCell; /// Common buffer object for the two tee halves #[derive(Debug)] @@ -19,24 +19,27 @@ #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Debug)] pub struct Tee<I> - where I: Iterator +where + I: Iterator, { rcbuffer: Rc<RefCell<TeeBuffer<I::Item, I>>>, id: bool, } pub fn new<I>(iter: I) -> (Tee<I>, Tee<I>) - where I: Iterator +where + I: Iterator, { - let buffer = TeeBuffer{backlog: VecDeque::new(), iter, owner: false}; - let t1 = Tee{rcbuffer: Rc::new(RefCell::new(buffer)), id: true}; - let t2 = Tee{rcbuffer: t1.rcbuffer.clone(), id: false}; + let buffer = TeeBuffer { backlog: VecDeque::new(), iter, owner: false }; + let t1 = Tee { rcbuffer: Rc::new(RefCell::new(buffer)), id: true }; + let t2 = Tee { rcbuffer: t1.rcbuffer.clone(), id: false }; (t1, t2) } impl<I> Iterator for Tee<I> - where I: Iterator, - I::Item: Clone +where + I: Iterator, + I::Item: Clone, { type Item = I::Item; fn next(&mut self) -> Option<Self::Item> { @@ -73,6 +76,8 @@ } impl<I> ExactSizeIterator for Tee<I> - where I: ExactSizeIterator, - I::Item: Clone -{} +where + I: ExactSizeIterator, + I::Item: Clone, +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tuple_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tuple_impl.rs new file mode 100644 index 0000000..faed07f --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tuple_impl.rs
@@ -0,0 +1,398 @@ +//! Some iterator that produces tuples + +use std::iter::Cycle; +use std::iter::Fuse; +use std::iter::FusedIterator; + +use crate::size_hint; + +// `HomogeneousTuple` is a public facade for `TupleCollect`, allowing +// tuple-related methods to be used by clients in generic contexts, while +// hiding the implementation details of `TupleCollect`. +// See https://github.com/rust-itertools/itertools/issues/387 + +/// Implemented for homogeneous tuples of size up to 12. +pub trait HomogeneousTuple: TupleCollect {} + +impl<T: TupleCollect> HomogeneousTuple for T {} + +/// An iterator over a incomplete tuple. +/// +/// See [`.tuples()`](crate::Itertools::tuples) and +/// [`Tuples::into_buffer()`]. +#[derive(Clone, Debug)] +pub struct TupleBuffer<T> +where + T: HomogeneousTuple, +{ + cur: usize, + buf: T::Buffer, +} + +impl<T> TupleBuffer<T> +where + T: HomogeneousTuple, +{ + fn new(buf: T::Buffer) -> Self { + Self { cur: 0, buf } + } +} + +impl<T> Iterator for TupleBuffer<T> +where + T: HomogeneousTuple, +{ + type Item = T::Item; + + fn next(&mut self) -> Option<Self::Item> { + let s = self.buf.as_mut(); + if let Some(ref mut item) = s.get_mut(self.cur) { + self.cur += 1; + item.take() + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let buffer = &self.buf.as_ref()[self.cur..]; + let len = if buffer.is_empty() { + 0 + } else { + buffer.iter().position(|x| x.is_none()).unwrap_or(buffer.len()) + }; + (len, Some(len)) + } +} + +impl<T> ExactSizeIterator for TupleBuffer<T> where T: HomogeneousTuple {} + +/// An iterator that groups the items in tuples of a specific size. +/// +/// See [`.tuples()`](crate::Itertools::tuples) for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Tuples<I, T> +where + I: Iterator<Item = T::Item>, + T: HomogeneousTuple, +{ + iter: Fuse<I>, + buf: T::Buffer, +} + +/// Create a new tuples iterator. +pub fn tuples<I, T>(iter: I) -> Tuples<I, T> +where + I: Iterator<Item = T::Item>, + T: HomogeneousTuple, +{ + Tuples { iter: iter.fuse(), buf: Default::default() } +} + +impl<I, T> Iterator for Tuples<I, T> +where + I: Iterator<Item = T::Item>, + T: HomogeneousTuple, +{ + type Item = T; + + fn next(&mut self) -> Option<Self::Item> { + T::collect_from_iter(&mut self.iter, &mut self.buf) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + // The number of elts we've drawn from the underlying iterator, but have + // not yet produced as a tuple. + let buffered = T::buffer_len(&self.buf); + // To that, we must add the size estimates of the underlying iterator. + let (unbuffered_lo, unbuffered_hi) = self.iter.size_hint(); + // The total low estimate is the sum of the already-buffered elements, + // plus the low estimate of remaining unbuffered elements, divided by + // the tuple size. + let total_lo = add_then_div(unbuffered_lo, buffered, T::num_items()).unwrap_or(usize::MAX); + // And likewise for the total high estimate, but using the high estimate + // of the remaining unbuffered elements. + let total_hi = unbuffered_hi.and_then(|hi| add_then_div(hi, buffered, T::num_items())); + (total_lo, total_hi) + } +} + +/// `(n + a) / d` avoiding overflow when possible, returns `None` if it +/// overflows. +fn add_then_div(n: usize, a: usize, d: usize) -> Option<usize> { + debug_assert_ne!(d, 0); + (n / d).checked_add(a / d)?.checked_add((n % d + a % d) / d) +} + +impl<I, T> ExactSizeIterator for Tuples<I, T> +where + I: ExactSizeIterator<Item = T::Item>, + T: HomogeneousTuple, +{ +} + +impl<I, T> Tuples<I, T> +where + I: Iterator<Item = T::Item>, + T: HomogeneousTuple, +{ + /// Return a buffer with the produced items that was not enough to be + /// grouped in a tuple. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut iter = (0..5).tuples(); + /// assert_eq!(Some((0, 1, 2)), iter.next()); + /// assert_eq!(None, iter.next()); + /// itertools::assert_equal(vec![3, 4], iter.into_buffer()); + /// ``` + pub fn into_buffer(self) -> TupleBuffer<T> { + TupleBuffer::new(self.buf) + } +} + +/// An iterator over all contiguous windows that produces tuples of a specific +/// size. +/// +/// See [`.tuple_windows()`](crate::Itertools::tuple_windows) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Clone, Debug)] +pub struct TupleWindows<I, T> +where + I: Iterator<Item = T::Item>, + T: HomogeneousTuple, +{ + iter: I, + last: Option<T>, +} + +/// Create a new tuple windows iterator. +pub fn tuple_windows<I, T>(iter: I) -> TupleWindows<I, T> +where + I: Iterator<Item = T::Item>, + T: HomogeneousTuple, + T::Item: Clone, +{ + TupleWindows { last: None, iter } +} + +impl<I, T> Iterator for TupleWindows<I, T> +where + I: Iterator<Item = T::Item>, + T: HomogeneousTuple + Clone, + T::Item: Clone, +{ + type Item = T; + + fn next(&mut self) -> Option<Self::Item> { + if T::num_items() == 1 { + return T::collect_from_iter_no_buf(&mut self.iter); + } + if let Some(new) = self.iter.next() { + if let Some(ref mut last) = self.last { + last.left_shift_push(new); + Some(last.clone()) + } else { + use std::iter::once; + let iter = once(new).chain(&mut self.iter); + self.last = T::collect_from_iter_no_buf(iter); + self.last.clone() + } + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let mut sh = self.iter.size_hint(); + // Adjust the size hint at the beginning + // OR when `num_items == 1` (but it does not change the size hint). + if self.last.is_none() { + sh = size_hint::sub_scalar(sh, T::num_items() - 1); + } + sh + } +} + +impl<I, T> ExactSizeIterator for TupleWindows<I, T> +where + I: ExactSizeIterator<Item = T::Item>, + T: HomogeneousTuple + Clone, + T::Item: Clone, +{ +} + +impl<I, T> FusedIterator for TupleWindows<I, T> +where + I: FusedIterator<Item = T::Item>, + T: HomogeneousTuple + Clone, + T::Item: Clone, +{ +} + +/// An iterator over all windows, wrapping back to the first elements when the +/// window would otherwise exceed the length of the iterator, producing tuples +/// of a specific size. +/// +/// See [`.circular_tuple_windows()`](crate::Itertools::circular_tuple_windows) +/// for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Debug, Clone)] +pub struct CircularTupleWindows<I, T> +where + I: Iterator<Item = T::Item> + Clone, + T: TupleCollect + Clone, +{ + iter: TupleWindows<Cycle<I>, T>, + len: usize, +} + +pub fn circular_tuple_windows<I, T>(iter: I) -> CircularTupleWindows<I, T> +where + I: Iterator<Item = T::Item> + Clone + ExactSizeIterator, + T: TupleCollect + Clone, + T::Item: Clone, +{ + let len = iter.len(); + let iter = tuple_windows(iter.cycle()); + + CircularTupleWindows { iter, len } +} + +impl<I, T> Iterator for CircularTupleWindows<I, T> +where + I: Iterator<Item = T::Item> + Clone, + T: TupleCollect + Clone, + T::Item: Clone, +{ + type Item = T; + + fn next(&mut self) -> Option<Self::Item> { + if self.len != 0 { + self.len -= 1; + self.iter.next() + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (self.len, Some(self.len)) + } +} + +impl<I, T> ExactSizeIterator for CircularTupleWindows<I, T> +where + I: Iterator<Item = T::Item> + Clone, + T: TupleCollect + Clone, + T::Item: Clone, +{ +} + +impl<I, T> FusedIterator for CircularTupleWindows<I, T> +where + I: Iterator<Item = T::Item> + Clone, + T: TupleCollect + Clone, + T::Item: Clone, +{ +} + +pub trait TupleCollect: Sized { + type Item; + type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>; + + fn buffer_len(buf: &Self::Buffer) -> usize { + let s = buf.as_ref(); + s.iter().position(Option::is_none).unwrap_or(s.len()) + } + + fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self> + where + I: IntoIterator<Item = Self::Item>; + + fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self> + where + I: IntoIterator<Item = Self::Item>; + + fn num_items() -> usize; + + fn left_shift_push(&mut self, item: Self::Item); +} + +macro_rules! rev_for_each_ident{ + ($m:ident, ) => {}; + ($m:ident, $i0:ident, $($i:ident,)*) => { + rev_for_each_ident!($m, $($i,)*); + $m!($i0); + }; +} + +macro_rules! impl_tuple_collect { + ($dummy:ident,) => {}; // stop + ($dummy:ident, $($Y:ident,)*) => ( + impl_tuple_collect!($($Y,)*); + impl<A> TupleCollect for ($(ignore_ident!($Y, A),)*) { + type Item = A; + type Buffer = [Option<A>; count_ident!($($Y)*) - 1]; + + #[allow(unused_assignments, unused_mut)] + fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self> + where I: IntoIterator<Item = A> + { + let mut iter = iter.into_iter(); + $( + let mut $Y = None; + )* + + loop { + $( + $Y = iter.next(); + if $Y.is_none() { + break + } + )* + return Some(($($Y.unwrap()),*,)) + } + + let mut i = 0; + let mut s = buf.as_mut(); + $( + if i < s.len() { + s[i] = $Y; + i += 1; + } + )* + return None; + } + + fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self> + where I: IntoIterator<Item = A> + { + let mut iter = iter.into_iter(); + + Some(($( + { let $Y = iter.next()?; $Y }, + )*)) + } + + fn num_items() -> usize { + count_ident!($($Y)*) + } + + fn left_shift_push(&mut self, mut item: A) { + use std::mem::replace; + + let &mut ($(ref mut $Y),*,) = self; + macro_rules! replace_item{($i:ident) => { + item = replace($i, item); + }} + rev_for_each_ident!(replace_item, $($Y,)*); + drop(item); + } + } + ) +} +impl_tuple_collect!(dummy, a, b, c, d, e, f, g, h, i, j, k, l,);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unique_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unique_impl.rs similarity index 62% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unique_impl.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unique_impl.rs index 4e81e78..64f4d97 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unique_impl.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unique_impl.rs
@@ -1,7 +1,7 @@ -use std::collections::HashMap; use std::collections::hash_map::Entry; -use std::hash::Hash; +use std::collections::HashMap; use std::fmt; +use std::hash::Hash; use std::iter::FusedIterator; /// An iterator adapter to filter out duplicate elements. @@ -19,29 +19,29 @@ } impl<I, V, F> fmt::Debug for UniqueBy<I, V, F> - where I: Iterator + fmt::Debug, - V: fmt::Debug + Hash + Eq, +where + I: Iterator + fmt::Debug, + V: fmt::Debug + Hash + Eq, { debug_fmt_fields!(UniqueBy, iter, used); } /// Create a new `UniqueBy` iterator. pub fn unique_by<I, V, F>(iter: I, f: F) -> UniqueBy<I, V, F> - where V: Eq + Hash, - F: FnMut(&I::Item) -> V, - I: Iterator, +where + V: Eq + Hash, + F: FnMut(&I::Item) -> V, + I: Iterator, { - UniqueBy { - iter, - used: HashMap::new(), - f, - } + UniqueBy { iter, used: HashMap::new(), f } } -// count the number of new unique keys in iterable (`used` is the set already seen) +// count the number of new unique keys in iterable (`used` is the set already +// seen) fn count_new_keys<I, K>(mut used: HashMap<K, ()>, iterable: I) -> usize - where I: IntoIterator<Item=K>, - K: Hash + Eq, +where + I: IntoIterator<Item = K>, + K: Hash + Eq, { let iter = iterable.into_iter(); let current_used = used.len(); @@ -50,20 +50,16 @@ } impl<I, V, F> Iterator for UniqueBy<I, V, F> - where I: Iterator, - V: Eq + Hash, - F: FnMut(&I::Item) -> V +where + I: Iterator, + V: Eq + Hash, + F: FnMut(&I::Item) -> V, { type Item = I::Item; fn next(&mut self) -> Option<Self::Item> { - while let Some(v) = self.iter.next() { - let key = (self.f)(&v); - if self.used.insert(key, ()).is_none() { - return Some(v); - } - } - None + let Self { iter, used, f } = self; + iter.find(|v| used.insert(f(v), ()).is_none()) } #[inline] @@ -79,42 +75,42 @@ } impl<I, V, F> DoubleEndedIterator for UniqueBy<I, V, F> - where I: DoubleEndedIterator, - V: Eq + Hash, - F: FnMut(&I::Item) -> V +where + I: DoubleEndedIterator, + V: Eq + Hash, + F: FnMut(&I::Item) -> V, { fn next_back(&mut self) -> Option<Self::Item> { - while let Some(v) = self.iter.next_back() { - let key = (self.f)(&v); - if self.used.insert(key, ()).is_none() { - return Some(v); - } - } - None + let Self { iter, used, f } = self; + iter.rfind(|v| used.insert(f(v), ()).is_none()) } } impl<I, V, F> FusedIterator for UniqueBy<I, V, F> - where I: FusedIterator, - V: Eq + Hash, - F: FnMut(&I::Item) -> V -{} +where + I: FusedIterator, + V: Eq + Hash, + F: FnMut(&I::Item) -> V, +{ +} impl<I> Iterator for Unique<I> - where I: Iterator, - I::Item: Eq + Hash + Clone +where + I: Iterator, + I::Item: Eq + Hash + Clone, { type Item = I::Item; fn next(&mut self) -> Option<Self::Item> { - while let Some(v) = self.iter.iter.next() { - if let Entry::Vacant(entry) = self.iter.used.entry(v) { + let UniqueBy { iter, used, .. } = &mut self.iter; + iter.find_map(|v| { + if let Entry::Vacant(entry) = used.entry(v) { let elt = entry.key().clone(); entry.insert(()); return Some(elt); } - } - None + None + }) } #[inline] @@ -129,51 +125,55 @@ } impl<I> DoubleEndedIterator for Unique<I> - where I: DoubleEndedIterator, - I::Item: Eq + Hash + Clone +where + I: DoubleEndedIterator, + I::Item: Eq + Hash + Clone, { fn next_back(&mut self) -> Option<Self::Item> { - while let Some(v) = self.iter.iter.next_back() { - if let Entry::Vacant(entry) = self.iter.used.entry(v) { + let UniqueBy { iter, used, .. } = &mut self.iter; + iter.rev().find_map(|v| { + if let Entry::Vacant(entry) = used.entry(v) { let elt = entry.key().clone(); entry.insert(()); return Some(elt); } - } - None + None + }) } } impl<I> FusedIterator for Unique<I> - where I: FusedIterator, - I::Item: Eq + Hash + Clone -{} +where + I: FusedIterator, + I::Item: Eq + Hash + Clone, +{ +} /// An iterator adapter to filter out duplicate elements. /// /// See [`.unique()`](crate::Itertools::unique) for more information. #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Unique<I: Iterator> { +pub struct Unique<I> +where + I: Iterator, + I::Item: Eq + Hash + Clone, +{ iter: UniqueBy<I, I::Item, ()>, } impl<I> fmt::Debug for Unique<I> - where I: Iterator + fmt::Debug, - I::Item: Hash + Eq + fmt::Debug, +where + I: Iterator + fmt::Debug, + I::Item: Hash + Eq + fmt::Debug + Clone, { debug_fmt_fields!(Unique, iter); } pub fn unique<I>(iter: I) -> Unique<I> - where I: Iterator, - I::Item: Eq + Hash, +where + I: Iterator, + I::Item: Eq + Hash + Clone, { - Unique { - iter: UniqueBy { - iter, - used: HashMap::new(), - f: (), - } - } + Unique { iter: UniqueBy { iter, used: HashMap::new(), f: () } } }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unziptuple.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unziptuple.rs similarity index 96% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unziptuple.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unziptuple.rs index 7af29ec4..5df899e 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unziptuple.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unziptuple.rs
@@ -1,7 +1,7 @@ /// Converts an iterator of tuples into a tuple of containers. /// -/// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each -/// column. +/// `multiunzip()` consumes an entire iterator of n-ary tuples, producing `n` +/// collections, one for each column. /// /// This function is, in some sense, the opposite of [`multizip`]. ///
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/with_position.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/with_position.rs new file mode 100644 index 0000000..9f9453a --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/with_position.rs
@@ -0,0 +1,123 @@ +use std::fmt; +use std::iter::{Fuse, FusedIterator, Peekable}; + +/// An iterator adaptor that wraps each element in an [`Position`]. +/// +/// Iterator element type is `(Position, I::Item)`. +/// +/// See [`.with_position()`](crate::Itertools::with_position) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct WithPosition<I> +where + I: Iterator, +{ + handled_first: bool, + peekable: Peekable<Fuse<I>>, +} + +impl<I> fmt::Debug for WithPosition<I> +where + I: Iterator, + Peekable<Fuse<I>>: fmt::Debug, +{ + debug_fmt_fields!(WithPosition, handled_first, peekable); +} + +impl<I> Clone for WithPosition<I> +where + I: Clone + Iterator, + I::Item: Clone, +{ + clone_fields!(handled_first, peekable); +} + +/// Create a new `WithPosition` iterator. +pub fn with_position<I>(iter: I) -> WithPosition<I> +where + I: Iterator, +{ + WithPosition { handled_first: false, peekable: iter.fuse().peekable() } +} + +/// The first component of the value yielded by `WithPosition`. +/// Indicates the position of this element in the iterator results. +/// +/// See [`.with_position()`](crate::Itertools::with_position) for more +/// information. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Position { + /// This is the first element. + First, + /// This is neither the first nor the last element. + Middle, + /// This is the last element. + Last, + /// This is the only element. + Only, +} + +impl<I: Iterator> Iterator for WithPosition<I> { + type Item = (Position, I::Item); + + fn next(&mut self) -> Option<Self::Item> { + match self.peekable.next() { + Some(item) => { + if !self.handled_first { + // Haven't seen the first item yet, and there is one to give. + self.handled_first = true; + // Peek to see if this is also the last item, + // in which case tag it as `Only`. + match self.peekable.peek() { + Some(_) => Some((Position::First, item)), + None => Some((Position::Only, item)), + } + } else { + // Have seen the first item, and there's something left. + // Peek to see if this is the last item. + match self.peekable.peek() { + Some(_) => Some((Position::Middle, item)), + None => Some((Position::Last, item)), + } + } + } + // Iterator is finished. + None => None, + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.peekable.size_hint() + } + + fn fold<B, F>(mut self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + if let Some(mut head) = self.peekable.next() { + if !self.handled_first { + // The current head is `First` or `Only`, + // it depends if there is another item or not. + match self.peekable.next() { + Some(second) => { + let first = std::mem::replace(&mut head, second); + init = f(init, (Position::First, first)); + } + None => return f(init, (Position::Only, head)), + } + } + // Have seen the first item, and there's something left. + init = self.peekable.fold(init, |acc, mut item| { + std::mem::swap(&mut head, &mut item); + f(acc, (Position::Middle, item)) + }); + // The "head" is now the last item. + init = f(init, (Position::Last, head)); + } + init + } +} + +impl<I> ExactSizeIterator for WithPosition<I> where I: ExactSizeIterator {} + +impl<I: Iterator> FusedIterator for WithPosition<I> {}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_eq_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_eq_impl.rs similarity index 62% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_eq_impl.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_eq_impl.rs index a079b32..f4a16d7 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_eq_impl.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_eq_impl.rs
@@ -1,6 +1,7 @@ use super::size_hint; /// An iterator which iterates two other iterators simultaneously +/// and panic if they have different lengths. /// /// See [`.zip_eq()`](crate::Itertools::zip_eq) for more information. #[derive(Clone, Debug)] @@ -10,11 +11,10 @@ b: J, } -/// Iterate `i` and `j` in lock step. +/// Zips two iterators but **panics** if they are not of the same length. /// -/// **Panics** if the iterators are not of the same length. -/// -/// [`IntoIterator`] enabled version of [`Itertools::zip_eq`](crate::Itertools::zip_eq). +/// [`IntoIterator`] enabled version of +/// [`Itertools::zip_eq`](crate::Itertools::zip_eq). /// /// ``` /// use itertools::zip_eq; @@ -22,21 +22,21 @@ /// let data = [1, 2, 3, 4, 5]; /// for (a, b) in zip_eq(&data[..data.len() - 1], &data[1..]) { /// /* loop body */ +/// # let _ = (a, b); /// } /// ``` pub fn zip_eq<I, J>(i: I, j: J) -> ZipEq<I::IntoIter, J::IntoIter> - where I: IntoIterator, - J: IntoIterator +where + I: IntoIterator, + J: IntoIterator, { - ZipEq { - a: i.into_iter(), - b: j.into_iter(), - } + ZipEq { a: i.into_iter(), b: j.into_iter() } } impl<I, J> Iterator for ZipEq<I, J> - where I: Iterator, - J: Iterator +where + I: Iterator, + J: Iterator, { type Item = (I::Item, J::Item); @@ -44,8 +44,9 @@ match (self.a.next(), self.b.next()) { (None, None) => None, (Some(a), Some(b)) => Some((a, b)), - (None, Some(_)) | (Some(_), None) => - panic!("itertools: .zip_eq() reached end of one iterator before the other") + (None, Some(_)) | (Some(_), None) => { + panic!("itertools: .zip_eq() reached end of one iterator before the other") + } } } @@ -55,6 +56,8 @@ } impl<I, J> ExactSizeIterator for ZipEq<I, J> - where I: ExactSizeIterator, - J: ExactSizeIterator -{} +where + I: ExactSizeIterator, + J: ExactSizeIterator, +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_longest.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_longest.rs new file mode 100644 index 0000000..565eed1 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_longest.rs
@@ -0,0 +1,126 @@ +use super::size_hint; +use std::cmp::Ordering::{Equal, Greater, Less}; +use std::iter::{Fuse, FusedIterator}; + +use crate::either_or_both::EitherOrBoth; + +// ZipLongest originally written by SimonSapin, +// and dedicated to itertools https://github.com/rust-lang/rust/pull/19283 + +/// An iterator which iterates two other iterators simultaneously +/// and wraps the elements in [`EitherOrBoth`]. +/// +/// This iterator is *fused*. +/// +/// See [`.zip_longest()`](crate::Itertools::zip_longest) for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct ZipLongest<T, U> { + a: Fuse<T>, + b: Fuse<U>, +} + +/// Create a new `ZipLongest` iterator. +pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U> +where + T: Iterator, + U: Iterator, +{ + ZipLongest { a: a.fuse(), b: b.fuse() } +} + +impl<T, U> Iterator for ZipLongest<T, U> +where + T: Iterator, + U: Iterator, +{ + type Item = EitherOrBoth<T::Item, U::Item>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + match (self.a.next(), self.b.next()) { + (None, None) => None, + (Some(a), None) => Some(EitherOrBoth::Left(a)), + (None, Some(b)) => Some(EitherOrBoth::Right(b)), + (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)), + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + size_hint::max(self.a.size_hint(), self.b.size_hint()) + } + + #[inline] + fn fold<B, F>(self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + let Self { mut a, mut b } = self; + let res = a.try_fold(init, |init, a| match b.next() { + Some(b) => Ok(f(init, EitherOrBoth::Both(a, b))), + None => Err(f(init, EitherOrBoth::Left(a))), + }); + match res { + Ok(acc) => b.map(EitherOrBoth::Right).fold(acc, f), + Err(acc) => a.map(EitherOrBoth::Left).fold(acc, f), + } + } +} + +impl<T, U> DoubleEndedIterator for ZipLongest<T, U> +where + T: DoubleEndedIterator + ExactSizeIterator, + U: DoubleEndedIterator + ExactSizeIterator, +{ + #[inline] + fn next_back(&mut self) -> Option<Self::Item> { + match self.a.len().cmp(&self.b.len()) { + Equal => match (self.a.next_back(), self.b.next_back()) { + (None, None) => None, + (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)), + // These can only happen if .len() is inconsistent with .next_back() + (Some(a), None) => Some(EitherOrBoth::Left(a)), + (None, Some(b)) => Some(EitherOrBoth::Right(b)), + }, + Greater => self.a.next_back().map(EitherOrBoth::Left), + Less => self.b.next_back().map(EitherOrBoth::Right), + } + } + + fn rfold<B, F>(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + let Self { mut a, mut b } = self; + let a_len = a.len(); + let b_len = b.len(); + match a_len.cmp(&b_len) { + Equal => {} + Greater => { + init = + a.by_ref().rev().take(a_len - b_len).map(EitherOrBoth::Left).fold(init, &mut f) + } + Less => { + init = + b.by_ref().rev().take(b_len - a_len).map(EitherOrBoth::Right).fold(init, &mut f) + } + } + a.rfold(init, |acc, item_a| f(acc, EitherOrBoth::Both(item_a, b.next_back().unwrap()))) + } +} + +impl<T, U> ExactSizeIterator for ZipLongest<T, U> +where + T: ExactSizeIterator, + U: ExactSizeIterator, +{ +} + +impl<T, U> FusedIterator for ZipLongest<T, U> +where + T: Iterator, + U: Iterator, +{ +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/ziptuple.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/ziptuple.rs similarity index 83% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/ziptuple.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/ziptuple.rs index 6d3a584..fcd9ec1f 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/ziptuple.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/ziptuple.rs
@@ -7,21 +7,23 @@ t: T, } -/// An iterator that generalizes *.zip()* and allows running multiple iterators in lockstep. +/// An iterator that generalizes `.zip()` and allows running multiple iterators +/// in lockstep. /// -/// The iterator `Zip<(I, J, ..., M)>` is formed from a tuple of iterators (or values that -/// implement [`IntoIterator`]) and yields elements +/// The iterator `Zip<(I, J, ..., M)>` is formed from a tuple of iterators (or +/// values that implement [`IntoIterator`]) and yields elements /// until any of the subiterators yields `None`. /// -/// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to `E` are the -/// element types of the subiterator. +/// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to +/// `E` are the element types of the subiterator. /// -/// **Note:** The result of this macro is a value of a named type (`Zip<(I, J, -/// ..)>` of each component iterator `I, J, ...`) if each component iterator is -/// nameable. +/// **Note:** The result of this function is a value of a named type (`Zip<(I, +/// J, ..)>` of each component iterator `I, J, ...`) if each component iterator +/// is nameable. /// -/// Prefer [`izip!()`] over `multizip` for the performance benefits of using the -/// standard library `.zip()`. Prefer `multizip` if a nameable type is needed. +/// Prefer [`izip!()`](crate::izip) over `multizip` for the performance benefits +/// of using the standard library `.zip()`. Prefer `multizip` if a nameable type +/// is needed. /// /// ``` /// use itertools::multizip; @@ -36,10 +38,9 @@ /// /// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]); /// ``` -/// [`izip!()`]: crate::izip pub fn multizip<T, U>(t: U) -> Zip<T> - where Zip<T>: From<U>, - Zip<T>: Iterator, +where + Zip<T>: From<U> + Iterator, { Zip::from(t) } @@ -82,7 +83,7 @@ fn size_hint(&self) -> (usize, Option<usize>) { - let sh = (::std::usize::MAX, None); + let sh = (usize::MAX, None); let ($(ref $B,)*) = self.t; $( let sh = size_hint::min($B.size_hint(), sh);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/adaptors_no_collect.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/adaptors_no_collect.rs similarity index 93% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/adaptors_no_collect.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/adaptors_no_collect.rs index 103db23..68ee20e 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/adaptors_no_collect.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/adaptors_no_collect.rs
@@ -22,7 +22,9 @@ } fn no_collect_test<A, T>(to_adaptor: T) - where A: Iterator, T: Fn(PanickingCounter) -> A +where + A: Iterator, + T: Fn(PanickingCounter) -> A, { let counter = PanickingCounter { curr: 0, max: 10_000 }; let adaptor = to_adaptor(counter); @@ -43,4 +45,4 @@ #[test] fn combinations_with_replacement_no_collect() { no_collect_test(|iter| iter.combinations_with_replacement(5)) -} \ No newline at end of file +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/flatten_ok.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/flatten_ok.rs new file mode 100644 index 0000000..12bab60 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/flatten_ok.rs
@@ -0,0 +1,49 @@ +use itertools::{assert_equal, Itertools}; +use std::{ops::Range, vec::IntoIter}; + +fn mix_data() -> IntoIter<Result<Range<i32>, bool>> { + vec![Ok(0..2), Err(false), Ok(2..4), Err(true), Ok(4..6)].into_iter() +} + +fn ok_data() -> IntoIter<Result<Range<i32>, bool>> { + vec![Ok(0..2), Ok(2..4), Ok(4..6)].into_iter() +} + +#[test] +fn flatten_ok_mixed_expected_forward() { + assert_equal( + mix_data().flatten_ok(), + vec![Ok(0), Ok(1), Err(false), Ok(2), Ok(3), Err(true), Ok(4), Ok(5)], + ); +} + +#[test] +fn flatten_ok_mixed_expected_reverse() { + assert_equal( + mix_data().flatten_ok().rev(), + vec![Ok(5), Ok(4), Err(true), Ok(3), Ok(2), Err(false), Ok(1), Ok(0)], + ); +} + +#[test] +fn flatten_ok_collect_mixed_forward() { + assert_eq!(mix_data().flatten_ok().collect::<Result<Vec<_>, _>>(), Err(false)); +} + +#[test] +fn flatten_ok_collect_mixed_reverse() { + assert_eq!(mix_data().flatten_ok().rev().collect::<Result<Vec<_>, _>>(), Err(true)); +} + +#[test] +fn flatten_ok_collect_ok_forward() { + assert_eq!(ok_data().flatten_ok().collect::<Result<Vec<_>, _>>(), Ok((0..6).collect())); +} + +#[test] +fn flatten_ok_collect_ok_reverse() { + assert_eq!( + ok_data().flatten_ok().rev().collect::<Result<Vec<_>, _>>(), + Ok((0..6).rev().collect()) + ); +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/laziness.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/laziness.rs new file mode 100644 index 0000000..c559d33 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/laziness.rs
@@ -0,0 +1,283 @@ +#![allow(unstable_name_collisions)] + +use itertools::Itertools; + +#[derive(Debug, Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +struct Panicking; + +impl Iterator for Panicking { + type Item = u8; + + fn next(&mut self) -> Option<u8> { + panic!("iterator adaptor is not lazy") + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (0, Some(0)) + } +} + +impl ExactSizeIterator for Panicking {} + +/// ## Usage example +/// ```compile_fail +/// must_use_tests! { +/// name { +/// Panicking.name(); // Add `let _ =` only if required (encountered error). +/// } +/// // ... +/// } +/// ``` +/// +/// **TODO:** test missing `must_use` attributes better, maybe with a new lint. +macro_rules! must_use_tests { + ($($(#[$attr:meta])* $name:ident $body:block)*) => { + $( + /// `#[deny(unused_must_use)]` should force us to ignore the resulting iterators + /// by adding `let _ = ...;` on every iterator. + /// If it does not, then a `must_use` attribute is missing on the associated struct. + /// + /// However, it's only helpful if we don't add `let _ =` before seeing if there is an error or not. + /// And it does not protect us against removed `must_use` attributes. + /// There is no simple way to test this yet. + #[deny(unused_must_use)] + #[test] + $(#[$attr])* + fn $name() $body + )* + }; +} + +must_use_tests! { + // Itertools trait: + interleave { + let _ = Panicking.interleave(Panicking); + } + interleave_shortest { + let _ = Panicking.interleave_shortest(Panicking); + } + intersperse { + let _ = Panicking.intersperse(0); + } + intersperse_with { + let _ = Panicking.intersperse_with(|| 0); + } + get { + let _ = Panicking.get(1..4); + let _ = Panicking.get(1..=4); + let _ = Panicking.get(1..); + let _ = Panicking.get(..4); + let _ = Panicking.get(..=4); + let _ = Panicking.get(..); + } + zip_longest { + let _ = Panicking.zip_longest(Panicking); + } + zip_eq { + let _ = Panicking.zip_eq(Panicking); + } + batching { + let _ = Panicking.batching(Iterator::next); + } + chunk_by { + // ChunkBy + let _ = Panicking.chunk_by(|x| *x); + // Groups + let _ = Panicking.chunk_by(|x| *x).into_iter(); + } + chunks { + // IntoChunks + let _ = Panicking.chunks(1); + let _ = Panicking.chunks(2); + // Chunks + let _ = Panicking.chunks(1).into_iter(); + let _ = Panicking.chunks(2).into_iter(); + } + tuple_windows { + let _ = Panicking.tuple_windows::<(_,)>(); + let _ = Panicking.tuple_windows::<(_, _)>(); + let _ = Panicking.tuple_windows::<(_, _, _)>(); + } + circular_tuple_windows { + let _ = Panicking.circular_tuple_windows::<(_,)>(); + let _ = Panicking.circular_tuple_windows::<(_, _)>(); + let _ = Panicking.circular_tuple_windows::<(_, _, _)>(); + } + tuples { + let _ = Panicking.tuples::<(_,)>(); + let _ = Panicking.tuples::<(_, _)>(); + let _ = Panicking.tuples::<(_, _, _)>(); + } + tee { + let _ = Panicking.tee(); + } + map_into { + let _ = Panicking.map_into::<u16>(); + } + map_ok { + let _ = Panicking.map(Ok::<u8, ()>).map_ok(|x| x + 1); + } + filter_ok { + let _ = Panicking.map(Ok::<u8, ()>).filter_ok(|x| x % 2 == 0); + } + filter_map_ok { + let _ = Panicking.map(Ok::<u8, ()>).filter_map_ok(|x| { + if x % 2 == 0 { + Some(x + 1) + } else { + None + } + }); + } + flatten_ok { + let _ = Panicking.map(|x| Ok::<_, ()>([x])).flatten_ok(); + } + merge { + let _ = Panicking.merge(Panicking); + } + merge_by { + let _ = Panicking.merge_by(Panicking, |_, _| true); + } + merge_join_by { + let _ = Panicking.merge_join_by(Panicking, |_, _| true); + let _ = Panicking.merge_join_by(Panicking, Ord::cmp); + } + #[should_panic] + kmerge { + let _ = Panicking.map(|_| Panicking).kmerge(); + } + #[should_panic] + kmerge_by { + let _ = Panicking.map(|_| Panicking).kmerge_by(|_, _| true); + } + cartesian_product { + let _ = Panicking.cartesian_product(Panicking); + } + multi_cartesian_product { + let _ = vec![Panicking, Panicking, Panicking].into_iter().multi_cartesian_product(); + } + coalesce { + let _ = Panicking.coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) }); + } + dedup { + let _ = Panicking.dedup(); + } + dedup_by { + let _ = Panicking.dedup_by(|_, _| true); + } + dedup_with_count { + let _ = Panicking.dedup_with_count(); + } + dedup_by_with_count { + let _ = Panicking.dedup_by_with_count(|_, _| true); + } + duplicates { + let _ = Panicking.duplicates(); + } + duplicates_by { + let _ = Panicking.duplicates_by(|x| *x); + } + unique { + let _ = Panicking.unique(); + } + unique_by { + let _ = Panicking.unique_by(|x| *x); + } + peeking_take_while { + let _ = Panicking.peekable().peeking_take_while(|x| x % 2 == 0); + } + take_while_ref { + let _ = Panicking.take_while_ref(|x| x % 2 == 0); + } + take_while_inclusive { + let _ = Panicking.take_while_inclusive(|x| x % 2 == 0); + } + while_some { + let _ = Panicking.map(Some).while_some(); + } + tuple_combinations1 { + let _ = Panicking.tuple_combinations::<(_,)>(); + } + #[should_panic] + tuple_combinations2 { + let _ = Panicking.tuple_combinations::<(_, _)>(); + } + #[should_panic] + tuple_combinations3 { + let _ = Panicking.tuple_combinations::<(_, _, _)>(); + } + combinations { + let _ = Panicking.combinations(0); + let _ = Panicking.combinations(1); + let _ = Panicking.combinations(2); + } + combinations_with_replacement { + let _ = Panicking.combinations_with_replacement(0); + let _ = Panicking.combinations_with_replacement(1); + let _ = Panicking.combinations_with_replacement(2); + } + permutations { + let _ = Panicking.permutations(0); + let _ = Panicking.permutations(1); + let _ = Panicking.permutations(2); + } + powerset { + let _ = Panicking.powerset(); + } + pad_using { + let _ = Panicking.pad_using(25, |_| 10); + } + with_position { + let _ = Panicking.with_position(); + } + positions { + let _ = Panicking.positions(|v| v % 2 == 0); + } + update { + let _ = Panicking.update(|n| *n += 1); + } + multipeek { + let _ = Panicking.multipeek(); + } + // Not iterator themselves but still lazy. + into_grouping_map { + let _ = Panicking.map(|x| (x, x + 1)).into_grouping_map(); + } + into_grouping_map_by { + let _ = Panicking.into_grouping_map_by(|x| *x); + } + // Macros: + iproduct { + let _ = itertools::iproduct!(Panicking); + let _ = itertools::iproduct!(Panicking, Panicking); + let _ = itertools::iproduct!(Panicking, Panicking, Panicking); + } + izip { + let _ = itertools::izip!(Panicking); + let _ = itertools::izip!(Panicking, Panicking); + let _ = itertools::izip!(Panicking, Panicking, Panicking); + } + chain { + let _ = itertools::chain!(Panicking); + let _ = itertools::chain!(Panicking, Panicking); + let _ = itertools::chain!(Panicking, Panicking, Panicking); + } + // Free functions: + multizip { + let _ = itertools::multizip((Panicking, Panicking)); + } + put_back { + let _ = itertools::put_back(Panicking); + let _ = itertools::put_back(Panicking).with_value(15); + } + peek_nth { + let _ = itertools::peek_nth(Panicking); + } + put_back_n { + let _ = itertools::put_back_n(Panicking); + } + rciter { + let _ = itertools::rciter(Panicking); + } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/macros_hygiene.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/macros_hygiene.rs new file mode 100644 index 0000000..e6e89555 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/macros_hygiene.rs
@@ -0,0 +1,27 @@ +mod alloc {} +mod core {} +mod either {} +mod std {} + +#[test] +fn iproduct_hygiene() { + let _ = itertools::iproduct!(); + let _ = itertools::iproduct!(0..6); + let _ = itertools::iproduct!(0..6, 0..9); + let _ = itertools::iproduct!(0..6, 0..9, 0..12); +} + +#[test] +fn izip_hygiene() { + let _ = itertools::izip!(0..6); + let _ = itertools::izip!(0..6, 0..9); + let _ = itertools::izip!(0..6, 0..9, 0..12); +} + +#[test] +fn chain_hygiene() { + let _: ::std::iter::Empty<i32> = itertools::chain!(); + let _ = itertools::chain!(0..6); + let _ = itertools::chain!(0..6, 0..9); + let _ = itertools::chain!(0..6, 0..9, 0..12); +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/merge_join.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/merge_join.rs new file mode 100644 index 0000000..2fa0975 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/merge_join.rs
@@ -0,0 +1,95 @@ +use itertools::free::merge_join_by; +use itertools::EitherOrBoth; + +#[test] +fn empty() { + let left: Vec<u32> = vec![]; + let right: Vec<u32> = vec![]; + let expected_result: Vec<EitherOrBoth<u32>> = vec![]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn left_only() { + let left: Vec<u32> = vec![1, 2, 3]; + let right: Vec<u32> = vec![]; + let expected_result: Vec<EitherOrBoth<u32>> = + vec![EitherOrBoth::Left(1), EitherOrBoth::Left(2), EitherOrBoth::Left(3)]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn right_only() { + let left: Vec<u32> = vec![]; + let right: Vec<u32> = vec![1, 2, 3]; + let expected_result: Vec<EitherOrBoth<u32>> = + vec![EitherOrBoth::Right(1), EitherOrBoth::Right(2), EitherOrBoth::Right(3)]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn first_left_then_right() { + let left: Vec<u32> = vec![1, 2, 3]; + let right: Vec<u32> = vec![4, 5, 6]; + let expected_result: Vec<EitherOrBoth<u32>> = vec![ + EitherOrBoth::Left(1), + EitherOrBoth::Left(2), + EitherOrBoth::Left(3), + EitherOrBoth::Right(4), + EitherOrBoth::Right(5), + EitherOrBoth::Right(6), + ]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn first_right_then_left() { + let left: Vec<u32> = vec![4, 5, 6]; + let right: Vec<u32> = vec![1, 2, 3]; + let expected_result: Vec<EitherOrBoth<u32>> = vec![ + EitherOrBoth::Right(1), + EitherOrBoth::Right(2), + EitherOrBoth::Right(3), + EitherOrBoth::Left(4), + EitherOrBoth::Left(5), + EitherOrBoth::Left(6), + ]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn interspersed_left_and_right() { + let left: Vec<u32> = vec![1, 3, 5]; + let right: Vec<u32> = vec![2, 4, 6]; + let expected_result: Vec<EitherOrBoth<u32>> = vec![ + EitherOrBoth::Left(1), + EitherOrBoth::Right(2), + EitherOrBoth::Left(3), + EitherOrBoth::Right(4), + EitherOrBoth::Left(5), + EitherOrBoth::Right(6), + ]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn overlapping_left_and_right() { + let left: Vec<u32> = vec![1, 3, 4, 6]; + let right: Vec<u32> = vec![2, 3, 4, 5]; + let expected_result: Vec<EitherOrBoth<u32>> = vec![ + EitherOrBoth::Left(1), + EitherOrBoth::Right(2), + EitherOrBoth::Both(3, 3), + EitherOrBoth::Both(4, 4), + EitherOrBoth::Right(5), + EitherOrBoth::Left(6), + ]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>(); + assert_eq!(expected_result, actual_result); +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/peeking_take_while.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/peeking_take_while.rs similarity index 86% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/peeking_take_while.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/peeking_take_while.rs index 5be97271..e9d6935 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/peeking_take_while.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/peeking_take_while.rs
@@ -52,18 +52,12 @@ #[test] fn peeking_take_while_nested() { let mut xs = (0..10).peekable(); - let ys: Vec<_> = xs - .peeking_take_while(|x| *x < 6) - .peeking_take_while(|x| *x != 3) - .collect(); + let ys: Vec<_> = xs.peeking_take_while(|x| *x < 6).peeking_take_while(|x| *x != 3).collect(); assert_eq!(ys, vec![0, 1, 2]); assert_eq!(xs.next(), Some(3)); let mut xs = (4..10).peekable(); - let ys: Vec<_> = xs - .peeking_take_while(|x| *x != 3) - .peeking_take_while(|x| *x < 6) - .collect(); + let ys: Vec<_> = xs.peeking_take_while(|x| *x != 3).peeking_take_while(|x| *x < 6).collect(); assert_eq!(ys, vec![4, 5]); assert_eq!(xs.next(), Some(6)); }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/quick.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/quick.rs similarity index 82% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/quick.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/quick.rs index c19af6c1..0177f122 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/quick.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/quick.rs
@@ -2,35 +2,28 @@ //! and adaptors. //! //! In particular we test the tedious size_hint and exact size correctness. +//! +//! **NOTE:** Due to performance limitations, these tests are not run with miri! +//! They cannot be relied upon to discover soundness issues. +#![cfg(not(miri))] +#![allow(deprecated, unstable_name_collisions)] + +use itertools::free::{ + cloned, enumerate, multipeek, peek_nth, put_back, put_back_n, rciter, zip, zip_eq, +}; +use itertools::Itertools; +use itertools::{iproduct, izip, multizip, EitherOrBoth}; use quickcheck as qc; +use std::cmp::{max, min, Ordering}; +use std::collections::{HashMap, HashSet}; use std::default::Default; use std::num::Wrapping; use std::ops::Range; -use std::cmp::{max, min, Ordering}; -use std::collections::{HashMap, HashSet}; -use itertools::Itertools; -use itertools::{ - multizip, - EitherOrBoth, - iproduct, - izip, -}; -use itertools::free::{ - cloned, - enumerate, - multipeek, - peek_nth, - put_back, - put_back_n, - rciter, - zip, - zip_eq, -}; -use rand::Rng; -use rand::seq::SliceRandom; use quickcheck::TestResult; +use rand::seq::SliceRandom; +use rand::Rng; /// Trait for size hint modifier types trait HintKind: Copy + Send + qc::Arbitrary { @@ -49,7 +42,7 @@ impl qc::Arbitrary for Exact { fn arbitrary<G: qc::Gen>(_: &mut G) -> Self { - Exact {} + Self {} } } @@ -66,8 +59,10 @@ impl HintKind for Inexact { fn loosen_bounds(&self, org_hint: (usize, Option<usize>)) -> (usize, Option<usize>) { let (org_lower, org_upper) = org_hint; - (org_lower.saturating_sub(self.underestimate), - org_upper.and_then(move |x| x.checked_add(self.overestimate))) + ( + org_lower.saturating_sub(self.underestimate), + org_upper.and_then(move |x| x.checked_add(self.overestimate)), + ) } } @@ -76,27 +71,22 @@ let ue_value = usize::arbitrary(g); let oe_value = usize::arbitrary(g); // Compensate for quickcheck using extreme values too rarely - let ue_choices = &[0, ue_value, usize::max_value()]; - let oe_choices = &[0, oe_value, usize::max_value()]; - Inexact { + let ue_choices = &[0, ue_value, usize::MAX]; + let oe_choices = &[0, oe_value, usize::MAX]; + Self { underestimate: *ue_choices.choose(g).unwrap(), overestimate: *oe_choices.choose(g).unwrap(), } } - fn shrink(&self) -> Box<dyn Iterator<Item=Self>> { + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { let underestimate_value = self.underestimate; let overestimate_value = self.overestimate; - Box::new( - underestimate_value.shrink().flat_map(move |ue_value| - overestimate_value.shrink().map(move |oe_value| - Inexact { - underestimate: ue_value, - overestimate: oe_value, - } - ) - ) - ) + Box::new(underestimate_value.shrink().flat_map(move |ue_value| { + overestimate_value + .shrink() + .map(move |oe_value| Self { underestimate: ue_value, overestimate: oe_value }) + })) } } @@ -116,75 +106,75 @@ hint_kind: SK, } -impl<T, HK> Iter<T, HK> where HK: HintKind +impl<T, HK> Iter<T, HK> +where + HK: HintKind, { fn new(it: Range<T>, hint_kind: HK) -> Self { - Iter { - iterator: it, - fuse_flag: 0, - hint_kind, - } + Self { iterator: it, fuse_flag: 0, hint_kind } } } impl<T, HK> Iterator for Iter<T, HK> - where Range<T>: Iterator, - <Range<T> as Iterator>::Item: Default, - HK: HintKind, +where + Range<T>: Iterator, + <Range<T> as Iterator>::Item: Default, + HK: HintKind, { type Item = <Range<T> as Iterator>::Item; - fn next(&mut self) -> Option<Self::Item> - { + fn next(&mut self) -> Option<Self::Item> { let elt = self.iterator.next(); if elt.is_none() { self.fuse_flag += 1; // check fuse flag if self.fuse_flag == 2 { - return Some(Default::default()) + return Some(Default::default()); } } elt } - fn size_hint(&self) -> (usize, Option<usize>) - { + fn size_hint(&self) -> (usize, Option<usize>) { let org_hint = self.iterator.size_hint(); self.hint_kind.loosen_bounds(org_hint) } } impl<T, HK> DoubleEndedIterator for Iter<T, HK> - where Range<T>: DoubleEndedIterator, - <Range<T> as Iterator>::Item: Default, - HK: HintKind +where + Range<T>: DoubleEndedIterator, + <Range<T> as Iterator>::Item: Default, + HK: HintKind, { - fn next_back(&mut self) -> Option<Self::Item> { self.iterator.next_back() } + fn next_back(&mut self) -> Option<Self::Item> { + self.iterator.next_back() + } } -impl<T> ExactSizeIterator for Iter<T, Exact> where Range<T>: ExactSizeIterator, +impl<T> ExactSizeIterator for Iter<T, Exact> +where + Range<T>: ExactSizeIterator, <Range<T> as Iterator>::Item: Default, -{ } +{ +} impl<T, HK> qc::Arbitrary for Iter<T, HK> - where T: qc::Arbitrary, - HK: HintKind, +where + T: qc::Arbitrary, + HK: HintKind, { - fn arbitrary<G: qc::Gen>(g: &mut G) -> Self - { - Iter::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g)) + fn arbitrary<G: qc::Gen>(g: &mut G) -> Self { + Self::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g)) } - fn shrink(&self) -> Box<dyn Iterator<Item=Iter<T, HK>>> - { + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { let r = self.iterator.clone(); let hint_kind = self.hint_kind; Box::new( - r.start.shrink().flat_map(move |a| - r.end.shrink().map(move |b| - Iter::new(a.clone()..b, hint_kind) - ) - ) + r.start + .shrink() + .flat_map(move |a| r.end.shrink().map(move |b| Self::new(a.clone()..b, hint_kind))), ) } } @@ -201,7 +191,10 @@ hint_kind: HK, } -impl<HK> Iterator for ShiftRange<HK> where HK: HintKind { +impl<HK> Iterator for ShiftRange<HK> +where + HK: HintKind, +{ type Item = Iter<i32, HK>; fn next(&mut self) -> Option<Self::Item> { @@ -219,10 +212,11 @@ } } -impl ExactSizeIterator for ShiftRange<Exact> { } +impl ExactSizeIterator for ShiftRange<Exact> {} impl<HK> qc::Arbitrary for ShiftRange<HK> - where HK: HintKind +where + HK: HintKind, { fn arbitrary<G: qc::Gen>(g: &mut G) -> Self { const MAX_STARTING_RANGE_DIFF: i32 = 32; @@ -236,21 +230,14 @@ let iter_count = g.gen_range(0, MAX_ITER_COUNT + 1); let hint_kind = qc::Arbitrary::arbitrary(g); - ShiftRange { - range_start, - range_end, - start_step, - end_step, - iter_count, - hint_kind, - } + Self { range_start, range_end, start_step, end_step, iter_count, hint_kind } } } fn correct_count<I, F>(get_it: F) -> bool where I: Iterator, - F: Fn() -> I + F: Fn() -> I, { let mut counts = vec![get_it().count()]; @@ -258,7 +245,6 @@ let mut it = get_it(); for _ in 0..(counts.len() - 1) { - #[allow(clippy::manual_assert)] if it.next().is_none() { panic!("Iterator shouldn't be finished, may not be deterministic"); } @@ -276,7 +262,10 @@ for (i, returned_count) in counts.into_iter().enumerate() { let actual_count = total_actual_count - i; if actual_count != returned_count { - println!("Total iterations: {} True count: {} returned count: {}", i, actual_count, returned_count); + println!( + "Total iterations: {} True count: {} returned count: {}", + i, actual_count, returned_count + ); return false; } @@ -299,12 +288,10 @@ // check all the size hints for &(low, hi) in &hints { true_count -= 1; - if low > true_count || - (hi.is_some() && hi.unwrap() < true_count) - { + if low > true_count || (hi.is_some() && hi.unwrap() < true_count) { println!("True size: {:?}, size hint: {:?}", true_count, (low, hi)); //println!("All hints: {:?}", hints); - return false + return false; } } true @@ -313,13 +300,19 @@ fn exact_size<I: ExactSizeIterator>(mut it: I) -> bool { // check every iteration let (mut low, mut hi) = it.size_hint(); - if Some(low) != hi { return false; } + if Some(low) != hi { + return false; + } while let Some(_) = it.next() { let (xlow, xhi) = it.size_hint(); - if low != xlow + 1 { return false; } + if low != xlow + 1 { + return false; + } low = xlow; hi = xhi; - if Some(low) != hi { return false; } + if Some(low) != hi { + return false; + } } let (low, hi) = it.size_hint(); low == 0 && hi == Some(0) @@ -329,13 +322,19 @@ fn exact_size_for_this<I: Iterator>(mut it: I) -> bool { // check every iteration let (mut low, mut hi) = it.size_hint(); - if Some(low) != hi { return false; } + if Some(low) != hi { + return false; + } while let Some(_) = it.next() { let (xlow, xhi) = it.size_hint(); - if low != xlow + 1 { return false; } + if low != xlow + 1 { + return false; + } low = xlow; hi = xhi; - if Some(low) != hi { return false; } + if Some(low) != hi { + return false; + } } let (low, hi) = it.size_hint(); low == 0 && hi == Some(0) @@ -442,43 +441,10 @@ assert_eq!(answer.into_iter().last(), a.multi_cartesian_product().last()); } - #[allow(deprecated)] - fn size_step(a: Iter<i16, Exact>, s: usize) -> bool { - let mut s = s; - if s == 0 { - s += 1; // never zero - } - let filt = a.clone().dedup(); - correct_size_hint(filt.step(s)) && - exact_size(a.step(s)) - } - - #[allow(deprecated)] - fn equal_step(a: Iter<i16>, s: usize) -> bool { - let mut s = s; - if s == 0 { - s += 1; // never zero - } - let mut i = 0; - itertools::equal(a.clone().step(s), a.filter(|_| { - let keep = i % s == 0; - i += 1; - keep - })) - } - - #[allow(deprecated)] - fn equal_step_vec(a: Vec<i16>, s: usize) -> bool { - let mut s = s; - if s == 0 { - s += 1; // never zero - } - let mut i = 0; - itertools::equal(a.iter().step(s), a.iter().filter(|_| { - let keep = i % s == 0; - i += 1; - keep - })) + fn correct_empty_multi_product() -> () { + let empty = Vec::<std::vec::IntoIter<i32>>::new().into_iter().multi_cartesian_product(); + assert!(correct_size_hint(empty.clone())); + itertools::assert_equal(empty, std::iter::once(Vec::new())) } fn size_multipeek(a: Iter<u16, Exact>, s: u8) -> bool { @@ -596,6 +562,20 @@ let b = &b[..len]; itertools::equal(zip_eq(a, b), zip(a, b)) } + + #[should_panic] + fn zip_eq_panics(a: Vec<u8>, b: Vec<u8>) -> TestResult { + if a.len() == b.len() { return TestResult::discard(); } + zip_eq(a.iter(), b.iter()).for_each(|_| {}); + TestResult::passed() // won't come here + } + + fn equal_positions(a: Vec<i32>) -> bool { + let with_pos = a.iter().positions(|v| v % 2 == 0); + let without = a.iter().enumerate().filter(|(_, v)| *v % 2 == 0).map(|(i, _)| i); + itertools::equal(with_pos.clone(), without.clone()) + && itertools::equal(with_pos.rev(), without.rev()) + } fn size_zip_longest(a: Iter<i16, Exact>, b: Iter<i16, Exact>) -> bool { let filt = a.clone().dedup(); let filt2 = b.clone().dedup(); @@ -751,6 +731,56 @@ } quickcheck! { + fn correct_peek_nth(mut a: Vec<u16>) -> () { + let mut it = peek_nth(a.clone()); + for start_pos in 0..a.len() + 2 { + for real_idx in start_pos..a.len() + 2 { + let peek_idx = real_idx - start_pos; + assert_eq!(it.peek_nth(peek_idx), a.get(real_idx)); + assert_eq!(it.peek_nth_mut(peek_idx), a.get_mut(real_idx)); + } + assert_eq!(it.next(), a.get(start_pos).copied()); + } + } + + fn peek_nth_mut_replace(a: Vec<u16>, b: Vec<u16>) -> () { + let mut it = peek_nth(a.iter()); + for (i, m) in b.iter().enumerate().take(a.len().min(b.len())) { + *it.peek_nth_mut(i).unwrap() = m; + } + for (i, m) in a.iter().enumerate() { + assert_eq!(it.next().unwrap(), b.get(i).unwrap_or(m)); + } + assert_eq!(it.next(), None); + assert_eq!(it.next(), None); + } + + fn peek_nth_next_if(a: Vec<u8>) -> () { + let mut it = peek_nth(a.clone()); + for (idx, mut value) in a.iter().copied().enumerate() { + let should_be_none = it.next_if(|x| x != &value); + assert_eq!(should_be_none, None); + if value % 5 == 0 { + // Sometimes, peek up to 3 further. + let n = value as usize % 3; + let nth = it.peek_nth(n); + assert_eq!(nth, a.get(idx + n)); + } else if value % 5 == 1 { + // Sometimes, peek next element mutably. + if let Some(v) = it.peek_mut() { + *v = v.wrapping_sub(1); + let should_be_none = it.next_if_eq(&value); + assert_eq!(should_be_none, None); + value = value.wrapping_sub(1); + } + } + let eq = it.next_if_eq(&value); + assert_eq!(eq, Some(value)); + } + } +} + +quickcheck! { fn dedup_via_coalesce(a: Vec<i32>) -> bool { let mut b = a.clone(); b.dedup(); @@ -811,9 +841,8 @@ quickcheck! { fn size_put_back(a: Vec<u8>, x: Option<u8>) -> bool { let mut it = put_back(a.into_iter()); - match x { - Some(t) => it.put_back(t), - None => {} + if let Some(t) = x { + it.put_back(t); } correct_size_hint(it) } @@ -895,8 +924,31 @@ } quickcheck! { - fn size_combinations(it: Iter<i16>) -> bool { - correct_size_hint(it.tuple_combinations::<(_, _)>()) + fn size_combinations(a: Iter<i16>) -> bool { + let it = a.clone().tuple_combinations::<(_, _)>(); + correct_size_hint(it.clone()) && it.count() == binomial(a.count(), 2) + } + + fn exact_size_combinations_1(a: Vec<u8>) -> bool { + let it = a.iter().tuple_combinations::<(_,)>(); + exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 1) + } + fn exact_size_combinations_2(a: Vec<u8>) -> bool { + let it = a.iter().tuple_combinations::<(_, _)>(); + exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 2) + } + fn exact_size_combinations_3(mut a: Vec<u8>) -> bool { + a.truncate(15); + let it = a.iter().tuple_combinations::<(_, _, _)>(); + exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 3) + } +} + +fn binomial(n: usize, k: usize) -> usize { + if k > n { + 0 + } else { + (n - k + 1..=n).product::<usize>() / (1..=k).product::<usize>() } } @@ -912,7 +964,7 @@ } } } - cmb.next() == None + cmb.next().is_none() } } @@ -961,58 +1013,58 @@ } quickcheck! { - fn fuzz_group_by_lazy_1(it: Iter<u8>) -> bool { + fn fuzz_chunk_by_lazy_1(it: Iter<u8>) -> bool { let jt = it.clone(); - let groups = it.group_by(|k| *k); - itertools::equal(jt, groups.into_iter().flat_map(|(_, x)| x)) + let chunks = it.chunk_by(|k| *k); + itertools::equal(jt, chunks.into_iter().flat_map(|(_, x)| x)) } } quickcheck! { - fn fuzz_group_by_lazy_2(data: Vec<u8>) -> bool { - let groups = data.iter().group_by(|k| *k / 10); - let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x)); + fn fuzz_chunk_by_lazy_2(data: Vec<u8>) -> bool { + let chunks = data.iter().chunk_by(|k| *k / 10); + let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x)); res } } quickcheck! { - fn fuzz_group_by_lazy_3(data: Vec<u8>) -> bool { - let grouper = data.iter().group_by(|k| *k / 10); - let groups = grouper.into_iter().collect_vec(); - let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x)); + fn fuzz_chunk_by_lazy_3(data: Vec<u8>) -> bool { + let grouper = data.iter().chunk_by(|k| *k / 10); + let chunks = grouper.into_iter().collect_vec(); + let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x)); res } } quickcheck! { - fn fuzz_group_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool { - let grouper = data.iter().group_by(|k| *k / 3); - let mut groups1 = grouper.into_iter(); - let mut groups2 = grouper.into_iter(); + fn fuzz_chunk_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool { + let grouper = data.iter().chunk_by(|k| *k / 3); + let mut chunks1 = grouper.into_iter(); + let mut chunks2 = grouper.into_iter(); let mut elts = Vec::<&u8>::new(); - let mut old_groups = Vec::new(); + let mut old_chunks = Vec::new(); let tup1 = |(_, b)| b; for &(ord, consume_now) in &order { - let iter = &mut [&mut groups1, &mut groups2][ord as usize]; + let iter = &mut [&mut chunks1, &mut chunks2][ord as usize]; match iter.next() { Some((_, gr)) => if consume_now { - for og in old_groups.drain(..) { + for og in old_chunks.drain(..) { elts.extend(og); } elts.extend(gr); } else { - old_groups.push(gr); + old_chunks.push(gr); }, None => break, } } - for og in old_groups.drain(..) { + for og in old_chunks.drain(..) { elts.extend(og); } - for gr in groups1.map(&tup1) { elts.extend(gr); } - for gr in groups2.map(&tup1) { elts.extend(gr); } + for gr in chunks1.map(&tup1) { elts.extend(gr); } + for gr in chunks2.map(&tup1) { elts.extend(gr); } itertools::assert_equal(&data, elts); true } @@ -1111,6 +1163,10 @@ true } + fn circular_tuple_windows_exact_size(a: Vec<u8>) -> bool { + exact_size(a.iter().circular_tuple_windows::<(_, _, _, _)>()) + } + fn equal_tuple_windows_1(a: Vec<u8>) -> bool { let x = a.windows(1).map(|s| (&s[0], )); let y = a.iter().tuple_windows::<(_,)>(); @@ -1135,6 +1191,14 @@ itertools::equal(x, y) } + fn tuple_windows_exact_size_1(a: Vec<u8>) -> bool { + exact_size(a.iter().tuple_windows::<(_,)>()) + } + + fn tuple_windows_exact_size_4(a: Vec<u8>) -> bool { + exact_size(a.iter().tuple_windows::<(_, _, _, _)>()) + } + fn equal_tuples_1(a: Vec<u8>) -> bool { let x = a.chunks(1).map(|s| (&s[0], )); let y = a.iter().tuples::<(_,)>(); @@ -1166,6 +1230,18 @@ assert_eq!(buffer.len(), a.len() % 4); exact_size(buffer) } + + fn tuples_size_hint_inexact(a: Iter<u8>) -> bool { + correct_size_hint(a.clone().tuples::<(_,)>()) + && correct_size_hint(a.clone().tuples::<(_, _)>()) + && correct_size_hint(a.tuples::<(_, _, _, _)>()) + } + + fn tuples_size_hint_exact(a: Iter<u8, Exact>) -> bool { + exact_size(a.clone().tuples::<(_,)>()) + && exact_size(a.clone().tuples::<(_, _)>()) + && exact_size(a.tuples::<(_, _, _, _)>()) + } } // with_position @@ -1197,14 +1273,14 @@ #[derive(Clone, Debug, PartialEq, Eq)] struct Val(u32, u32); -impl PartialOrd<Val> for Val { - fn partial_cmp(&self, other: &Val) -> Option<Ordering> { - self.0.partial_cmp(&other.0) +impl PartialOrd<Self> for Val { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) } } impl Ord for Val { - fn cmp(&self, other: &Val) -> Ordering { + fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) } } @@ -1212,10 +1288,10 @@ impl qc::Arbitrary for Val { fn arbitrary<G: qc::Gen>(g: &mut G) -> Self { let (x, y) = <(u32, u32)>::arbitrary(g); - Val(x, y) + Self(x, y) } fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { - Box::new((self.0, self.1).shrink().map(|(x, y)| Val(x, y))) + Box::new((self.0, self.1).shrink().map(|(x, y)| Self(x, y))) } } @@ -1257,13 +1333,12 @@ } quickcheck! { - #[allow(deprecated)] - fn tree_fold1_f64(mut a: Vec<f64>) -> TestResult { + fn tree_reduce_f64(mut a: Vec<f64>) -> TestResult { fn collapse_adjacent<F>(x: Vec<f64>, mut f: F) -> Vec<f64> where F: FnMut(f64, f64) -> f64 { let mut out = Vec::new(); - for i in (0..x.len()).step(2) { + for i in (0..x.len()).step_by(2) { if i == x.len()-1 { out.push(x[i]) } else { @@ -1277,7 +1352,7 @@ return TestResult::discard(); } - let actual = a.iter().cloned().tree_fold1(f64::atan2); + let actual = a.iter().cloned().tree_reduce(f64::atan2); while a.len() > 1 { a = collapse_adjacent(a, f64::atan2); @@ -1302,7 +1377,7 @@ fn at_most_one_i32(a: Vec<i32>) -> TestResult { let ret = a.iter().cloned().at_most_one(); match a.len() { - 0 => TestResult::from_bool(ret.unwrap() == None), + 0 => TestResult::from_bool(ret.unwrap().is_none()), 1 => TestResult::from_bool(ret.unwrap() == Some(a[0])), _ => TestResult::from_bool(ret.unwrap_err().eq(a.iter().cloned())), } @@ -1332,7 +1407,7 @@ Some(acc.unwrap_or(0) + val) } }); - + let group_map_lookup = a.iter() .map(|&b| b as u64) .map(|i| (i % modulo, i)) @@ -1352,7 +1427,7 @@ for m in 0..modulo { assert_eq!( - lookup.get(&m).copied(), + lookup.get(&m).copied(), a.iter() .map(|&b| b as u64) .filter(|&val| val % modulo == m) @@ -1367,6 +1442,35 @@ } } + fn correct_grouping_map_by_fold_with_modulo_key(a: Vec<u8>, modulo: u8) -> () { + #[derive(Debug, Default, PartialEq)] + struct Accumulator { + acc: u64, + } + + let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0` + let lookup = a.iter().map(|&b| b as u64) // Avoid overflows + .into_grouping_map_by(|i| i % modulo) + .fold_with(|_key, _val| Default::default(), |Accumulator { acc }, &key, val| { + assert!(val % modulo == key); + let acc = acc + val; + Accumulator { acc } + }); + + let group_map_lookup = a.iter() + .map(|&b| b as u64) + .map(|i| (i % modulo, i)) + .into_group_map() + .into_iter() + .map(|(key, vals)| (key, vals.into_iter().sum())).map(|(key, acc)| (key,Accumulator { acc })) + .collect::<HashMap<_,_>>(); + assert_eq!(lookup, group_map_lookup); + + for (&key, &Accumulator { acc: sum }) in lookup.iter() { + assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::<u64>()); + } + } + fn correct_grouping_map_by_fold_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0` let lookup = a.iter().map(|&b| b as u64) // Avoid overflows @@ -1390,22 +1494,21 @@ } } - fn correct_grouping_map_by_fold_first_modulo_key(a: Vec<u8>, modulo: u8) -> () { + fn correct_grouping_map_by_reduce_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0` let lookup = a.iter().map(|&b| b as u64) // Avoid overflows .into_grouping_map_by(|i| i % modulo) - .fold_first(|acc, &key, val| { + .reduce(|acc, &key, val| { assert!(val % modulo == key); acc + val }); - // TODO: Swap `fold1` with stdlib's `fold_first` when it's stabilized let group_map_lookup = a.iter() .map(|&b| b as u64) .map(|i| (i % modulo, i)) .into_group_map() .into_iter() - .map(|(key, vals)| (key, vals.into_iter().fold1(|acc, val| acc + val).unwrap())) + .map(|(key, vals)| (key, vals.into_iter().reduce(|acc, val| acc + val).unwrap())) .collect::<HashMap<_,_>>(); assert_eq!(lookup, group_map_lookup); @@ -1472,7 +1575,7 @@ assert_eq!(Some(max), a.iter().copied().filter(|&val| val % modulo == key).max_by_key(|&val| val)); } } - + fn correct_grouping_map_by_min_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).min(); @@ -1523,7 +1626,7 @@ assert_eq!(Some(min), a.iter().copied().filter(|&val| val % modulo == key).min_by_key(|&val| val)); } } - + fn correct_grouping_map_by_minmax_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).minmax(); @@ -1636,7 +1739,7 @@ .min_by(|_, _, _| Ordering::Equal); assert_eq!(lookup[&0], 0); - + let lookup = (0..=10) .into_grouping_map_by(|_| 0) .minmax_by(|_, _, _| Ordering::Equal); @@ -1694,12 +1797,10 @@ } } - -fn is_fused<I: Iterator>(mut it: I) -> bool -{ +fn is_fused<I: Iterator>(mut it: I) -> bool { for _ in it.by_ref() {} - for _ in 0..10{ - if it.next().is_some(){ + for _ in 0..10 { + if it.next().is_some() { return false; } } @@ -1740,7 +1841,7 @@ !is_fused(a.clone().interleave_shortest(b.clone())) && is_fused(a.fuse().interleave_shortest(b.fuse())) } - + fn fused_product(a: Iter<i16>, b: Iter<i16>) -> bool { is_fused(a.fuse().cartesian_product(b.fuse())) @@ -1846,4 +1947,11 @@ result_set.is_empty() } } + + fn tail(v: Vec<i32>, n: u8) -> bool { + let n = n as usize; + let result = &v[v.len().saturating_sub(n)..]; + itertools::equal(v.iter().tail(n), result) + && itertools::equal(v.iter().filter(|_| true).tail(n), result) + } }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/specializations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/specializations.rs new file mode 100644 index 0000000..6408985d --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/specializations.rs
@@ -0,0 +1,605 @@ +//! Test specializations of methods with default impls match the behavior of the +//! default impls. +//! +//! **NOTE:** Due to performance limitations, these tests are not run with miri! +//! They cannot be relied upon to discover soundness issues. + +#![cfg(not(miri))] +#![allow(unstable_name_collisions)] + +use itertools::Itertools; +use quickcheck::Arbitrary; +use quickcheck::{quickcheck, TestResult}; +use rand::Rng; +use std::fmt::Debug; + +struct Unspecialized<I>(I); + +impl<I> Iterator for Unspecialized<I> +where + I: Iterator, +{ + type Item = I::Item; + + #[inline(always)] + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } +} + +impl<I> DoubleEndedIterator for Unspecialized<I> +where + I: DoubleEndedIterator, +{ + #[inline(always)] + fn next_back(&mut self) -> Option<Self::Item> { + self.0.next_back() + } +} + +fn test_specializations<I>(it: &I) +where + I::Item: Eq + Debug + Clone, + I: Iterator + Clone, +{ + macro_rules! check_specialized { + ($src:expr, |$it:pat| $closure:expr) => { + // Many iterators special-case the first elements, so we test specializations + // for iterators that have already been advanced. + let mut src = $src.clone(); + for _ in 0..5 { + let $it = src.clone(); + let v1 = $closure; + let $it = Unspecialized(src.clone()); + let v2 = $closure; + assert_eq!(v1, v2); + src.next(); + } + }; + } + check_specialized!(it, |i| i.count()); + check_specialized!(it, |i| i.last()); + check_specialized!(it, |i| i.collect::<Vec<_>>()); + check_specialized!(it, |i| { + let mut parameters_from_fold = vec![]; + let fold_result = i.fold(vec![], |mut acc, v: I::Item| { + parameters_from_fold.push((acc.clone(), v.clone())); + acc.push(v); + acc + }); + (parameters_from_fold, fold_result) + }); + check_specialized!(it, |mut i| { + let mut parameters_from_all = vec![]; + let first = i.next(); + let all_result = i.all(|x| { + parameters_from_all.push(x.clone()); + Some(x) == first + }); + (parameters_from_all, all_result) + }); + let size = it.clone().count(); + for n in 0..size + 2 { + check_specialized!(it, |mut i| i.nth(n)); + } + // size_hint is a bit harder to check + let mut it_sh = it.clone(); + for n in 0..size + 2 { + let len = it_sh.clone().count(); + let (min, max) = it_sh.size_hint(); + assert_eq!(size - n.min(size), len); + assert!(min <= len); + if let Some(max) = max { + assert!(len <= max); + } + it_sh.next(); + } +} + +fn test_double_ended_specializations<I>(it: &I) +where + I::Item: Eq + Debug + Clone, + I: DoubleEndedIterator + Clone, +{ + macro_rules! check_specialized { + ($src:expr, |$it:pat| $closure:expr) => { + // Many iterators special-case the first elements, so we test specializations + // for iterators that have already been advanced. + let mut src = $src.clone(); + for step in 0..8 { + let $it = src.clone(); + let v1 = $closure; + let $it = Unspecialized(src.clone()); + let v2 = $closure; + assert_eq!(v1, v2); + if step % 2 == 0 { + src.next(); + } else { + src.next_back(); + } + } + }; + } + check_specialized!(it, |i| { + let mut parameters_from_rfold = vec![]; + let rfold_result = i.rfold(vec![], |mut acc, v: I::Item| { + parameters_from_rfold.push((acc.clone(), v.clone())); + acc.push(v); + acc + }); + (parameters_from_rfold, rfold_result) + }); + let size = it.clone().count(); + for n in 0..size + 2 { + check_specialized!(it, |mut i| i.nth_back(n)); + } +} + +quickcheck! { + fn interleave(v: Vec<u8>, w: Vec<u8>) -> () { + test_specializations(&v.iter().interleave(w.iter())); + } + + fn interleave_shortest(v: Vec<u8>, w: Vec<u8>) -> () { + test_specializations(&v.iter().interleave_shortest(w.iter())); + } + + fn batching(v: Vec<u8>) -> () { + test_specializations(&v.iter().batching(Iterator::next)); + } + + fn tuple_windows(v: Vec<u8>) -> () { + test_specializations(&v.iter().tuple_windows::<(_,)>()); + test_specializations(&v.iter().tuple_windows::<(_, _)>()); + test_specializations(&v.iter().tuple_windows::<(_, _, _)>()); + } + + fn circular_tuple_windows(v: Vec<u8>) -> () { + test_specializations(&v.iter().circular_tuple_windows::<(_,)>()); + test_specializations(&v.iter().circular_tuple_windows::<(_, _)>()); + test_specializations(&v.iter().circular_tuple_windows::<(_, _, _)>()); + } + + fn tuples(v: Vec<u8>) -> () { + test_specializations(&v.iter().tuples::<(_,)>()); + test_specializations(&v.iter().tuples::<(_, _)>()); + test_specializations(&v.iter().tuples::<(_, _, _)>()); + } + + fn cartesian_product(a: Vec<u8>, b: Vec<u8>) -> TestResult { + if a.len() * b.len() > 100 { + return TestResult::discard(); + } + test_specializations(&a.iter().cartesian_product(&b)); + TestResult::passed() + } + + fn multi_cartesian_product(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult { + if a.len() * b.len() * c.len() > 100 { + return TestResult::discard(); + } + test_specializations(&vec![a, b, c].into_iter().multi_cartesian_product()); + TestResult::passed() + } + + fn coalesce(v: Vec<u8>) -> () { + test_specializations(&v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) })) + } + + fn dedup(v: Vec<u8>) -> () { + test_specializations(&v.iter().dedup()) + } + + fn dedup_by(v: Vec<u8>) -> () { + test_specializations(&v.iter().dedup_by(PartialOrd::ge)) + } + + fn dedup_with_count(v: Vec<u8>) -> () { + test_specializations(&v.iter().dedup_with_count()) + } + + fn dedup_by_with_count(v: Vec<u8>) -> () { + test_specializations(&v.iter().dedup_by_with_count(PartialOrd::ge)) + } + + fn duplicates(v: Vec<u8>) -> () { + let it = v.iter().duplicates(); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn duplicates_by(v: Vec<u8>) -> () { + let it = v.iter().duplicates_by(|x| *x % 10); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn unique(v: Vec<u8>) -> () { + let it = v.iter().unique(); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn unique_by(v: Vec<u8>) -> () { + let it = v.iter().unique_by(|x| *x % 50); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn take_while_inclusive(v: Vec<u8>) -> () { + test_specializations(&v.iter().copied().take_while_inclusive(|&x| x < 100)); + } + + fn while_some(v: Vec<u8>) -> () { + test_specializations(&v.iter().map(|&x| if x < 100 { Some(2 * x) } else { None }).while_some()); + } + + fn pad_using(v: Vec<u8>) -> () { + use std::convert::TryFrom; + let it = v.iter().copied().pad_using(10, |i| u8::try_from(5 * i).unwrap_or(u8::MAX)); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn with_position(v: Vec<u8>) -> () { + test_specializations(&v.iter().with_position()); + } + + fn positions(v: Vec<u8>) -> () { + let it = v.iter().positions(|x| x % 5 == 0); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn update(v: Vec<u8>) -> () { + let it = v.iter().copied().update(|x| *x = x.wrapping_mul(7)); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn tuple_combinations(v: Vec<u8>) -> TestResult { + if v.len() > 10 { + return TestResult::discard(); + } + test_specializations(&v.iter().tuple_combinations::<(_,)>()); + test_specializations(&v.iter().tuple_combinations::<(_, _)>()); + test_specializations(&v.iter().tuple_combinations::<(_, _, _)>()); + TestResult::passed() + } + + fn intersperse(v: Vec<u8>) -> () { + test_specializations(&v.into_iter().intersperse(0)); + } + + fn intersperse_with(v: Vec<u8>) -> () { + test_specializations(&v.into_iter().intersperse_with(|| 0)); + } + + fn array_combinations(v: Vec<u8>) -> TestResult { + if v.len() > 10 { + return TestResult::discard(); + } + test_specializations(&v.iter().array_combinations::<1>()); + test_specializations(&v.iter().array_combinations::<2>()); + test_specializations(&v.iter().array_combinations::<3>()); + TestResult::passed() + } + + fn combinations(a: Vec<u8>, n: u8) -> TestResult { + if n > 3 || a.len() > 8 { + return TestResult::discard(); + } + test_specializations(&a.iter().combinations(n as usize)); + TestResult::passed() + } + + fn combinations_with_replacement(a: Vec<u8>, n: u8) -> TestResult { + if n > 3 || a.len() > 7 { + return TestResult::discard(); + } + test_specializations(&a.iter().combinations_with_replacement(n as usize)); + TestResult::passed() + } + + fn permutations(a: Vec<u8>, n: u8) -> TestResult { + if n > 3 || a.len() > 8 { + return TestResult::discard(); + } + test_specializations(&a.iter().permutations(n as usize)); + TestResult::passed() + } + + fn powerset(a: Vec<u8>) -> TestResult { + if a.len() > 6 { + return TestResult::discard(); + } + test_specializations(&a.iter().powerset()); + TestResult::passed() + } + + fn zip_longest(a: Vec<u8>, b: Vec<u8>) -> () { + let it = a.into_iter().zip_longest(b); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn zip_eq(a: Vec<u8>) -> () { + test_specializations(&a.iter().zip_eq(a.iter().rev())) + } + + fn multizip(a: Vec<u8>) -> () { + let it = itertools::multizip((a.iter(), a.iter().rev(), a.iter().take(50))); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn izip(a: Vec<u8>, b: Vec<u8>) -> () { + test_specializations(&itertools::izip!(b.iter(), a, b.iter().rev())); + } + + fn iproduct(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult { + if a.len() * b.len() * c.len() > 200 { + return TestResult::discard(); + } + test_specializations(&itertools::iproduct!(a, b.iter(), c)); + TestResult::passed() + } + + fn repeat_n(element: i8, n: u8) -> () { + let it = itertools::repeat_n(element, n as usize); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn exactly_one_error(v: Vec<u8>) -> TestResult { + // Use `at_most_one` would be similar. + match v.iter().exactly_one() { + Ok(_) => TestResult::discard(), + Err(it) => { + test_specializations(&it); + TestResult::passed() + } + } + } +} + +quickcheck! { + fn put_back_qc(test_vec: Vec<i32>) -> () { + test_specializations(&itertools::put_back(test_vec.iter())); + let mut pb = itertools::put_back(test_vec.into_iter()); + pb.put_back(1); + test_specializations(&pb); + } + + fn put_back_n(v: Vec<u8>, n: u8) -> () { + let mut it = itertools::put_back_n(v); + for k in 0..n { + it.put_back(k); + } + test_specializations(&it); + } + + fn multipeek(v: Vec<u8>, n: u8) -> () { + let mut it = v.into_iter().multipeek(); + for _ in 0..n { + it.peek(); + } + test_specializations(&it); + } + + fn peek_nth_with_peek(v: Vec<u8>, n: u8) -> () { + let mut it = itertools::peek_nth(v); + for _ in 0..n { + it.peek(); + } + test_specializations(&it); + } + + fn peek_nth_with_peek_nth(v: Vec<u8>, n: u8) -> () { + let mut it = itertools::peek_nth(v); + it.peek_nth(n as usize); + test_specializations(&it); + } + + fn peek_nth_with_peek_mut(v: Vec<u8>, n: u8) -> () { + let mut it = itertools::peek_nth(v); + for _ in 0..n { + if let Some(x) = it.peek_mut() { + *x = x.wrapping_add(50); + } + } + test_specializations(&it); + } + + fn peek_nth_with_peek_nth_mut(v: Vec<u8>, n: u8) -> () { + let mut it = itertools::peek_nth(v); + if let Some(x) = it.peek_nth_mut(n as usize) { + *x = x.wrapping_add(50); + } + test_specializations(&it); + } +} + +quickcheck! { + fn merge(a: Vec<u8>, b: Vec<u8>) -> () { + test_specializations(&a.into_iter().merge(b)) + } + + fn merge_by(a: Vec<u8>, b: Vec<u8>) -> () { + test_specializations(&a.into_iter().merge_by(b, PartialOrd::ge)) + } + + fn merge_join_by_ordering(i1: Vec<u8>, i2: Vec<u8>) -> () { + test_specializations(&i1.into_iter().merge_join_by(i2, Ord::cmp)); + } + + fn merge_join_by_bool(i1: Vec<u8>, i2: Vec<u8>) -> () { + test_specializations(&i1.into_iter().merge_join_by(i2, PartialOrd::ge)); + } + + fn kmerge(a: Vec<i8>, b: Vec<i8>, c: Vec<i8>) -> () { + test_specializations(&vec![a, b, c] + .into_iter() + .map(|v| v.into_iter().sorted()) + .kmerge()); + } + + fn kmerge_by(a: Vec<i8>, b: Vec<i8>, c: Vec<i8>) -> () { + test_specializations(&vec![a, b, c] + .into_iter() + .map(|v| v.into_iter().sorted_by_key(|a| a.abs())) + .kmerge_by(|a, b| a.abs() < b.abs())); + } +} + +quickcheck! { + fn map_into(v: Vec<u8>) -> () { + let it = v.into_iter().map_into::<u32>(); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn map_ok(v: Vec<Result<u8, char>>) -> () { + let it = v.into_iter().map_ok(|u| u.checked_add(1)); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn filter_ok(v: Vec<Result<u8, char>>) -> () { + let it = v.into_iter().filter_ok(|&i| i < 20); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn filter_map_ok(v: Vec<Result<u8, char>>) -> () { + let it = v.into_iter().filter_map_ok(|i| if i < 20 { Some(i * 2) } else { None }); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + // `SmallIter2<u8>` because `Vec<u8>` is too slow and we get bad coverage from a singleton like Option<u8> + fn flatten_ok(v: Vec<Result<SmallIter2<u8>, char>>) -> () { + let it = v.into_iter().flatten_ok(); + test_specializations(&it); + test_double_ended_specializations(&it); + } +} + +quickcheck! { + // TODO Replace this function by a normal call to test_specializations + fn process_results(v: Vec<Result<u8, u8>>) -> () { + helper(v.iter().copied()); + helper(v.iter().copied().filter(Result::is_ok)); + + fn helper(it: impl DoubleEndedIterator<Item = Result<u8, u8>> + Clone) { + macro_rules! check_results_specialized { + ($src:expr, |$it:pat| $closure:expr) => { + assert_eq!( + itertools::process_results($src.clone(), |$it| $closure), + itertools::process_results($src.clone(), |i| { + let $it = Unspecialized(i); + $closure + }), + ) + } + } + + check_results_specialized!(it, |i| i.count()); + check_results_specialized!(it, |i| i.last()); + check_results_specialized!(it, |i| i.collect::<Vec<_>>()); + check_results_specialized!(it, |i| i.rev().collect::<Vec<_>>()); + check_results_specialized!(it, |i| { + let mut parameters_from_fold = vec![]; + let fold_result = i.fold(vec![], |mut acc, v| { + parameters_from_fold.push((acc.clone(), v)); + acc.push(v); + acc + }); + (parameters_from_fold, fold_result) + }); + check_results_specialized!(it, |i| { + let mut parameters_from_rfold = vec![]; + let rfold_result = i.rfold(vec![], |mut acc, v| { + parameters_from_rfold.push((acc.clone(), v)); + acc.push(v); + acc + }); + (parameters_from_rfold, rfold_result) + }); + check_results_specialized!(it, |mut i| { + let mut parameters_from_all = vec![]; + let first = i.next(); + let all_result = i.all(|x| { + parameters_from_all.push(x); + Some(x)==first + }); + (parameters_from_all, all_result) + }); + let size = it.clone().count(); + for n in 0..size + 2 { + check_results_specialized!(it, |mut i| i.nth(n)); + } + for n in 0..size + 2 { + check_results_specialized!(it, |mut i| i.nth_back(n)); + } + } + } +} + +/// Like `VecIntoIter<T>` with maximum 2 elements. +#[derive(Debug, Clone, Default)] +enum SmallIter2<T> { + #[default] + Zero, + One(T), + Two(T, T), +} + +impl<T: Arbitrary> Arbitrary for SmallIter2<T> { + fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Self { + match g.gen_range(0u8, 3) { + 0 => Self::Zero, + 1 => Self::One(T::arbitrary(g)), + 2 => Self::Two(T::arbitrary(g), T::arbitrary(g)), + _ => unreachable!(), + } + } + // maybe implement shrink too, maybe not +} + +impl<T> Iterator for SmallIter2<T> { + type Item = T; + + fn next(&mut self) -> Option<Self::Item> { + match std::mem::take(self) { + Self::Zero => None, + Self::One(val) => Some(val), + Self::Two(val, second) => { + *self = Self::One(second); + Some(val) + } + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let len = match self { + Self::Zero => 0, + Self::One(_) => 1, + Self::Two(_, _) => 2, + }; + (len, Some(len)) + } +} + +impl<T> DoubleEndedIterator for SmallIter2<T> { + fn next_back(&mut self) -> Option<Self::Item> { + match std::mem::take(self) { + Self::Zero => None, + Self::One(val) => Some(val), + Self::Two(first, val) => { + *self = Self::One(first); + Some(val) + } + } + } +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_core.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_core.rs similarity index 71% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_core.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_core.rs index df94eb6..eabd662 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_core.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_core.rs
@@ -4,18 +4,71 @@ //! option. This file may not be copied, modified, or distributed //! except according to those terms. #![no_std] +#![allow(deprecated)] -use core::iter; -use itertools as it; -use crate::it::Itertools; +use crate::it::chain; +use crate::it::free::put_back; use crate::it::interleave; use crate::it::intersperse; use crate::it::intersperse_with; -use crate::it::multizip; -use crate::it::free::put_back; use crate::it::iproduct; use crate::it::izip; -use crate::it::chain; +use crate::it::multizip; +use crate::it::Itertools; +use core::iter; +use itertools as it; + +#[allow(dead_code)] +fn get_esi_then_esi<I: ExactSizeIterator + Clone>(it: I) { + fn is_esi(_: impl ExactSizeIterator) {} + is_esi(it.clone().get(1..4)); + is_esi(it.clone().get(1..=4)); + is_esi(it.clone().get(1..)); + is_esi(it.clone().get(..4)); + is_esi(it.clone().get(..=4)); + is_esi(it.get(..)); +} + +#[allow(dead_code)] +fn get_dei_esi_then_dei_esi<I: DoubleEndedIterator + ExactSizeIterator + Clone>(it: I) { + fn is_dei_esi(_: impl DoubleEndedIterator + ExactSizeIterator) {} + is_dei_esi(it.clone().get(1..4)); + is_dei_esi(it.clone().get(1..=4)); + is_dei_esi(it.clone().get(1..)); + is_dei_esi(it.clone().get(..4)); + is_dei_esi(it.clone().get(..=4)); + is_dei_esi(it.get(..)); +} + +#[test] +fn get_1_max() { + let mut it = (0..5).get(1..=usize::MAX); + assert_eq!(it.next(), Some(1)); + assert_eq!(it.next_back(), Some(4)); +} + +#[test] +#[should_panic] +fn get_full_range_inclusive() { + let _it = (0..5).get(0..=usize::MAX); +} + +#[test] +fn product0() { + let mut prod = iproduct!(); + assert_eq!(prod.next(), Some(())); + assert!(prod.next().is_none()); +} + +#[test] +fn iproduct1() { + let s = "αβ"; + + let mut prod = iproduct!(s.chars()); + assert_eq!(prod.next(), Some(('α',))); + assert_eq!(prod.next(), Some(('β',))); + assert!(prod.next().is_none()); +} #[test] fn product2() { @@ -26,21 +79,18 @@ assert!(prod.next() == Some(('α', 1))); assert!(prod.next() == Some(('β', 0))); assert!(prod.next() == Some(('β', 1))); - assert!(prod.next() == None); + assert!(prod.next().is_none()); } #[test] fn product_temporary() { - for (_x, _y, _z) in iproduct!( - [0, 1, 2].iter().cloned(), - [0, 1, 2].iter().cloned(), - [0, 1, 2].iter().cloned()) + for (_x, _y, _z) in + iproduct!([0, 1, 2].iter().cloned(), [0, 1, 2].iter().cloned(), [0, 1, 2].iter().cloned()) { // ok } } - #[test] fn izip_macro() { let mut zip = izip!(2..3); @@ -61,7 +111,7 @@ #[test] fn izip2() { let _zip1: iter::Zip<_, _> = izip!(1.., 2..); - let _zip2: iter::Zip<_, _> = izip!(1.., 2.., ); + let _zip2: iter::Zip<_, _> = izip!(1.., 2..,); } #[test] @@ -109,7 +159,7 @@ #[test] fn chain2() { let _ = chain!(1.., 2..); - let _ = chain!(1.., 2.., ); + let _ = chain!(1.., 2..,); } #[test] @@ -127,7 +177,7 @@ #[test] fn test_interleave() { - let xs: [u8; 0] = []; + let xs: [u8; 0] = []; let ys = [7u8, 9, 8, 10]; let zs = [2u8, 77]; let it = interleave(xs.iter(), ys.iter()); @@ -155,15 +205,6 @@ it::assert_equal(it, ys.iter()); } -#[allow(deprecated)] -#[test] -fn foreach() { - let xs = [1i32, 2, 3]; - let mut sum = 0; - xs.iter().foreach(|elt| sum += *elt); - assert!(sum == 6); -} - #[test] fn dropping() { let xs = [1, 2, 3]; @@ -180,10 +221,7 @@ let ys = [(0, 1), (2, 1)]; // An iterator that gathers elements up in pairs - let pit = xs - .iter() - .cloned() - .batching(|it| it.next().and_then(|x| it.next().map(|y| (x, y)))); + let pit = xs.iter().cloned().batching(|it| it.next().and_then(|x| it.next().map(|y| (x, y)))); it::assert_equal(pit, ys.iter().cloned()); } @@ -197,21 +235,11 @@ it::assert_equal(pb, xs.iter().cloned()); } -#[allow(deprecated)] -#[test] -fn step() { - it::assert_equal((0..10).step(1), 0..10); - it::assert_equal((0..10).step(2), (0..10).filter(|x: &i32| *x % 2 == 0)); - it::assert_equal((0..10).step(10), 0..1); -} - -#[allow(deprecated)] #[test] fn merge() { - it::assert_equal((0..10).step(2).merge((1..10).step(2)), 0..10); + it::assert_equal((0..10).step_by(2).merge((1..10).step_by(2)), 0..10); } - #[test] fn repeatn() { let s = "α"; @@ -231,29 +259,26 @@ use core::cell::Cell; #[derive(PartialEq, Debug)] struct Foo { - n: Cell<usize> + n: Cell<usize>, } - impl Clone for Foo - { - fn clone(&self) -> Self - { + impl Clone for Foo { + fn clone(&self) -> Self { let n = self.n.get(); self.n.set(n + 1); - Foo { n: Cell::new(n + 1) } + Self { n: Cell::new(n + 1) } } } - for n in 0..10 { - let f = Foo{n: Cell::new(0)}; + let f = Foo { n: Cell::new(0) }; let it = it::repeat_n(f, n); // drain it let last = it.last(); if n == 0 { assert_eq!(last, None); } else { - assert_eq!(last, Some(Foo{n: Cell::new(n - 1)})); + assert_eq!(last, Some(Foo { n: Cell::new(n - 1) })); } } } @@ -276,9 +301,9 @@ } #[test] -fn tree_fold1() { +fn tree_reduce() { for i in 0..100 { - assert_eq!((0..i).tree_fold1(|x, y| x + y), (0..i).fold1(|x, y| x + y)); + assert_eq!((0..i).tree_reduce(|x, y| x + y), (0..i).fold1(|x, y| x + y)); } } @@ -315,3 +340,28 @@ assert_eq!(v[1..3].iter().cloned().product1::<i32>(), Some(2)); assert_eq!(v[1..5].iter().cloned().product1::<i32>(), Some(24)); } + +#[test] +fn next_array() { + let v = [1, 2, 3, 4, 5]; + let mut iter = v.iter(); + assert_eq!(iter.next_array(), Some([])); + assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([1, 2])); + assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([3, 4])); + assert_eq!(iter.next_array::<2>(), None); +} + +#[test] +fn collect_array() { + let v = [1, 2]; + let iter = v.iter().cloned(); + assert_eq!(iter.collect_array(), Some([1, 2])); + + let v = [1]; + let iter = v.iter().cloned(); + assert_eq!(iter.collect_array::<2>(), None); + + let v = [1, 2, 3]; + let iter = v.iter().cloned(); + assert_eq!(iter.collect_array::<2>(), None); +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_std.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_std.rs new file mode 100644 index 0000000..ad391faad6 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_std.rs
@@ -0,0 +1,1569 @@ +#![allow(unstable_name_collisions)] + +use crate::it::cloned; +use crate::it::free::put_back_n; +use crate::it::free::rciter; +use crate::it::iproduct; +use crate::it::izip; +use crate::it::multipeek; +use crate::it::multizip; +use crate::it::peek_nth; +use crate::it::repeat_n; +use crate::it::ExactlyOneError; +use crate::it::FoldWhile; +use crate::it::Itertools; +use itertools as it; +use quickcheck as qc; +use rand::{ + distributions::{Distribution, Standard}, + rngs::StdRng, + Rng, SeedableRng, +}; +use rand::{seq::SliceRandom, thread_rng}; +use std::{cmp::min, fmt::Debug, marker::PhantomData}; + +#[test] +fn product3() { + let prod = iproduct!(0..3, 0..2, 0..2); + assert_eq!(prod.size_hint(), (12, Some(12))); + let v = prod.collect_vec(); + for i in 0..3 { + for j in 0..2 { + for k in 0..2 { + assert!((i, j, k) == v[(i * 2 * 2 + j * 2 + k) as usize]); + } + } + } + for (_, _, _, _) in iproduct!(0..3, 0..2, 0..2, 0..3) { /* test compiles */ } +} + +#[test] +fn interleave_shortest() { + let v0: Vec<i32> = vec![0, 2, 4]; + let v1: Vec<i32> = vec![1, 3, 5, 7]; + let it = v0.into_iter().interleave_shortest(v1); + assert_eq!(it.size_hint(), (6, Some(6))); + assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5]); + + let v0: Vec<i32> = vec![0, 2, 4, 6, 8]; + let v1: Vec<i32> = vec![1, 3, 5]; + let it = v0.into_iter().interleave_shortest(v1); + assert_eq!(it.size_hint(), (7, Some(7))); + assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5, 6]); + + let i0 = ::std::iter::repeat(0); + let v1: Vec<_> = vec![1, 3, 5]; + let it = i0.interleave_shortest(v1); + assert_eq!(it.size_hint(), (7, Some(7))); + + let v0: Vec<_> = vec![0, 2, 4]; + let i1 = ::std::iter::repeat(1); + let it = v0.into_iter().interleave_shortest(i1); + assert_eq!(it.size_hint(), (6, Some(6))); +} + +#[test] +fn duplicates_by() { + let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"]; + let ys = ["aa", "bbbb", "cccc"]; + it::assert_equal(ys.iter(), xs.iter().duplicates_by(|x| x[..2].to_string())); + it::assert_equal( + ys.iter(), + xs.iter().rev().duplicates_by(|x| x[..2].to_string()).rev(), + ); + let ys_rev = ["ccc", "aa", "bbbbb"]; + it::assert_equal( + ys_rev.iter(), + xs.iter().duplicates_by(|x| x[..2].to_string()).rev(), + ); +} + +#[test] +fn duplicates() { + let xs = [0, 1, 2, 3, 2, 1, 3]; + let ys = [2, 1, 3]; + it::assert_equal(ys.iter(), xs.iter().duplicates()); + it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev()); + let ys_rev = [3, 2, 1]; + it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev()); + + let xs = [0, 1, 0, 1]; + let ys = [0, 1]; + it::assert_equal(ys.iter(), xs.iter().duplicates()); + it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev()); + let ys_rev = [1, 0]; + it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev()); + + let xs = [0, 1, 2, 1, 2]; + let ys = vec![1, 2]; + assert_eq!(ys, xs.iter().duplicates().cloned().collect_vec()); + assert_eq!( + ys, + xs.iter().rev().duplicates().rev().cloned().collect_vec() + ); + let ys_rev = vec![2, 1]; + assert_eq!(ys_rev, xs.iter().duplicates().rev().cloned().collect_vec()); +} + +#[test] +fn unique_by() { + let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"]; + let ys = ["aaa", "bbbbb", "ccc"]; + it::assert_equal(ys.iter(), xs.iter().unique_by(|x| x[..2].to_string())); + it::assert_equal( + ys.iter(), + xs.iter().rev().unique_by(|x| x[..2].to_string()).rev(), + ); + let ys_rev = ["cccc", "aaaaa", "bbbb"]; + it::assert_equal( + ys_rev.iter(), + xs.iter().unique_by(|x| x[..2].to_string()).rev(), + ); +} + +#[test] +fn unique() { + let xs = [0, 1, 2, 3, 2, 1, 3]; + let ys = [0, 1, 2, 3]; + it::assert_equal(ys.iter(), xs.iter().unique()); + it::assert_equal(ys.iter(), xs.iter().rev().unique().rev()); + let ys_rev = [3, 1, 2, 0]; + it::assert_equal(ys_rev.iter(), xs.iter().unique().rev()); + + let xs = [0, 1]; + let ys = [0, 1]; + it::assert_equal(ys.iter(), xs.iter().unique()); + it::assert_equal(ys.iter(), xs.iter().rev().unique().rev()); + let ys_rev = [1, 0]; + it::assert_equal(ys_rev.iter(), xs.iter().unique().rev()); +} + +#[test] +fn intersperse() { + let xs = ["a", "", "b", "c"]; + let v: Vec<&str> = xs.iter().cloned().intersperse(", ").collect(); + let text: String = v.concat(); + assert_eq!(text, "a, , b, c".to_string()); + + let ys = [0, 1, 2, 3]; + let mut it = ys[..0].iter().copied().intersperse(1); + assert!(it.next().is_none()); +} + +#[test] +fn dedup() { + let xs = [0, 1, 1, 1, 2, 1, 3, 3]; + let ys = [0, 1, 2, 1, 3]; + it::assert_equal(ys.iter(), xs.iter().dedup()); + let xs = [0, 0, 0, 0, 0]; + let ys = [0]; + it::assert_equal(ys.iter(), xs.iter().dedup()); + + let xs = [0, 1, 1, 1, 2, 1, 3, 3]; + let ys = [0, 1, 2, 1, 3]; + let mut xs_d = Vec::new(); + xs.iter().dedup().fold((), |(), &elt| xs_d.push(elt)); + assert_eq!(&xs_d, &ys); +} + +#[test] +fn coalesce() { + let data = [-1., -2., -3., 3., 1., 0., -1.]; + let it = data.iter().cloned().coalesce(|x, y| { + if (x >= 0.) == (y >= 0.) { + Ok(x + y) + } else { + Err((x, y)) + } + }); + itertools::assert_equal(it.clone(), vec![-6., 4., -1.]); + assert_eq!( + it.fold(vec![], |mut v, n| { + v.push(n); + v + }), + vec![-6., 4., -1.] + ); +} + +#[test] +fn dedup_by() { + let xs = [ + (0, 0), + (0, 1), + (1, 1), + (2, 1), + (0, 2), + (3, 1), + (0, 3), + (1, 3), + ]; + let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)]; + it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.1 == y.1)); + let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]; + let ys = [(0, 1)]; + it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.0 == y.0)); + + let xs = [ + (0, 0), + (0, 1), + (1, 1), + (2, 1), + (0, 2), + (3, 1), + (0, 3), + (1, 3), + ]; + let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)]; + let mut xs_d = Vec::new(); + xs.iter() + .dedup_by(|x, y| x.1 == y.1) + .fold((), |(), &elt| xs_d.push(elt)); + assert_eq!(&xs_d, &ys); +} + +#[test] +fn dedup_with_count() { + let xs: [i32; 8] = [0, 1, 1, 1, 2, 1, 3, 3]; + let ys: [(usize, &i32); 5] = [(1, &0), (3, &1), (1, &2), (1, &1), (2, &3)]; + + it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count()); + + let xs: [i32; 5] = [0, 0, 0, 0, 0]; + let ys: [(usize, &i32); 1] = [(5, &0)]; + + it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count()); +} + +#[test] +fn dedup_by_with_count() { + let xs = [ + (0, 0), + (0, 1), + (1, 1), + (2, 1), + (0, 2), + (3, 1), + (0, 3), + (1, 3), + ]; + let ys = [ + (1, &(0, 0)), + (3, &(0, 1)), + (1, &(0, 2)), + (1, &(3, 1)), + (2, &(0, 3)), + ]; + + it::assert_equal( + ys.iter().cloned(), + xs.iter().dedup_by_with_count(|x, y| x.1 == y.1), + ); + + let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]; + let ys = [(5, &(0, 1))]; + + it::assert_equal( + ys.iter().cloned(), + xs.iter().dedup_by_with_count(|x, y| x.0 == y.0), + ); +} + +#[test] +fn all_equal() { + assert!("".chars().all_equal()); + assert!("A".chars().all_equal()); + assert!(!"AABBCCC".chars().all_equal()); + assert!("AAAAAAA".chars().all_equal()); + for (_key, mut sub) in &"AABBCCC".chars().chunk_by(|&x| x) { + assert!(sub.all_equal()); + } +} + +#[test] +fn all_equal_value() { + assert_eq!("".chars().all_equal_value(), Err(None)); + assert_eq!("A".chars().all_equal_value(), Ok('A')); + assert_eq!("AABBCCC".chars().all_equal_value(), Err(Some(('A', 'B')))); + assert_eq!("AAAAAAA".chars().all_equal_value(), Ok('A')); + { + let mut it = [1, 2, 3].iter().copied(); + let result = it.all_equal_value(); + assert_eq!(result, Err(Some((1, 2)))); + let remaining = it.next(); + assert_eq!(remaining, Some(3)); + assert!(it.next().is_none()); + } +} + +#[test] +fn all_unique() { + assert!("ABCDEFGH".chars().all_unique()); + assert!(!"ABCDEFGA".chars().all_unique()); + assert!(::std::iter::empty::<usize>().all_unique()); +} + +#[test] +fn test_put_back_n() { + let xs = [0, 1, 1, 1, 2, 1, 3, 3]; + let mut pb = put_back_n(xs.iter().cloned()); + pb.next(); + pb.next(); + pb.put_back(1); + pb.put_back(0); + it::assert_equal(pb, xs.iter().cloned()); +} + +#[test] +fn tee() { + let xs = [0, 1, 2, 3]; + let (mut t1, mut t2) = xs.iter().cloned().tee(); + assert_eq!(t1.next(), Some(0)); + assert_eq!(t2.next(), Some(0)); + assert_eq!(t1.next(), Some(1)); + assert_eq!(t1.next(), Some(2)); + assert_eq!(t1.next(), Some(3)); + assert_eq!(t1.next(), None); + assert_eq!(t2.next(), Some(1)); + assert_eq!(t2.next(), Some(2)); + assert_eq!(t1.next(), None); + assert_eq!(t2.next(), Some(3)); + assert_eq!(t2.next(), None); + assert_eq!(t1.next(), None); + assert_eq!(t2.next(), None); + + let (t1, t2) = xs.iter().cloned().tee(); + it::assert_equal(t1, xs.iter().cloned()); + it::assert_equal(t2, xs.iter().cloned()); + + let (t1, t2) = xs.iter().cloned().tee(); + it::assert_equal(t1.zip(t2), xs.iter().cloned().zip(xs.iter().cloned())); +} + +#[test] +fn test_rciter() { + let xs = [0, 1, 1, 1, 2, 1, 3, 5, 6]; + + let mut r1 = rciter(xs.iter().cloned()); + let mut r2 = r1.clone(); + assert_eq!(r1.next(), Some(0)); + assert_eq!(r2.next(), Some(1)); + let mut z = r1.zip(r2); + assert_eq!(z.next(), Some((1, 1))); + assert_eq!(z.next(), Some((2, 1))); + assert_eq!(z.next(), Some((3, 5))); + assert_eq!(z.next(), None); + + // test intoiterator + let r1 = rciter(0..5); + let mut z = izip!(&r1, r1); + assert_eq!(z.next(), Some((0, 1))); +} + +#[test] +fn trait_pointers() { + struct ByRef<'r, I: ?Sized>(&'r mut I); + + impl<'r, X, I> Iterator for ByRef<'r, I> + where + I: ?Sized + 'r + Iterator<Item = X>, + { + type Item = X; + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } + } + + let mut it = Box::new(0..10) as Box<dyn Iterator<Item = i32>>; + assert_eq!(it.next(), Some(0)); + + { + let jt: &mut dyn Iterator<Item = i32> = &mut *it; + assert_eq!(jt.next(), Some(1)); + + { + let mut r = ByRef(jt); + assert_eq!(r.next(), Some(2)); + } + + assert_eq!(jt.find_position(|x| *x == 4), Some((1, 4))); + jt.for_each(|_| ()); + } +} + +#[test] +fn merge_by() { + let odd: Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")]; + let even = [(2, "foo"), (4, "bar"), (6, "baz")]; + let expected = [ + (1, "hello"), + (2, "foo"), + (3, "world"), + (4, "bar"), + (5, "!"), + (6, "baz"), + ]; + let results = odd.iter().merge_by(even.iter(), |a, b| a.0 <= b.0); + it::assert_equal(results, expected.iter()); +} + +#[test] +fn merge_by_btree() { + use std::collections::BTreeMap; + let mut bt1 = BTreeMap::new(); + bt1.insert("hello", 1); + bt1.insert("world", 3); + let mut bt2 = BTreeMap::new(); + bt2.insert("foo", 2); + bt2.insert("bar", 4); + let results = bt1.into_iter().merge_by(bt2, |a, b| a.0 <= b.0); + let expected = vec![("bar", 4), ("foo", 2), ("hello", 1), ("world", 3)]; + it::assert_equal(results, expected); +} + +#[test] +fn kmerge() { + let its = (0..4).map(|s| (s..10).step_by(4)); + + it::assert_equal(its.kmerge(), 0..10); +} + +#[test] +fn kmerge_2() { + let its = vec![3, 2, 1, 0].into_iter().map(|s| (s..10).step_by(4)); + + it::assert_equal(its.kmerge(), 0..10); +} + +#[test] +fn kmerge_empty() { + let its = (0..4).map(|_| 0..0); + assert_eq!(its.kmerge().next(), None); +} + +#[test] +fn kmerge_size_hint() { + let its = (0..5).map(|_| (0..10)); + assert_eq!(its.kmerge().size_hint(), (50, Some(50))); +} + +#[test] +fn kmerge_empty_size_hint() { + let its = (0..5).map(|_| (0..0)); + assert_eq!(its.kmerge().size_hint(), (0, Some(0))); +} + +#[test] +fn join() { + let many = [1, 2, 3]; + let one = [1]; + let none: Vec<i32> = vec![]; + + assert_eq!(many.iter().join(", "), "1, 2, 3"); + assert_eq!(one.iter().join(", "), "1"); + assert_eq!(none.iter().join(", "), ""); +} + +#[test] +fn sorted_unstable_by() { + let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| a.cmp(&b)); + it::assert_equal(sc, vec![1, 2, 3, 4]); + + let v = (0..5).sorted_unstable_by(|&a, &b| a.cmp(&b).reverse()); + it::assert_equal(v, vec![4, 3, 2, 1, 0]); +} + +#[test] +fn sorted_unstable_by_key() { + let sc = [3, 4, 1, 2].iter().cloned().sorted_unstable_by_key(|&x| x); + it::assert_equal(sc, vec![1, 2, 3, 4]); + + let v = (0..5).sorted_unstable_by_key(|&x| -x); + it::assert_equal(v, vec![4, 3, 2, 1, 0]); +} + +#[test] +fn sorted_by() { + let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| a.cmp(&b)); + it::assert_equal(sc, vec![1, 2, 3, 4]); + + let v = (0..5).sorted_by(|&a, &b| a.cmp(&b).reverse()); + it::assert_equal(v, vec![4, 3, 2, 1, 0]); +} + +#[cfg(not(miri))] +qc::quickcheck! { + fn k_smallest_range(n: i64, m: u16, k: u16) -> () { + // u16 is used to constrain k and m to 0..2¹⁶, + // otherwise the test could use too much memory. + let (k, m) = (k as usize, m as u64); + + let mut v: Vec<_> = (n..n.saturating_add(m as _)).collect(); + // Generate a random permutation of n..n+m + v.shuffle(&mut thread_rng()); + + // Construct the right answers for the top and bottom elements + let mut sorted = v.clone(); + sorted.sort(); + // how many elements are we checking + let num_elements = min(k, m as _); + + // Compute the top and bottom k in various combinations + let sorted_smallest = sorted[..num_elements].iter().cloned(); + let smallest = v.iter().cloned().k_smallest(k); + let smallest_by = v.iter().cloned().k_smallest_by(k, Ord::cmp); + let smallest_by_key = v.iter().cloned().k_smallest_by_key(k, |&x| x); + + let sorted_largest = sorted[sorted.len() - num_elements..].iter().rev().cloned(); + let largest = v.iter().cloned().k_largest(k); + let largest_by = v.iter().cloned().k_largest_by(k, Ord::cmp); + let largest_by_key = v.iter().cloned().k_largest_by_key(k, |&x| x); + + // Check the variations produce the same answers and that they're right + it::assert_equal(smallest, sorted_smallest.clone()); + it::assert_equal(smallest_by, sorted_smallest.clone()); + it::assert_equal(smallest_by_key, sorted_smallest); + + it::assert_equal(largest, sorted_largest.clone()); + it::assert_equal(largest_by, sorted_largest.clone()); + it::assert_equal(largest_by_key, sorted_largest); + } + + fn k_smallest_relaxed_range(n: i64, m: u16, k: u16) -> () { + // u16 is used to constrain k and m to 0..2¹⁶, + // otherwise the test could use too much memory. + let (k, m) = (k as usize, m as u64); + + let mut v: Vec<_> = (n..n.saturating_add(m as _)).collect(); + // Generate a random permutation of n..n+m + v.shuffle(&mut thread_rng()); + + // Construct the right answers for the top and bottom elements + let mut sorted = v.clone(); + sorted.sort(); + // how many elements are we checking + let num_elements = min(k, m as _); + + // Compute the top and bottom k in various combinations + let sorted_smallest = sorted[..num_elements].iter().cloned(); + let smallest = v.iter().cloned().k_smallest_relaxed(k); + let smallest_by = v.iter().cloned().k_smallest_relaxed_by(k, Ord::cmp); + let smallest_by_key = v.iter().cloned().k_smallest_relaxed_by_key(k, |&x| x); + + let sorted_largest = sorted[sorted.len() - num_elements..].iter().rev().cloned(); + let largest = v.iter().cloned().k_largest_relaxed(k); + let largest_by = v.iter().cloned().k_largest_relaxed_by(k, Ord::cmp); + let largest_by_key = v.iter().cloned().k_largest_relaxed_by_key(k, |&x| x); + + // Check the variations produce the same answers and that they're right + it::assert_equal(smallest, sorted_smallest.clone()); + it::assert_equal(smallest_by, sorted_smallest.clone()); + it::assert_equal(smallest_by_key, sorted_smallest); + + it::assert_equal(largest, sorted_largest.clone()); + it::assert_equal(largest_by, sorted_largest.clone()); + it::assert_equal(largest_by_key, sorted_largest); + } +} + +#[derive(Clone, Debug)] +struct RandIter<T: 'static + Clone + Send, R: 'static + Clone + Rng + SeedableRng + Send = StdRng> { + idx: usize, + len: usize, + rng: R, + _t: PhantomData<T>, +} + +impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> Iterator for RandIter<T, R> +where + Standard: Distribution<T>, +{ + type Item = T; + fn next(&mut self) -> Option<T> { + if self.idx == self.len { + None + } else { + self.idx += 1; + Some(self.rng.gen()) + } + } +} + +impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> qc::Arbitrary for RandIter<T, R> { + fn arbitrary<G: qc::Gen>(g: &mut G) -> Self { + Self { + idx: 0, + len: g.size(), + rng: R::seed_from_u64(g.next_u64()), + _t: PhantomData {}, + } + } +} + +// Check that taking the k smallest is the same as +// sorting then taking the k first elements +fn k_smallest_sort<I>(i: I, k: u16) +where + I: Iterator + Clone, + I::Item: Ord + Debug, +{ + let j = i.clone(); + let i1 = i.clone(); + let j1 = i.clone(); + let k = k as usize; + it::assert_equal(i.k_smallest(k), j.sorted().take(k)); + it::assert_equal(i1.k_smallest_relaxed(k), j1.sorted().take(k)); +} + +// Similar to `k_smallest_sort` but for our custom heap implementation. +fn k_smallest_by_sort<I>(i: I, k: u16) +where + I: Iterator + Clone, + I::Item: Ord + Debug, +{ + let j = i.clone(); + let i1 = i.clone(); + let j1 = i.clone(); + let k = k as usize; + it::assert_equal(i.k_smallest_by(k, Ord::cmp), j.sorted().take(k)); + it::assert_equal(i1.k_smallest_relaxed_by(k, Ord::cmp), j1.sorted().take(k)); +} + +macro_rules! generic_test { + ($f:ident, $($t:ty),+) => { + $(paste::item! { + qc::quickcheck! { + fn [< $f _ $t >](i: RandIter<$t>, k: u16) -> () { + $f(i, k) + } + } + })+ + }; +} + +#[cfg(not(miri))] +generic_test!(k_smallest_sort, u8, u16, u32, u64, i8, i16, i32, i64); +#[cfg(not(miri))] +generic_test!(k_smallest_by_sort, u8, u16, u32, u64, i8, i16, i32, i64); + +#[test] +fn sorted_by_key() { + let sc = [3, 4, 1, 2].iter().cloned().sorted_by_key(|&x| x); + it::assert_equal(sc, vec![1, 2, 3, 4]); + + let v = (0..5).sorted_by_key(|&x| -x); + it::assert_equal(v, vec![4, 3, 2, 1, 0]); +} + +#[test] +fn sorted_by_cached_key() { + // Track calls to key function + let mut ncalls = 0; + + let sorted = [3, 4, 1, 2].iter().cloned().sorted_by_cached_key(|&x| { + ncalls += 1; + x.to_string() + }); + it::assert_equal(sorted, vec![1, 2, 3, 4]); + // Check key function called once per element + assert_eq!(ncalls, 4); + + let mut ncalls = 0; + + let sorted = (0..5).sorted_by_cached_key(|&x| { + ncalls += 1; + -x + }); + it::assert_equal(sorted, vec![4, 3, 2, 1, 0]); + // Check key function called once per element + assert_eq!(ncalls, 5); +} + +#[test] +fn test_multipeek() { + let nums = vec![1u8, 2, 3, 4, 5]; + + let mp = multipeek(nums.iter().copied()); + assert_eq!(nums, mp.collect::<Vec<_>>()); + + let mut mp = multipeek(nums.iter().copied()); + assert_eq!(mp.peek(), Some(&1)); + assert_eq!(mp.next(), Some(1)); + assert_eq!(mp.peek(), Some(&2)); + assert_eq!(mp.peek(), Some(&3)); + assert_eq!(mp.next(), Some(2)); + assert_eq!(mp.peek(), Some(&3)); + assert_eq!(mp.peek(), Some(&4)); + assert_eq!(mp.peek(), Some(&5)); + assert_eq!(mp.peek(), None); + assert_eq!(mp.next(), Some(3)); + assert_eq!(mp.next(), Some(4)); + assert_eq!(mp.peek(), Some(&5)); + assert_eq!(mp.peek(), None); + assert_eq!(mp.next(), Some(5)); + assert_eq!(mp.next(), None); + assert_eq!(mp.peek(), None); +} + +#[test] +fn test_multipeek_reset() { + let data = [1, 2, 3, 4]; + + let mut mp = multipeek(cloned(&data)); + assert_eq!(mp.peek(), Some(&1)); + assert_eq!(mp.next(), Some(1)); + assert_eq!(mp.peek(), Some(&2)); + assert_eq!(mp.peek(), Some(&3)); + mp.reset_peek(); + assert_eq!(mp.peek(), Some(&2)); + assert_eq!(mp.next(), Some(2)); +} + +#[test] +fn test_multipeek_peeking_next() { + use crate::it::PeekingNext; + let nums = [1u8, 2, 3, 4, 5, 6, 7]; + + let mut mp = multipeek(nums.iter().copied()); + assert_eq!(mp.peeking_next(|&x| x != 0), Some(1)); + assert_eq!(mp.next(), Some(2)); + assert_eq!(mp.peek(), Some(&3)); + assert_eq!(mp.peek(), Some(&4)); + assert_eq!(mp.peeking_next(|&x| x == 3), Some(3)); + assert_eq!(mp.peek(), Some(&4)); + assert_eq!(mp.peeking_next(|&x| x != 4), None); + assert_eq!(mp.peeking_next(|&x| x == 4), Some(4)); + assert_eq!(mp.peek(), Some(&5)); + assert_eq!(mp.peek(), Some(&6)); + assert_eq!(mp.peeking_next(|&x| x != 5), None); + assert_eq!(mp.peek(), Some(&7)); + assert_eq!(mp.peeking_next(|&x| x == 5), Some(5)); + assert_eq!(mp.peeking_next(|&x| x == 6), Some(6)); + assert_eq!(mp.peek(), Some(&7)); + assert_eq!(mp.peek(), None); + assert_eq!(mp.next(), Some(7)); + assert_eq!(mp.peek(), None); +} + +#[test] +fn test_repeat_n_peeking_next() { + use crate::it::PeekingNext; + let mut rn = repeat_n(0, 5); + assert_eq!(rn.peeking_next(|&x| x != 0), None); + assert_eq!(rn.peeking_next(|&x| x <= 0), Some(0)); + assert_eq!(rn.next(), Some(0)); + assert_eq!(rn.peeking_next(|&x| x <= 0), Some(0)); + assert_eq!(rn.peeking_next(|&x| x != 0), None); + assert_eq!(rn.peeking_next(|&x| x >= 0), Some(0)); + assert_eq!(rn.next(), Some(0)); + assert_eq!(rn.peeking_next(|&x| x <= 0), None); + assert_eq!(rn.next(), None); +} + +#[test] +fn test_peek_nth() { + let nums = vec![1u8, 2, 3, 4, 5]; + + let iter = peek_nth(nums.iter().copied()); + assert_eq!(nums, iter.collect::<Vec<_>>()); + + let mut iter = peek_nth(nums.iter().copied()); + + assert_eq!(iter.peek_nth(0), Some(&1)); + assert_eq!(iter.peek_nth(0), Some(&1)); + assert_eq!(iter.next(), Some(1)); + + assert_eq!(iter.peek_nth(0), Some(&2)); + assert_eq!(iter.peek_nth(1), Some(&3)); + assert_eq!(iter.next(), Some(2)); + + assert_eq!(iter.peek_nth(0), Some(&3)); + assert_eq!(iter.peek_nth(1), Some(&4)); + assert_eq!(iter.peek_nth(2), Some(&5)); + assert_eq!(iter.peek_nth(3), None); + + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), Some(4)); + + assert_eq!(iter.peek_nth(0), Some(&5)); + assert_eq!(iter.peek_nth(1), None); + assert_eq!(iter.next(), Some(5)); + assert_eq!(iter.next(), None); + + assert_eq!(iter.peek_nth(0), None); + assert_eq!(iter.peek_nth(1), None); +} + +#[test] +fn test_peek_nth_peeking_next() { + use it::PeekingNext; + let nums = [1u8, 2, 3, 4, 5, 6, 7]; + let mut iter = peek_nth(nums.iter().copied()); + + assert_eq!(iter.peeking_next(|&x| x != 0), Some(1)); + assert_eq!(iter.next(), Some(2)); + + assert_eq!(iter.peek_nth(0), Some(&3)); + assert_eq!(iter.peek_nth(1), Some(&4)); + assert_eq!(iter.peeking_next(|&x| x == 3), Some(3)); + assert_eq!(iter.peek(), Some(&4)); + + assert_eq!(iter.peeking_next(|&x| x != 4), None); + assert_eq!(iter.peeking_next(|&x| x == 4), Some(4)); + assert_eq!(iter.peek_nth(0), Some(&5)); + assert_eq!(iter.peek_nth(1), Some(&6)); + + assert_eq!(iter.peeking_next(|&x| x != 5), None); + assert_eq!(iter.peek(), Some(&5)); + + assert_eq!(iter.peeking_next(|&x| x == 5), Some(5)); + assert_eq!(iter.peeking_next(|&x| x == 6), Some(6)); + assert_eq!(iter.peek_nth(0), Some(&7)); + assert_eq!(iter.peek_nth(1), None); + assert_eq!(iter.next(), Some(7)); + assert_eq!(iter.peek(), None); +} + +#[test] +fn test_peek_nth_next_if() { + let nums = [1u8, 2, 3, 4, 5, 6, 7]; + let mut iter = peek_nth(nums.iter().copied()); + + assert_eq!(iter.next_if(|&x| x != 0), Some(1)); + assert_eq!(iter.next(), Some(2)); + + assert_eq!(iter.peek_nth(0), Some(&3)); + assert_eq!(iter.peek_nth(1), Some(&4)); + assert_eq!(iter.next_if_eq(&3), Some(3)); + assert_eq!(iter.peek(), Some(&4)); + + assert_eq!(iter.next_if(|&x| x != 4), None); + assert_eq!(iter.next_if_eq(&4), Some(4)); + assert_eq!(iter.peek_nth(0), Some(&5)); + assert_eq!(iter.peek_nth(1), Some(&6)); + + assert_eq!(iter.next_if(|&x| x != 5), None); + assert_eq!(iter.peek(), Some(&5)); + + assert_eq!(iter.next_if(|&x| x % 2 == 1), Some(5)); + assert_eq!(iter.next_if_eq(&6), Some(6)); + assert_eq!(iter.peek_nth(0), Some(&7)); + assert_eq!(iter.peek_nth(1), None); + assert_eq!(iter.next(), Some(7)); + assert_eq!(iter.peek(), None); +} + +#[test] +fn pad_using() { + it::assert_equal((0..0).pad_using(1, |_| 1), 1..2); + + let v: Vec<usize> = vec![0, 1, 2]; + let r = v.into_iter().pad_using(5, |n| n); + it::assert_equal(r, vec![0, 1, 2, 3, 4]); + + let v: Vec<usize> = vec![0, 1, 2]; + let r = v.into_iter().pad_using(1, |_| panic!()); + it::assert_equal(r, vec![0, 1, 2]); +} + +#[test] +fn chunk_by() { + for (ch1, sub) in &"AABBCCC".chars().chunk_by(|&x| x) { + for ch2 in sub { + assert_eq!(ch1, ch2); + } + } + + for (ch1, sub) in &"AAABBBCCCCDDDD".chars().chunk_by(|&x| x) { + for ch2 in sub { + assert_eq!(ch1, ch2); + if ch1 == 'C' { + break; + } + } + } + + let toupper = |ch: &char| ch.to_uppercase().next().unwrap(); + + // try all possible orderings + for indices in permutohedron::Heap::new(&mut [0, 1, 2, 3]) { + let chunks = "AaaBbbccCcDDDD".chars().chunk_by(&toupper); + let mut subs = chunks.into_iter().collect_vec(); + + for &idx in &indices[..] { + let (key, text) = match idx { + 0 => ('A', "Aaa".chars()), + 1 => ('B', "Bbb".chars()), + 2 => ('C', "ccCc".chars()), + 3 => ('D', "DDDD".chars()), + _ => unreachable!(), + }; + assert_eq!(key, subs[idx].0); + it::assert_equal(&mut subs[idx].1, text); + } + } + + let chunks = "AAABBBCCCCDDDD".chars().chunk_by(|&x| x); + let mut subs = chunks.into_iter().map(|(_, g)| g).collect_vec(); + + let sd = subs.pop().unwrap(); + let sc = subs.pop().unwrap(); + let sb = subs.pop().unwrap(); + let sa = subs.pop().unwrap(); + for (a, b, c, d) in multizip((sa, sb, sc, sd)) { + assert_eq!(a, 'A'); + assert_eq!(b, 'B'); + assert_eq!(c, 'C'); + assert_eq!(d, 'D'); + } + + // check that the key closure is called exactly n times + { + let mut ntimes = 0; + let text = "AABCCC"; + for (_, sub) in &text.chars().chunk_by(|&x| { + ntimes += 1; + x + }) { + for _ in sub {} + } + assert_eq!(ntimes, text.len()); + } + + { + let mut ntimes = 0; + let text = "AABCCC"; + for _ in &text.chars().chunk_by(|&x| { + ntimes += 1; + x + }) {} + assert_eq!(ntimes, text.len()); + } + + { + let text = "ABCCCDEEFGHIJJKK"; + let gr = text.chars().chunk_by(|&x| x); + it::assert_equal(gr.into_iter().flat_map(|(_, sub)| sub), text.chars()); + } +} + +#[test] +fn chunk_by_lazy_2() { + let data = [0, 1]; + let chunks = data.iter().chunk_by(|k| *k); + let gs = chunks.into_iter().collect_vec(); + it::assert_equal(data.iter(), gs.into_iter().flat_map(|(_k, g)| g)); + + let data = [0, 1, 1, 0, 0]; + let chunks = data.iter().chunk_by(|k| *k); + let mut gs = chunks.into_iter().collect_vec(); + gs[1..].reverse(); + it::assert_equal(&[0, 0, 0, 1, 1], gs.into_iter().flat_map(|(_, g)| g)); + + let grouper = data.iter().chunk_by(|k| *k); + let mut chunks = Vec::new(); + for (k, chunk) in &grouper { + if *k == 1 { + chunks.push(chunk); + } + } + it::assert_equal(&mut chunks[0], &[1, 1]); + + let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; + let grouper = data.iter().chunk_by(|k| *k); + let mut chunks = Vec::new(); + for (i, (_, chunk)) in grouper.into_iter().enumerate() { + if i < 2 { + chunks.push(chunk); + } else if i < 4 { + for _ in chunk {} + } else { + chunks.push(chunk); + } + } + it::assert_equal(&mut chunks[0], &[0, 0, 0]); + it::assert_equal(&mut chunks[1], &[1, 1]); + it::assert_equal(&mut chunks[2], &[3, 3]); + + let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; + let mut i = 0; + let grouper = data.iter().chunk_by(move |_| { + let k = i / 3; + i += 1; + k + }); + for (i, chunk) in &grouper { + match i { + 0 => it::assert_equal(chunk, &[0, 0, 0]), + 1 => it::assert_equal(chunk, &[1, 1, 0]), + 2 => it::assert_equal(chunk, &[0, 2, 2]), + 3 => it::assert_equal(chunk, &[3, 3]), + _ => unreachable!(), + } + } +} + +#[test] +fn chunk_by_lazy_3() { + // test consuming each chunk on the lap after it was produced + let data = [0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2]; + let grouper = data.iter().chunk_by(|elt| *elt); + let mut last = None; + for (key, chunk) in &grouper { + if let Some(gr) = last.take() { + for elt in gr { + assert!(elt != key && i32::abs(elt - key) == 1); + } + } + last = Some(chunk); + } +} + +#[test] +fn chunks() { + let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; + let grouper = data.iter().chunks(3); + for (i, chunk) in grouper.into_iter().enumerate() { + match i { + 0 => it::assert_equal(chunk, &[0, 0, 0]), + 1 => it::assert_equal(chunk, &[1, 1, 0]), + 2 => it::assert_equal(chunk, &[0, 2, 2]), + 3 => it::assert_equal(chunk, &[3, 3]), + _ => unreachable!(), + } + } +} + +#[test] +fn concat_empty() { + let data: Vec<Vec<()>> = Vec::new(); + assert_eq!(data.into_iter().concat(), Vec::new()) +} + +#[test] +fn concat_non_empty() { + let data = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; + assert_eq!(data.into_iter().concat(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9]) +} + +#[test] +fn combinations() { + assert!((1..3).combinations(5).next().is_none()); + + let it = (1..3).combinations(2); + it::assert_equal(it, vec![vec![1, 2]]); + + let it = (1..5).combinations(2); + it::assert_equal( + it, + vec![ + vec![1, 2], + vec![1, 3], + vec![1, 4], + vec![2, 3], + vec![2, 4], + vec![3, 4], + ], + ); + + it::assert_equal((0..0).tuple_combinations::<(_, _)>(), <Vec<_>>::new()); + it::assert_equal((0..1).tuple_combinations::<(_, _)>(), <Vec<_>>::new()); + it::assert_equal((0..2).tuple_combinations::<(_, _)>(), vec![(0, 1)]); + + it::assert_equal((0..0).combinations(2), <Vec<Vec<_>>>::new()); + it::assert_equal((0..1).combinations(1), vec![vec![0]]); + it::assert_equal((0..2).combinations(1), vec![vec![0], vec![1]]); + it::assert_equal((0..2).combinations(2), vec![vec![0, 1]]); +} + +#[test] +fn combinations_of_too_short() { + for i in 1..10 { + assert!((0..0).combinations(i).next().is_none()); + assert!((0..i - 1).combinations(i).next().is_none()); + } +} + +#[test] +fn combinations_zero() { + it::assert_equal((1..3).combinations(0), vec![vec![]]); + it::assert_equal((0..0).combinations(0), vec![vec![]]); +} + +fn binomial(n: usize, k: usize) -> usize { + if k > n { + 0 + } else { + (n - k + 1..=n).product::<usize>() / (1..=k).product::<usize>() + } +} + +#[test] +fn combinations_range_count() { + for n in 0..=7 { + for k in 0..=7 { + let len = binomial(n, k); + let mut it = (0..n).combinations(k); + assert_eq!(len, it.clone().count()); + assert_eq!(len, it.size_hint().0); + assert_eq!(Some(len), it.size_hint().1); + for count in (0..len).rev() { + let elem = it.next(); + assert!(elem.is_some()); + assert_eq!(count, it.clone().count()); + assert_eq!(count, it.size_hint().0); + assert_eq!(Some(count), it.size_hint().1); + } + let should_be_none = it.next(); + assert!(should_be_none.is_none()); + } + } +} + +#[test] +fn combinations_inexact_size_hints() { + for k in 0..=7 { + let mut numbers = (0..18).filter(|i| i % 2 == 0); // 9 elements + let mut it = numbers.clone().combinations(k); + let real_n = numbers.clone().count(); + let len = binomial(real_n, k); + assert_eq!(len, it.clone().count()); + + let mut nb_loaded = 0; + let sh = numbers.size_hint(); + assert_eq!(binomial(sh.0 + nb_loaded, k), it.size_hint().0); + assert_eq!(sh.1.map(|n| binomial(n + nb_loaded, k)), it.size_hint().1); + + for next_count in 1..=len { + let elem = it.next(); + assert!(elem.is_some()); + assert_eq!(len - next_count, it.clone().count()); + if next_count == 1 { + // The very first time, the lazy buffer is prefilled. + nb_loaded = numbers.by_ref().take(k).count(); + } else { + // Then it loads one item each time until exhausted. + let nb = numbers.next(); + if nb.is_some() { + nb_loaded += 1; + } + } + let sh = numbers.size_hint(); + if next_count > real_n - k + 1 { + assert_eq!(0, sh.0); + assert_eq!(Some(0), sh.1); + assert_eq!(real_n, nb_loaded); + // Once it's fully loaded, size hints of `it` are exacts. + } + assert_eq!(binomial(sh.0 + nb_loaded, k) - next_count, it.size_hint().0); + assert_eq!( + sh.1.map(|n| binomial(n + nb_loaded, k) - next_count), + it.size_hint().1 + ); + } + let should_be_none = it.next(); + assert!(should_be_none.is_none()); + } +} + +#[test] +fn permutations_zero() { + it::assert_equal((1..3).permutations(0), vec![vec![]]); + it::assert_equal((0..0).permutations(0), vec![vec![]]); +} + +#[test] +fn permutations_range_count() { + for n in 0..=4 { + for k in 0..=4 { + let len = if k <= n { (n - k + 1..=n).product() } else { 0 }; + let mut it = (0..n).permutations(k); + assert_eq!(len, it.clone().count()); + assert_eq!(len, it.size_hint().0); + assert_eq!(Some(len), it.size_hint().1); + for count in (0..len).rev() { + let elem = it.next(); + assert!(elem.is_some()); + assert_eq!(count, it.clone().count()); + assert_eq!(count, it.size_hint().0); + assert_eq!(Some(count), it.size_hint().1); + } + let should_be_none = it.next(); + assert!(should_be_none.is_none()); + } + } +} + +#[test] +fn permutations_overflowed_size_hints() { + let mut it = std::iter::repeat(()).permutations(2); + assert_eq!(it.size_hint().0, usize::MAX); + assert_eq!(it.size_hint().1, None); + for nb_generated in 1..=1000 { + it.next(); + assert!(it.size_hint().0 >= usize::MAX - nb_generated); + assert_eq!(it.size_hint().1, None); + } +} + +#[test] +#[cfg(not(miri))] +fn combinations_with_replacement() { + // Pool smaller than n + it::assert_equal((0..1).combinations_with_replacement(2), vec![vec![0, 0]]); + // Pool larger than n + it::assert_equal( + (0..3).combinations_with_replacement(2), + vec![ + vec![0, 0], + vec![0, 1], + vec![0, 2], + vec![1, 1], + vec![1, 2], + vec![2, 2], + ], + ); + // Zero size + it::assert_equal((0..3).combinations_with_replacement(0), vec![vec![]]); + // Zero size on empty pool + it::assert_equal((0..0).combinations_with_replacement(0), vec![vec![]]); + // Empty pool + it::assert_equal( + (0..0).combinations_with_replacement(2), + <Vec<Vec<_>>>::new(), + ); +} + +#[test] +fn combinations_with_replacement_range_count() { + for n in 0..=4 { + for k in 0..=4 { + let len = binomial(usize::saturating_sub(n + k, 1), k); + let mut it = (0..n).combinations_with_replacement(k); + assert_eq!(len, it.clone().count()); + assert_eq!(len, it.size_hint().0); + assert_eq!(Some(len), it.size_hint().1); + for count in (0..len).rev() { + let elem = it.next(); + assert!(elem.is_some()); + assert_eq!(count, it.clone().count()); + assert_eq!(count, it.size_hint().0); + assert_eq!(Some(count), it.size_hint().1); + } + let should_be_none = it.next(); + assert!(should_be_none.is_none()); + } + } +} + +#[test] +fn powerset() { + it::assert_equal((0..0).powerset(), vec![vec![]]); + it::assert_equal((0..1).powerset(), vec![vec![], vec![0]]); + it::assert_equal( + (0..2).powerset(), + vec![vec![], vec![0], vec![1], vec![0, 1]], + ); + it::assert_equal( + (0..3).powerset(), + vec![ + vec![], + vec![0], + vec![1], + vec![2], + vec![0, 1], + vec![0, 2], + vec![1, 2], + vec![0, 1, 2], + ], + ); + + assert_eq!((0..4).powerset().count(), 1 << 4); + assert_eq!((0..8).powerset().count(), 1 << 8); + assert_eq!((0..16).powerset().count(), 1 << 16); + + for n in 0..=4 { + let mut it = (0..n).powerset(); + let len = 2_usize.pow(n); + assert_eq!(len, it.clone().count()); + assert_eq!(len, it.size_hint().0); + assert_eq!(Some(len), it.size_hint().1); + for count in (0..len).rev() { + let elem = it.next(); + assert!(elem.is_some()); + assert_eq!(count, it.clone().count()); + assert_eq!(count, it.size_hint().0); + assert_eq!(Some(count), it.size_hint().1); + } + let should_be_none = it.next(); + assert!(should_be_none.is_none()); + } +} + +#[test] +fn diff_mismatch() { + let a = [1, 2, 3, 4]; + let b = vec![1.0, 5.0, 3.0, 4.0]; + let b_map = b.into_iter().map(|f| f as i32); + let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); + + assert!(match diff { + Some(it::Diff::FirstMismatch(1, _, from_diff)) => + from_diff.collect::<Vec<_>>() == vec![5, 3, 4], + _ => false, + }); +} + +#[test] +fn diff_longer() { + let a = [1, 2, 3, 4]; + let b = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let b_map = b.into_iter().map(|f| f as i32); + let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); + + assert!(match diff { + Some(it::Diff::Longer(_, remaining)) => remaining.collect::<Vec<_>>() == vec![5, 6], + _ => false, + }); +} + +#[test] +fn diff_shorter() { + let a = [1, 2, 3, 4]; + let b = vec![1.0, 2.0]; + let b_map = b.into_iter().map(|f| f as i32); + let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); + + assert!(match diff { + Some(it::Diff::Shorter(len, _)) => len == 2, + _ => false, + }); +} + +#[test] +fn extrema_set() { + use std::cmp::Ordering; + + // A peculiar type: Equality compares both tuple items, but ordering only the + // first item. Used to distinguish equal elements. + #[derive(Clone, Debug, PartialEq, Eq)] + struct Val(u32, u32); + + impl PartialOrd<Self> for Val { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } + } + + impl Ord for Val { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } + } + + assert_eq!(None::<u32>.iter().min_set(), Vec::<&u32>::new()); + assert_eq!(None::<u32>.iter().max_set(), Vec::<&u32>::new()); + + assert_eq!(Some(1u32).iter().min_set(), vec![&1]); + assert_eq!(Some(1u32).iter().max_set(), vec![&1]); + + let data = [Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)]; + + let min_set = data.iter().min_set(); + assert_eq!(min_set, vec![&Val(0, 1), &Val(0, 2)]); + + let min_set_by_key = data.iter().min_set_by_key(|v| v.1); + assert_eq!(min_set_by_key, vec![&Val(2, 0), &Val(1, 0)]); + + let min_set_by = data.iter().min_set_by(|x, y| x.1.cmp(&y.1)); + assert_eq!(min_set_by, vec![&Val(2, 0), &Val(1, 0)]); + + let max_set = data.iter().max_set(); + assert_eq!(max_set, vec![&Val(2, 0), &Val(2, 1)]); + + let max_set_by_key = data.iter().max_set_by_key(|v| v.1); + assert_eq!(max_set_by_key, vec![&Val(0, 2)]); + + let max_set_by = data.iter().max_set_by(|x, y| x.1.cmp(&y.1)); + assert_eq!(max_set_by, vec![&Val(0, 2)]); +} + +#[test] +fn minmax() { + use crate::it::MinMaxResult; + use std::cmp::Ordering; + + // A peculiar type: Equality compares both tuple items, but ordering only the + // first item. This is so we can check the stability property easily. + #[derive(Clone, Debug, PartialEq, Eq)] + struct Val(u32, u32); + + impl PartialOrd<Self> for Val { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } + } + + impl Ord for Val { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } + } + + assert_eq!( + None::<Option<u32>>.iter().minmax(), + MinMaxResult::NoElements + ); + + assert_eq!(Some(1u32).iter().minmax(), MinMaxResult::OneElement(&1)); + + let data = [Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)]; + + let minmax = data.iter().minmax(); + assert_eq!(minmax, MinMaxResult::MinMax(&Val(0, 1), &Val(2, 1))); + + let (min, max) = data.iter().minmax_by_key(|v| v.1).into_option().unwrap(); + assert_eq!(min, &Val(2, 0)); + assert_eq!(max, &Val(0, 2)); + + let (min, max) = data + .iter() + .minmax_by(|x, y| x.1.cmp(&y.1)) + .into_option() + .unwrap(); + assert_eq!(min, &Val(2, 0)); + assert_eq!(max, &Val(0, 2)); +} + +#[test] +fn format() { + let data = [0, 1, 2, 3]; + let ans1 = "0, 1, 2, 3"; + let ans2 = "0--1--2--3"; + + let t1 = format!("{}", data.iter().format(", ")); + assert_eq!(t1, ans1); + let t2 = format!("{:?}", data.iter().format("--")); + assert_eq!(t2, ans2); + + let dataf = [1.1, 5.71828, -22.]; + let t3 = format!("{:.2e}", dataf.iter().format(", ")); + assert_eq!(t3, "1.10e0, 5.72e0, -2.20e1"); +} + +#[test] +fn while_some() { + let ns = (1..10) + .map(|x| if x % 5 != 0 { Some(x) } else { None }) + .while_some(); + it::assert_equal(ns, vec![1, 2, 3, 4]); +} + +#[test] +fn fold_while() { + let mut iterations = 0; + let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let sum = vec + .into_iter() + .fold_while(0, |acc, item| { + iterations += 1; + let new_sum = acc + item; + if new_sum <= 20 { + FoldWhile::Continue(new_sum) + } else { + FoldWhile::Done(acc) + } + }) + .into_inner(); + assert_eq!(iterations, 6); + assert_eq!(sum, 15); +} + +#[test] +fn tree_reduce() { + let x = [ + "", + "0", + "0 1 x", + "0 1 x 2 x", + "0 1 x 2 3 x x", + "0 1 x 2 3 x x 4 x", + "0 1 x 2 3 x x 4 5 x x", + "0 1 x 2 3 x x 4 5 x 6 x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 x x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 15 x x x x", + ]; + for (i, &s) in x.iter().enumerate() { + let expected = if s.is_empty() { + None + } else { + Some(s.to_string()) + }; + let num_strings = (0..i).map(|x| x.to_string()); + let actual = num_strings.tree_reduce(|a, b| format!("{} {} x", a, b)); + assert_eq!(actual, expected); + } +} + +#[test] +fn exactly_one_question_mark_syntax_works() { + exactly_one_question_mark_return().unwrap_err(); +} + +fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError<std::slice::Iter<'static, ()>>> +{ + [].iter().exactly_one()?; + Ok(()) +} + +#[test] +fn multiunzip() { + let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2), (3, 4, 5), (6, 7, 8)] + .iter() + .cloned() + .multiunzip(); + assert_eq!((a, b, c), (vec![0, 3, 6], vec![1, 4, 7], vec![2, 5, 8])); + let (): () = [(), (), ()].iter().cloned().multiunzip(); + #[allow(clippy::type_complexity)] + let t: ( + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + ) = [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)] + .iter() + .cloned() + .multiunzip(); + assert_eq!( + t, + ( + vec![0], + vec![1], + vec![2], + vec![3], + vec![4], + vec![5], + vec![6], + vec![7], + vec![8], + vec![9], + vec![10], + vec![11] + ) + ); +}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/tuples.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/tuples.rs similarity index 100% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/tuples.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/tuples.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/zip.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/zip.rs similarity index 71% rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/zip.rs rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/zip.rs index 75157d3..2f192b5 100644 --- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/zip.rs +++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/zip.rs
@@ -1,17 +1,14 @@ -use itertools::Itertools; -use itertools::EitherOrBoth::{Both, Left, Right}; -use itertools::free::zip_eq; use itertools::multizip; +use itertools::EitherOrBoth::{Both, Left, Right}; +use itertools::Itertools; #[test] fn zip_longest_fused() { let a = [Some(1), None, Some(3), Some(4)]; let b = [1, 2, 3]; - let unfused = a.iter().batching(|it| *it.next().unwrap()) - .zip_longest(b.iter().cloned()); - itertools::assert_equal(unfused, - vec![Both(1, 1), Right(2), Right(3)]); + let unfused = a.iter().batching(|it| *it.next().unwrap()).zip_longest(b.iter().cloned()); + itertools::assert_equal(unfused, vec![Both(1, 1), Right(2), Right(3)]); } #[test] @@ -20,7 +17,7 @@ let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; let v2 = &[10, 11, 12]; - assert_eq!(c.zip_longest(v.iter()).size_hint(), (std::usize::MAX, None)); + assert_eq!(c.zip_longest(v.iter()).size_hint(), (usize::MAX, None)); assert_eq!(v.iter().zip_longest(v2.iter()).size_hint(), (10, Some(10))); } @@ -54,24 +51,3 @@ assert_eq!(it.next_back(), Some((1, 1))); assert_eq!(it.next_back(), None); } - - -#[should_panic] -#[test] -fn zip_eq_panic1() -{ - let a = [1, 2]; - let b = [1, 2, 3]; - - zip_eq(&a, &b).count(); -} - -#[should_panic] -#[test] -fn zip_eq_panic2() -{ - let a: [i32; 0] = []; - let b = [1, 2, 3]; - - zip_eq(&a, &b).count(); -}
diff --git a/third_party/rust/itertools/v0_11/BUILD.gn b/third_party/rust/itertools/v0_14/BUILD.gn similarity index 79% rename from third_party/rust/itertools/v0_11/BUILD.gn rename to third_party/rust/itertools/v0_14/BUILD.gn index b4e7d57..f2cb9a43 100644 --- a/third_party/rust/itertools/v0_11/BUILD.gn +++ b/third_party/rust/itertools/v0_14/BUILD.gn
@@ -10,59 +10,61 @@ cargo_crate("lib") { crate_name = "itertools" - epoch = "0.11" + epoch = "0.14" crate_type = "rlib" crate_root = - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lib.rs" + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lib.rs" sources = [ - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/coalesce.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/map.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/mod.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/multi_product.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations_with_replacement.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/concat_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/cons_tuples_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/diff.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/duplicates_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/either_or_both.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/exactly_one_err.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/extrema_set.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/flatten_ok.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/format.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/free.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/group_map.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/groupbylazy.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/grouping_map.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/impl_macros.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/intersperse.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/k_smallest.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/kmerge_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lazy_buffer.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lib.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/merge_join.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/minmax.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/multipeek_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/pad_tail.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peek_nth.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peeking_take_while.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/permutations.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/powerset.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/process_results_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/put_back_n_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/rciter_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/repeatn.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/size_hint.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/sources.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/take_while_inclusive.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tee.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tuple_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unique_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unziptuple.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/with_position.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_eq_impl.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_longest.rs", - "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/ziptuple.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/coalesce.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/map.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/mod.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/multi_product.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations_with_replacement.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/concat_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/cons_tuples_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/diff.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/duplicates_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/either_or_both.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/exactly_one_err.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/extrema_set.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/flatten_ok.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/format.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/free.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/group_map.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/groupbylazy.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/grouping_map.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/impl_macros.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/intersperse.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/iter_index.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/k_smallest.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/kmerge_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lazy_buffer.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lib.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/merge_join.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/minmax.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/multipeek_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/next_array.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/pad_tail.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peek_nth.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peeking_take_while.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/permutations.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/powerset.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/process_results_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/put_back_n_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/rciter_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/repeatn.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/size_hint.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/sources.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/take_while_inclusive.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tee.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tuple_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unique_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unziptuple.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/with_position.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_eq_impl.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_longest.rs", + "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/ziptuple.rs", ] inputs = [] @@ -73,7 +75,7 @@ cargo_pkg_description = "Extra iterator adaptors, iterator methods, free functions, and macros." cargo_pkg_repository = "https://github.com/rust-itertools/itertools" - cargo_pkg_version = "0.11.0" + cargo_pkg_version = "0.14.0" allow_unsafe = true
diff --git a/third_party/rust/itertools/v0_11/README.chromium b/third_party/rust/itertools/v0_14/README.chromium similarity index 77% rename from third_party/rust/itertools/v0_11/README.chromium rename to third_party/rust/itertools/v0_14/README.chromium index 68a08d5a..f5ce0ef 100644 --- a/third_party/rust/itertools/v0_11/README.chromium +++ b/third_party/rust/itertools/v0_14/README.chromium
@@ -1,10 +1,10 @@ Name: itertools URL: https://crates.io/crates/itertools -Version: 0.11.0 -Revision: 62a6401afd6d45e1c2aea94c05cb5c70076b2ca4 +Version: 0.14.0 +Revision: a015a6831525ee1637df747d3f530a627d9741bf Update Mechanism: Manual (https://crbug.com/449898466) License: Apache-2.0 -License File: //third_party/rust/chromium_crates_io/vendor/itertools-v0_11/LICENSE-APACHE +License File: //third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-APACHE Shipped: no Security Critical: no
diff --git a/third_party/rust/prost_derive/v0_14/BUILD.gn b/third_party/rust/prost_derive/v0_14/BUILD.gn index c842797..dfa6b3ac 100644 --- a/third_party/rust/prost_derive/v0_14/BUILD.gn +++ b/third_party/rust/prost_derive/v0_14/BUILD.gn
@@ -36,7 +36,7 @@ deps = [ "//third_party/rust/anyhow/v1:lib", - "//third_party/rust/itertools/v0_11:lib", + "//third_party/rust/itertools/v0_14:lib", "//third_party/rust/proc_macro2/v1:lib", "//third_party/rust/quote/v1:lib", "//third_party/rust/syn/v2:lib",
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src index 47eef7b..6e9d60d 160000 --- a/third_party/spirv-tools/src +++ b/third_party/spirv-tools/src
@@ -1 +1 @@ -Subproject commit 47eef7be333fe709795fd695ba4f8e58c70a0cf0 +Subproject commit 6e9d60d80905f561858c2b1234bbb82bb529c044
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps index d61243d..c29f9df 160000 --- a/third_party/vulkan-deps +++ b/third_party/vulkan-deps
@@ -1 +1 @@ -Subproject commit d61243d1b0837231283e456a908637ead7917dd9 +Subproject commit c29f9df0edc12f925253a95526823c15ed249d6a
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src index 562af80..4fc964e 160000 --- a/third_party/vulkan-validation-layers/src +++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@ -Subproject commit 562af80edf57126f333036a707da8cb2f9c1522e +Subproject commit 4fc964ebd2fcad5d65372512bd483767f08dc345
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml index 83e2a3cc..3528cab 100644 --- a/tools/metrics/histograms/metadata/android/enums.xml +++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -738,6 +738,8 @@ <int value="179" label="WebSettingsCompat.setMaxPrerenders"/> <int value="180" label="WebSettingsCompat.setMaxPrefetches"/> <int value="181" label="WebSettingsCompat.setPrefetchTtlSeconds"/> + <int value="182" label="BackForwardCacheSettings.getKeepForwardEntries"/> + <int value="183" label="BackForwardCacheSettings.setKeepForwardEntries"/> <!-- LINT.ThenChange(/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java:ApiCall) --> </enum>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml index affd61e..0741dcd 100644 --- a/tools/metrics/histograms/metadata/apps/histograms.xml +++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -790,13 +790,16 @@ </histogram> <histogram name="Apps.AppList.GeminiSearchBoxIcon" enum="SearchBoxIconEvent" - expires_after="2026-03-01"> + expires_after="2027-03-01"> <owner>yawano@google.com</owner> <owner>assistive-eng@google.com</owner> <summary> Impression or click events for a Gemini search box icon. This metric was previously recorded as Assistant.NewEntryPoint.Eligibility and Assistant.NewEntryPoint.Launcher before M138. + + Warning: this histogram was expired from 2026-03-01 to 2026-03-10; data may + be missing. </summary> </histogram>
diff --git a/tools/metrics/histograms/metadata/assistant/histograms.xml b/tools/metrics/histograms/metadata/assistant/histograms.xml index 4b4c5c4..3bd5a42 100644 --- a/tools/metrics/histograms/metadata/assistant/histograms.xml +++ b/tools/metrics/histograms/metadata/assistant/histograms.xml
@@ -23,7 +23,7 @@ <histograms> <histogram name="Assistant.NewEntryPoint.Eligibility" - enum="AssistantNewEntryPointEligibility" expires_after="2026-04-12"> + enum="AssistantNewEntryPointEligibility" expires_after="2027-04-12"> <owner>yawano@google.com</owner> <owner>assistive-eng@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml index 364111b..a4bb840 100644 --- a/tools/metrics/histograms/metadata/autofill/histograms.xml +++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -961,6 +961,20 @@ </summary> </histogram> +<histogram name="Autofill.Actor.AutofillAttentionDialogsPerTask" + units="dialogs" expires_after="2026-11-30"> + <owner>battre@chromium.org</owner> + <owner>jkeitel@google.com</owner> + <owner>slobodan@chromium.org</owner> + <owner>chrome-autofill-alerts@google.com</owner> + <summary> + Records the number of autofill attention dialogs presented in the UI to the + user for a given task. Recorded in `ActionTrackerForMetrics`'s destructor. + The metric is incremented by one for every UI dialog (even if it contains + more than 1 suggestion dropdowns). + </summary> +</histogram> + <histogram name="Autofill.Actor.AutofillSuggestionAccepted.RecordType" enum="ActorFormFillingRequestedData" expires_after="2026-11-30"> <owner>battre@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml index a95cf6e..657f776 100644 --- a/tools/metrics/histograms/metadata/blink/histograms.xml +++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -4365,6 +4365,19 @@ </summary> </histogram> +<histogram name="Blink.ModelContext.DelayedToolCount" units="count" + expires_after="2027-03-10"> + <owner>mfoltz@chromium.org</owner> + <owner>agentic-web-platform-team@google.com</owner> + <summary> + Records the total number of tools registered with navigator.modelContext + after some delay after the first registration. This is recorded once per top + level document. It provides an approximate number of tool registrations, + assuming that declarative tool registrations happen on the main document + load, followed by some imperative tool registrations in script. + </summary> +</histogram> + <histogram name="Blink.Network.DataUrlLength{Type}" units="characters" expires_after="2026-06-25"> <owner>nidhijaju@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/glic/enums.xml b/tools/metrics/histograms/metadata/glic/enums.xml index 6bd6fe8..3b28d77 100644 --- a/tools/metrics/histograms/metadata/glic/enums.xml +++ b/tools/metrics/histograms/metadata/glic/enums.xml
@@ -559,6 +559,7 @@ <int value="19" label="Automatically opened for a PDF"/> <int value="20" label="From capture region hotkey"/> <int value="21" label="In-product help"/> + <int value="22" label="User clicked an anchored contextual cue chip"/> </enum> <!-- LINT.ThenChange(//chrome/browser/glic/host/glic.mojom:InvocationSource) --> @@ -753,6 +754,10 @@ <int value="86" label="IPH, attached, audio mode."/> <int value="87" label="IPH, detached, text mode."/> <int value="88" label="IPH, detached, audio mode."/> + <int value="89" label="Anchored contextual cue, attached, text mode."/> + <int value="90" label="Anchored contextual cue, attached, audio mode."/> + <int value="91" label="Anchored contextual cue, detached, text mode."/> + <int value="92" label="Anchored contextual cue, detached, audio mode."/> </enum> <!-- LINT.ThenChange(//chrome/browser/glic/glic_metrics.h:ResponseSegmentation) -->
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml index e99cd86..8de0ebf 100644 --- a/tools/metrics/histograms/metadata/optimization/histograms.xml +++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -1796,6 +1796,28 @@ </histogram> <histogram + name="OptimizationGuide.PageContentAnnotations.CategoryClassifier.EducationScore" + units="%" expires_after="2026-09-10"> + <owner>zekunjiang@google.com</owner> + <owner>chrome-intelligence-core@google.com</owner> + <summary> + Records the score (0-100) produced by the On Device Category Classifier for + the Education category. Recorded once for every page load. + </summary> +</histogram> + +<histogram + name="OptimizationGuide.PageContentAnnotations.CategoryClassifier.ShoppingScore" + units="%" expires_after="2026-09-10"> + <owner>zekunjiang@google.com</owner> + <owner>chrome-intelligence-core@google.com</owner> + <summary> + Records the score (0-100) produced by the On Device Category Classifier for + the Shopping category. Recorded once for every page load. + </summary> +</histogram> + +<histogram name="OptimizationGuide.PageContentAnnotations.GoogleSearchMetadataExtracted" enum="Boolean" expires_after="2026-03-29"> <owner>sophiechang@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/page/enums.xml b/tools/metrics/histograms/metadata/page/enums.xml index dd3aeeb51..3ef7ef3 100644 --- a/tools/metrics/histograms/metadata/page/enums.xml +++ b/tools/metrics/histograms/metadata/page/enums.xml
@@ -302,6 +302,7 @@ <int value="39" label="RecordReplay"/> <int value="40" label="Indigo"/> <int value="41" label="Federation"/> + <int value="42" label="Glic"/> </enum> <!-- LINT.ThenChange(//chrome/browser/ui/page_action/page_action_icon_type.h:PageActionIconType) -->
diff --git a/tools/metrics/histograms/metadata/stability/enums.xml b/tools/metrics/histograms/metadata/stability/enums.xml index de97da0..3ea66989 100644 --- a/tools/metrics/histograms/metadata/stability/enums.xml +++ b/tools/metrics/histograms/metadata/stability/enums.xml
@@ -501,6 +501,7 @@ <int value="333" label="RFH_NEW_ISOLATED_WEB_APP_PERMISSION_POLICIES"/> <int value="334" label="RFH_CREATE_NEW_WINDOW_INVALID_DISPOSITION"/> <int value="335" label="RFH_CREATE_NEW_WINDOW_FROM_SANDBOXED_FRAME"/> + <int value="336" label="RFH_MODAL_DIALOG_FROM_SANDBOXED_FRAME"/> </enum> <enum name="BadMessageReasonExtensions">
diff --git a/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java b/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java index 3004cbb..71ba924 100644 --- a/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java +++ b/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java
@@ -10,6 +10,11 @@ @NullMarked @FunctionalInterface public interface ClickWithMetaStateCallback { - /** Called when a click occurs. {@param metaState} is the meta key state at the time. */ - void onClickWithMeta(int metaState); + /** + * Called when a click occurs. + * + * @param metaState The meta key state at the time. + * @param buttonState The mouse button state. + */ + void onClickWithMeta(int metaState, int buttonState); }
diff --git a/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java b/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java index a206d74..a87bce1 100644 --- a/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java +++ b/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java
@@ -8,6 +8,7 @@ import android.content.Context; import android.graphics.PorterDuff.Mode; import android.util.AttributeSet; +import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; @@ -28,6 +29,11 @@ // Used to keep track of meta state so that things like shift+click and ctrl+click can work. private int mLastEventMetaState; + // Used to keep track of button state so that things like middle click can work. + private int mLastEventButtonState; + + private @Nullable ClickWithMetaStateCallback mClickCallback; + public ChromeImageButton(Context context) { super(context); } @@ -62,32 +68,68 @@ // 5) The OnClickListener is called, which calls the callback set in setClickCallback and // provides the meta state saved in (3). mLastEventMetaState = event.getMetaState(); + mLastEventButtonState = 0; return super.onKeyUp(keyCode, event); } @Override @SuppressLint("ClickableViewAccessibility") public boolean onTouchEvent(MotionEvent event) { - // NOTE: Update `mLastEventMetaState` in anticipation of a potential click. - // This handles button activations with touch/mouse. The way it works is as follows: + // NOTE: Update `mLastEventMetaState` and `mLastEventButtonState` in anticipation of a + // potential click. This handles button activations with touch/mouse. The way it works is as + // follows: // 1) User clicks/touches button. // 2) This method is triggered. - // 3) The current meta state is saved. + // 3) The current meta state and button state are saved. // 4) The button is clicked because user clicked button in (1). // 5) The OnClickListener is called, which calls the callback set in setClickCallback and - // provides the meta state saved in (3). + // provides the meta state and button state saved in (3). mLastEventMetaState = event.getMetaState(); + + int action = event.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_BUTTON_PRESS) { + mLastEventButtonState = event.getButtonState(); + } + + // Consume middle-clicks to avoid triggering the OnClickListener. Without this, a + // middle-click (e.g. from a mouse) would trigger both the middle-click specific action + // (handled in onGenericMotionEvent) and the standard click action (triggered by + // super.onTouchEvent), leading to duplicate or conflicting behaviors. + if ((mLastEventButtonState & MotionEvent.BUTTON_TERTIARY) != 0) { + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + mLastEventButtonState = 0; + } + return true; + } + return super.onTouchEvent(event); } + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0 + && event.getActionMasked() == MotionEvent.ACTION_BUTTON_PRESS + && event.getActionButton() == MotionEvent.BUTTON_TERTIARY) { + if (mClickCallback != null) { + mClickCallback.onClickWithMeta(event.getMetaState(), MotionEvent.BUTTON_TERTIARY); + } + return true; + } + return super.onGenericMotionEvent(event); + } + /** * Sets the callback to notify of button click events. The callback is provided the meta state - * of the most recent key/touch event. + * and button state of the most recent key/touch event. * * @param callback the callback to notify. */ public void setClickCallback(@Nullable ClickWithMetaStateCallback callback) { + mClickCallback = callback; setOnClickListener( - callback != null ? (v) -> callback.onClickWithMeta(mLastEventMetaState) : null); + callback != null + ? (v) -> + callback.onClickWithMeta(mLastEventMetaState, mLastEventButtonState) + : null); } }
diff --git a/ui/android/window_android.h b/ui/android/window_android.h index f532fba0..114a82e 100644 --- a/ui/android/window_android.h +++ b/ui/android/window_android.h
@@ -235,4 +235,19 @@ } // namespace ui +namespace jni_zero { +template <> +inline ui::WindowAndroid* FromJniType<ui::WindowAndroid*>( + JNIEnv* env, + const JavaRef<jobject>& j_obj) { + return ui::WindowAndroid::FromJavaWindowAndroid(j_obj); +} + +template <> +inline ScopedJavaLocalRef<jobject> ToJniType(JNIEnv* env, + ui::WindowAndroid* obj) { + return obj->GetJavaObject(); +} +} // namespace jni_zero + #endif // UI_ANDROID_WINDOW_ANDROID_H_
diff --git a/ui/views/test/mock_activation_controller.h b/ui/views/test/mock_activation_controller.h index 01364eb4..de23bf3 100644 --- a/ui/views/test/mock_activation_controller.h +++ b/ui/views/test/mock_activation_controller.h
@@ -10,9 +10,14 @@ #include "base/functional/callback.h" #include "base/memory/weak_ptr.h" +#include "ui/views/buildflags.h" #include "ui/views/widget/widget_activation_delegate.h" #include "ui/views/widget/widget_observer.h" +// Only desktop aura platforms or macOS needs the activation emulation. +#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) +#define USE_MOCK_ACTIVATION_CONTROLLER 1 + namespace views { class Widget; namespace test { @@ -63,4 +68,6 @@ } // namespace test } // namespace views +#endif // BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC) + #endif // UI_VIEWS_TEST_MOCK_ACTIVATION_CONTROLLER_H_
diff --git a/ui/views/window/native_frame_view.cc b/ui/views/window/native_frame_view.cc index 8c3d80c..4f5033f 100644 --- a/ui/views/window/native_frame_view.cc +++ b/ui/views/window/native_frame_view.cc
@@ -46,6 +46,12 @@ } int NativeFrameView::NonClientHitTest(const gfx::Point& point) { + if (!non_client_hit_test_callback_.is_null()) { + if (auto result = non_client_hit_test_callback_.Run(point); result) { + return result.value(); + } + } + return widget_->client_view()->NonClientHitTest(point); }
diff --git a/ui/webui/resources/cr_components/composebox/composebox.html.ts b/ui/webui/resources/cr_components/composebox/composebox.html.ts index 0cd2200..0896249 100644 --- a/ui/webui/resources/cr_components/composebox/composebox.html.ts +++ b/ui/webui/resources/cr_components/composebox/composebox.html.ts
@@ -176,14 +176,16 @@ button enabled/disabled state. --> ${!this.searchboxNextEnabled ? getSubmitButtonHtml.bind(this)() : ''} </div> - <cr-composebox-voice-search id="voiceSearch" - @voice-search-cancel="${this.onVoiceSearchCancel_}" - @voice-search-final-result="${this.onVoiceSearchFinalResult_}" - @voice-search-error="${this.onVoiceSearchError_}" - @transcript-update="${this.onTranscriptUpdate_}" - @speech-received="${this.onSpeechReceived_}" - exportparts="voice-close-button"> - </cr-composebox-voice-search> + ${this.shouldShowVoiceSearch_() ? html` + <cr-composebox-voice-search id="voiceSearch" + @voice-search-cancel="${this.onVoiceSearchCancel_}" + @voice-search-final-result="${this.onVoiceSearchFinalResult_}" + @voice-search-error="${this.onVoiceSearchError_}" + @transcript-update="${this.onTranscriptUpdate_}" + @speech-received="${this.onSpeechReceived_}" + exportparts="voice-close-button"> + </cr-composebox-voice-search> + ` : ''} ${this.shouldShowSuggestionActivityLink_() && this.suggestionActivityEnabled ? html` <div id="suggestionActivity">
diff --git a/ui/webui/resources/cr_components/composebox/composebox.ts b/ui/webui/resources/cr_components/composebox/composebox.ts index f6f62697..5878badd 100644 --- a/ui/webui/resources/cr_components/composebox/composebox.ts +++ b/ui/webui/resources/cr_components/composebox/composebox.ts
@@ -50,6 +50,7 @@ import type {ContextualEntrypointAndMenuElement} from './contextual_entrypoint_and_menu.js'; import type {ErrorScrimElement} from './error_scrim.js'; import type {ComposeboxFileCarouselElement} from './file_carousel.js'; +import {WindowProxy} from './window_proxy.js'; export enum VoiceSearchAction { ACTIVATE = 0, @@ -76,7 +77,6 @@ fileInputs: ComposeboxFileInputsElement, matches: ComposeboxDropdownElement, errorScrim: ErrorScrimElement, - voiceSearch: ComposeboxVoiceSearchElement, caret: HTMLInputElement, mirror: HTMLDivElement, }; @@ -907,8 +907,11 @@ protected shouldShowVoiceSearch_(): boolean { const isExpanded = this.showDropdown_ || this.files_.size > 0; - return isExpanded ? this.showVoiceSearchInExpandedComposebox_ : - this.showVoiceSearchInSteadyComposebox_; + const isFeatureEnabled = isExpanded ? + this.showVoiceSearchInExpandedComposebox_ : + this.showVoiceSearchInSteadyComposebox_; + return isFeatureEnabled && + WindowProxy.getInstance().hasWebkitSpeechRecognition(); } protected shouldShowVoiceSearchAnimation_(): boolean { @@ -1288,7 +1291,9 @@ this.fire('voice-search-action', {value: VoiceSearchAction.ACTIVATE}); // For contextual tasks composebox voice metrics. this.fire('composebox-voice-search-start'); - this.$.voiceSearch.start(); + this.shadowRoot + .querySelector<ComposeboxVoiceSearchElement>( + 'cr-composebox-voice-search')!.start(); } protected onVoiceSearchCancel_(e: CustomEvent<boolean>) {
diff --git a/ui/webui/resources/cr_components/composebox/composebox_tool_chips.html.ts b/ui/webui/resources/cr_components/composebox/composebox_tool_chips.html.ts index 8e01b32..01e0674 100644 --- a/ui/webui/resources/cr_components/composebox/composebox_tool_chips.html.ts +++ b/ui/webui/resources/cr_components/composebox/composebox_tool_chips.html.ts
@@ -9,8 +9,9 @@ export function getHtml(this: ComposeboxElement) { // clang-format off + // TODO(crbug.com/491669968): Clean up redundant `activeToolMode_`. return html` -${this.activeToolMode_ === ComposeboxToolMode.kDeepSearch ? html` +${this.inputState_?.activeTool === ComposeboxToolMode.kDeepSearch ? html` <cr-composebox-tool-chip id="deepSearchChip" exportparts="tool-chip-label" @@ -24,7 +25,7 @@ @click="${this.onDeepSearchClick_}"> </cr-composebox-tool-chip> ` : ''} -${this.activeToolMode_ === ComposeboxToolMode.kImageGen ? html` +${this.inputState_?.activeTool === ComposeboxToolMode.kImageGen ? html` <cr-composebox-tool-chip id="nanoBananaChip" exportparts="tool-chip-label" @@ -38,7 +39,7 @@ @click="${this.onCreateImageClick_}"> </cr-composebox-tool-chip> ` : ''} -${this.activeToolMode_ === ComposeboxToolMode.kCanvas ? html` +${this.inputState_?.activeTool === ComposeboxToolMode.kCanvas ? html` <cr-composebox-tool-chip id="canvasChip" exportparts="tool-chip-label"
diff --git a/ui/webui/resources/cr_components/composebox/contextual_action_menu.css b/ui/webui/resources/cr_components/composebox/contextual_action_menu.css index c9a55c4..6f4616b8 100644 --- a/ui/webui/resources/cr_components/composebox/contextual_action_menu.css +++ b/ui/webui/resources/cr_components/composebox/contextual_action_menu.css
@@ -82,11 +82,13 @@ } .dropdown-item:hover { - background-color: var(--cr-hover-background-color); + background-color: var(--color-context-menu-hover-background, + var(--cr-hover-background-color)); } .dropdown-item:focus { - background-color: var(--cr-hover-background-color); + background-color: var(--color-context-menu-hover-background, + var(--cr-hover-background-color)); outline: none; } @@ -131,11 +133,14 @@ } #multi-tab-add { - color: var(--color-composebox-file-chip-text); + color: var(--color-composebox-context-menu-text, + var(--color-composebox-file-chip-text)); } #multi-tab-check { - color: var(--color-composebox-tab-selector-button-selected); + color: var(--color-composebox-context-menu-checkbox-override, + /* Default defined in colors.css: */ + var(--color-composebox-tab-selector-button-selected)); } #model-check {
diff --git a/ui/webui/resources/cr_components/composebox/window_proxy.ts b/ui/webui/resources/cr_components/composebox/window_proxy.ts index 79565a5..341c5728 100644 --- a/ui/webui/resources/cr_components/composebox/window_proxy.ts +++ b/ui/webui/resources/cr_components/composebox/window_proxy.ts
@@ -31,4 +31,8 @@ matchMedia(query: string): MediaQueryList { return window.matchMedia(query); } + + hasWebkitSpeechRecognition(): boolean { + return 'webkitSpeechRecognition' in window; + } }
diff --git a/ui/webui/resources/tools/eslint/lit_element_structure.js b/ui/webui/resources/tools/eslint/lit_element_structure.js index dadfd84..783cf16 100644 --- a/ui/webui/resources/tools/eslint/lit_element_structure.js +++ b/ui/webui/resources/tools/eslint/lit_element_structure.js
@@ -3,6 +3,7 @@ // found in the LICENSE file. import assert from 'node:assert'; +import path from 'node:path'; import {ESLintUtils} from '../../../../../third_party/node/node_modules/@typescript-eslint/utils/dist/index.js'; @@ -67,16 +68,6 @@ return; } - if (!node.id.name.endsWith('Element')) { - this.context.report({ - node: node, - messageId: 'incorrectClassName', - data: { - className: node.id.name, - }, - }); - } - this.node = node; } @@ -275,6 +266,65 @@ }); } + runConsistentClassDomNameCheck() { + if (!this.isLitElement) { + return; + } + + if (!this.node.id.name.endsWith('Element')) { + this.context.report({ + node: this.node, + messageId: 'incorrectClassNameSuffix', + data: { + className: this.node.id.name, + }, + }); + return; + } + + if (this.domName === '') { + // Handle case where the element is used as a super class for other + // elements and has no DOM name. + return; + } + + if (this.domName.endsWith('-element')) { + this.context.report({ + node: this.node, + messageId: 'incorrectDomNameSuffix', + data: { + domName: this.domName, + }, + }); + return; + } + + const domNameParts = this.domName.split('-'); + const isClassNameConsistent = domNameParts.some((_, i) => { + const candidateSuffix = + dashCaseToCamelCase('-' + domNameParts.slice(i).join('-')) + + 'Element'; + + // Allow a class name prefix that does not exist in the DOM name, only if + // all the DOM name parts are reflected in the class name. + return i === 0 ? this.node.id.name.endsWith(candidateSuffix) : + candidateSuffix === this.node.id.name; + }); + + if (isClassNameConsistent) { + return; + } + + this.context.report({ + node: this.node, + messageId: 'inconsistentClassName', + data: { + className: this.node.id.name, + domName: this.domName, + }, + }); + } + runMethodDefinitionOrderCheck() { if (!this.isLitElement || !this.node) { return; @@ -319,8 +369,12 @@ 'Use this.fire(...) instead of this.dispatchEvent(new CustomEvent(...))..', useFireHelperWithEventName: 'Use this.fire(...) instead of this.dispatchEvent(new CustomEvent(...)), for event \'{{eventName}}\'.', - incorrectClassName: - 'CrLitElement subclass {{className}} should end with the \'Element\' suffix.', + incorrectClassNameSuffix: + 'Class name \'{{className}}\' should end with the \'Element\' suffix.', + incorrectDomNameSuffix: + 'DOM name \'{{domName}}\' should not end with the \'-element\' suffix.', + inconsistentClassName: + 'Naming of class/dom pair {{className}} ↔ {{domName}} is inconsistent.', incorrectDollarSignNotation: 'Use camelCase instead of dash-case for DOM ids, change this.$[\'{{dashCaseName}}\'] to this.$.{{camelCaseName}}.', incorrectMethodDefinitionOrder: @@ -450,6 +504,7 @@ currentClassInfo.runMissingStaticIsGetterCheck(); currentClassInfo.runMissingSuperCallsCheck(); currentClassInfo.runMethodDefinitionOrderCheck(); + currentClassInfo.runConsistentClassDomNameCheck(); }, ['Program > TSModuleDeclaration[kind=global] > TSModuleBlock > TSInterfaceDeclaration[id.name="HTMLElementTagNameMap"] > TSInterfaceBody > TSPropertySignature']( node) {
diff --git a/ui/webui/resources/tools/eslint_ts_test.py b/ui/webui/resources/tools/eslint_ts_test.py index b0856825..1c7db477 100755 --- a/ui/webui/resources/tools/eslint_ts_test.py +++ b/ui/webui/resources/tools/eslint_ts_test.py
@@ -241,7 +241,9 @@ self.assertTrue(_EXPECTED_STRING in str(context.exception)) _EXPECTED_INCONSISTENT_METHOD_DEFINITION_ORDER_ERROR = "Inconsistent method definition order in class %(className)s. Expected %(expectedOrder)s, found %(actualOrder)s" - _EXPECTED_INCORRECT_CLASS_NAME_ERROR = 'CrLitElement subclass %(className)s should end with the \'Element\' suffix' + _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR = 'Class name \'%(className)s\' should end with the \'Element\' suffix' + _EXPECTED_INCONSISTENT_CLASS_NAME_ERROR = 'Naming of class/dom pair %(className)s ↔ %(domName)s is inconsistent' + _EXPECTED_INCORRECT_DOM_NAME_SUFFIX_ERROR = 'DOM name \'%(domName)s\' should not end with the \'-element\' suffix' _EXPECTED_INCORRECT_DOLLAR_SIGN_NOTATION_ERROR = 'Use camelCase instead of dash-case for DOM ids, change this.$[\'%(dashCaseName)s\'] to this.$.%(camelCaseName)s' _EXPECTED_MISSING_CUSTOM_ELEMENTS_DEFINE_ERROR = "Missing customElements.define(%(className)s.is, %(className)s) call" _EXPECTED_MISSING_STATIC_GET_IS_ERROR = "Missing 'static get is() {...}' for web component class %(className)s" @@ -287,6 +289,9 @@ 'lifecycleMethods': ', '.join(super_call_required_methods), }, # Case 1.6 + _EXPECTED_INCORRECT_DOM_NAME_SUFFIX_ERROR % { + 'domName': 'test-error6-element', + }, _EXPECTED_INCONSISTENT_METHOD_DEFINITION_ORDER_ERROR % { 'className': 'TestError6Element', @@ -315,21 +320,26 @@ 'name': '_otherEvent', }, # Case 1.7 - _EXPECTED_INCORRECT_CLASS_NAME_ERROR % { + _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % { 'className': 'TestError7ElementFoo', }, # Case 1.8 - _EXPECTED_INCORRECT_CLASS_NAME_ERROR % { + _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % { 'className': 'TestError8ElementFoo', }, # Case 1.9 - _EXPECTED_INCORRECT_CLASS_NAME_ERROR % { + _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % { 'className': 'TestError9ElementFoo', }, # Case 1.10 - _EXPECTED_INCORRECT_CLASS_NAME_ERROR % { + _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % { 'className': 'TestError10ElementFoo', }, + # Case 1.11 + _EXPECTED_INCONSISTENT_CLASS_NAME_ERROR % { + 'className': 'TestError11Element', + 'domName': 'test-other-error11', + }, ] for e in errors: self.assertTrue( @@ -352,7 +362,7 @@ 'className': 'TestNoError1Element', 'lifecycleMethods': ', '.join(super_call_required_methods) }, - _EXPECTED_INCORRECT_CLASS_NAME_ERROR % { + _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % { 'className': 'TestNoError1Element', }, # Case 2.2 @@ -370,10 +380,13 @@ 'className': 'TestNoError2Element', 'lifecycleMethods': ', '.join(super_call_required_methods), }, - _EXPECTED_INCORRECT_CLASS_NAME_ERROR % { + _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % { 'className': 'TestNoError2Element', }, # Case 2.3 + _EXPECTED_INCORRECT_DOM_NAME_SUFFIX_ERROR % { + 'domName': 'test-no-error3', + }, _EXPECTED_MISSING_STATIC_GET_IS_ERROR % { 'className': 'TestNoError3Element', }, @@ -396,9 +409,13 @@ 'actualOrder': '', }, - _EXPECTED_INCORRECT_CLASS_NAME_ERROR % { + _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % { 'className': 'TestNoError3Element', }, + _EXPECTED_INCONSISTENT_CLASS_NAME_ERROR % { + 'className': 'TestNoError3Element', + 'domName': 'test-no-error3', + }, _EXPECTED_USE_FIRE_HELPER_WITH_EVENT_NAME_ERROR % { 'eventName': 'bar-updated', }, @@ -415,7 +432,7 @@ 'name': '_otherEvent2', }, # Case 2.4 - _EXPECTED_INCORRECT_CLASS_NAME_ERROR % { + _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % { 'className': 'TestNoError4Element', }, ]
diff --git a/ui/webui/resources/tools/tests/eslint_ts/with_webui_plugin_lit_element_structure_violations.ts b/ui/webui/resources/tools/tests/eslint_ts/with_webui_plugin_lit_element_structure_violations.ts index 5f1451bb..3b6e49d 100644 --- a/ui/webui/resources/tools/tests/eslint_ts/with_webui_plugin_lit_element_structure_violations.ts +++ b/ui/webui/resources/tools/tests/eslint_ts/with_webui_plugin_lit_element_structure_violations.ts
@@ -69,10 +69,11 @@ customElements.define(TestError5Element.is, TestError5Element); // Case1.6: Class with -// 1) Incorrect order method definition order -// 2) Usage of this.dispatchEvent(new CustomEvent(...)) -// 3) Usage of incorrect dollar sign notation. -// 4) Usage of CustomEvent type without a type parameter. +// 1) Inconsistent DOM name suffix. +// 2) Incorrect order method definition order +// 3) Usage of this.dispatchEvent(new CustomEvent(...)) +// 4) Usage of incorrect dollar sign notation. +// 5) Usage of CustomEvent type without a type parameter. export class TestError6Element extends CrLitElement { override render() { return ''; @@ -83,7 +84,7 @@ } static get is() { - return 'test-error6'; + return 'test-error6-element'; } static override get properties() { @@ -132,7 +133,7 @@ declare global { interface HTMLElementTagNameMap { - 'test-error6': TestError6Element; + 'test-error6-element': TestError6Element; } } @@ -204,6 +205,21 @@ customElements.define(TestError10ElementFoo.is, TestError10ElementFoo); +// Case1.11: Class with inconsistent class name. +export class TestError11Element extends CrLitElement { + static get is() { + return 'test-other-error11'; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'test-other-error11': TestError11Element; + } +} + +customElements.define(TestError11Element.is, TestError11Element); + /* Cases with no violations below. */
diff --git a/url/origin.h b/url/origin.h index fa37b81..97fa03d 100644 --- a/url/origin.h +++ b/url/origin.h
@@ -512,7 +512,8 @@ } // namespace url -#if BUILDFLAG(IS_ANDROID) && !BUILDFLAG(CRONET_BUILD) +#if (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC)) && \ + !BUILDFLAG(CRONET_BUILD) namespace jni_zero { // @JniType conversion function.
diff --git a/v8 b/v8 index 3ec501c..705aa9d3 160000 --- a/v8 +++ b/v8
@@ -1 +1 @@ -Subproject commit 3ec501ccaac5680794d8f70459fde1e7c120abe6 +Subproject commit 705aa9d3796d3e20193c302e1a32fa05ab4363dc